I am looking for a dotnet core WCF wsHttpBinding workaround.
I am aware that .net core WCF implementation currently does not support wsHttpBinding (see support matrix here https://github.com/dotnet/wcf/blob/master/release-notes/SupportedFeatures-v2.1.0.md)
I'm integrating with a legacy third party service that appears to only support wsHttpBinding. Our tech stack is .net core, so I cannot revert to the full version of .net framework or mono variant.
The question is whether it's possible to use the service via custom bindings? I am hoping that there is a workaround that maybe isn't fully functional, but at least allows me to consume the service.
var cBinding = new CustomBinding();
var textBindingElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.Soap12WSAddressing10
};
cBinding.Elements.Add(textBindingElement);
var httpBindingElement =
new HttpsTransportBindingElement
{
AllowCookies = true, MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue,
};
cBinding.Elements.Add(httpBindingElement);
var myEndpoint = new EndpointAddress("https://..../Service.svc/wss");
using (var myChannelFactory = new ChannelFactory<ISearch>(cBinding, myEndpoint))
{
myChannelFactory.Credentials.UserName.UserName = "...";
myChannelFactory.Credentials.UserName.Password = "...";
ISearch client = null;
try
{
client = myChannelFactory.CreateChannel();
var result = client.Find(new Search("Criteria")).Result;
((ICommunicationObject)client).Close();
myChannelFactory.Close();
}
catch (Exception ex)
{
(client as ICommunicationObject)?.Abort();
}
}
Client gets created and a call is made to the service, but it fails because:
Message = "The message could not be processed. This is most likely because the action '' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between binding
Forget it, they are not completely compatible with Core. Under some specified case, there may be a basic call to WsHttpBinding. You could refer to the following example.
Server.
Uri uri = new Uri("http://localhost:11011");
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.None;
using (ServiceHost sh=new ServiceHost(typeof(MyService),uri))
{
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb==null)
{
smb = new ServiceMetadataBehavior()
{
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Opened += delegate
{
Console.WriteLine("Service is ready");
};
sh.Closed += delegate
{
Console.WriteLine("Service is clsoed");
};
sh.Open();
Client(auto-generated)
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IService))
{
System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
result.Elements.Add(textBindingElement);
System.ServiceModel.Channels.HttpTransportBindingElement httpBindingElement = new System.ServiceModel.Channels.HttpTransportBindingElement();
httpBindingElement.AllowCookies = true;
httpBindingElement.MaxBufferSize = int.MaxValue;
httpBindingElement.MaxReceivedMessageSize = int.MaxValue;
result.Elements.Add(httpBindingElement);
return result;
}
Invocation.
ServiceReference1.ServiceClient client2 = new ServiceReference1.ServiceClient();
try
{
var res = client2.SayHelloAsync();
Console.WriteLine(res.Result);
}
catch (Exception)
{
throw;
}
While in most cases it is impossible to call WCF service created by WsHttpBinding.
Officials also have no intention of continuing to support plan for wshttpbinding.
Here are related discussions.
https://github.com/dotnet/wcf/issues/31
https://github.com/dotnet/wcf/issues/1370
Related
I am trying to create a service to consume it through the internet, but for some reason I cannot access it and I need help finding the error I am making.
I leave the code for you to see.
public class ServiceHost<T> : System.ServiceModel.ServiceHost
{
public ServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
public ServiceHost(object singletonInstance, params Uri[] baseAddresses)
:base(singletonInstance, baseAddresses)
{
}
protected ServiceHost()
: base()
{
}
public void EnableMetadataExchange(bool enableHttpGet = true)
{
if (State == CommunicationState.Opened)
{
throw new InvalidOperationException("La comunicación ya está abierta");
}
ServiceMetadataBehavior metadataBehavior
= Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
Description.Behaviors.Add(metadataBehavior);
if (BaseAddresses.Any(uri => uri.Scheme == "http"))
metadataBehavior.HttpGetEnabled = enableHttpGet;
}
AddAllMexEndPoints();
}
public bool HasMexEndpoint
{
get
{
return Description.Endpoints.Any(
endpoint => endpoint.Contract.ContractType ==
typeof(IMetadataExchange));
}
}
private void AddAllMexEndPoints()
{
Debug.Assert(HasMexEndpoint == false);
foreach (Uri baseAddress in BaseAddresses)
{
Binding binding = null;
switch (baseAddress.Scheme)
{
case "net.tcp":
{
binding = MetadataExchangeBindings.CreateMexTcpBinding();
break;
}
case "http":
{
binding = MetadataExchangeBindings.CreateMexHttpBinding();
break;
}
case "https":
{
binding = MetadataExchangeBindings.CreateMexHttpsBinding();
break;
}
case "net.pipe":
{
binding = MetadataExchangeBindings.CreateMexNamedPipeBinding();
break;
}
}
if (binding != null)
{
AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
}
}
}
}
Hosting
public void HostService()
{
try
{
Uri tcpBaseAddress = new Uri("net.tcp://192.168.1.110:28620/");
Uri httpBaseAddress = new Uri("http://192.168.1.110:28621/");
ServiceHost<wesling.Services.GC> host = new ServiceHost<wesling.Services.GC>(typeof(wesling.Services.GC), tcpBaseAddress, httpBaseAddress);
//add tcp binding
var netTcpBinding = new NetTcpBinding()
{
MaxBufferPoolSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue
},
};
netTcpBinding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(wesling.Services.IGC), netTcpBinding, "GC");
//add WSHttp binding
var httpBinding = new WSHttpBinding()
{
MaxBufferPoolSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue
},
};
httpBinding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(wesling.Services.IGC), httpBinding, "GC");
host.EnableMetadataExchange(true);
host.Open();
}
catch (CommunicationException ce)
{
MessageBox.Show(ce.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The uri ip is the lan ip of the pc where the service is hosted
The customer is like this
public async void GetProduct()
{
try
{
var endPointConfiguration = "WSHttpBinding_IGC";//cfg.GetEndPointConfiguration();
var address = "http://fabianwesling.dynu.com:28621/GC"; //cfg.getAddress();
ServiceReference1.GCClient service = new ServiceReference1.GCClient(endPointConfiguration, address);
var bindins = service.Endpoint.Binding;
if (bindins is NetTcpBinding tcpBinding)
{
tcpBinding.MaxBufferPoolSize = int.MaxValue;
tcpBinding.MaxReceivedMessageSize = int.MaxValue;
tcpBinding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
}
else if (bindins is WSHttpBinding wS)
{
wS.MaxBufferPoolSize = int.MaxValue;
wS.MaxReceivedMessageSize = int.MaxValue;
wS.ReaderQuotas.MaxStringContentLength = int.MaxValue;
}
var result = await service.GetProductsAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I opened the ports in the Reuter so that it directs those ports to the pc, and I also opened the ports in the firewall, I also activated the Windows .net framework features. But when I try to connect from the client, it tells me that there was no end listening
There must be some concept that I am not understanding, but I cannot identify what it is ... I need your advice, everything is welcome
First try to use the browser on the client computer to visit http//fabianwesling.dynu.com:28621/GC to see if the metadata can be accessed normally.If not, the client and server cannot access normally. Modify the hots file on the client computer:
All in all, you must ensure that the client and server are mutually accessible on the Internet.
If it is not a network problem, you can create a simple WCF service for testing to know where the problem is.Here is a very simple example of WCF service:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/getting-started-tutorial
UPDATE
This is my remote WCF service:
Baseadress is the WAN IP.
Ensure that the local computer can access the WSDL of the remote service through a browser
We use ServiceHost for Years now for local communication between our applications.
We open a net.pipe: net.pipe://localhost/OurCompanyName/
This worked well until the windows 10 patch KB4480966 arrived.
This is the code that opens the service host:
NetNamedPipeBinding binding = new NetNamedPipeBinding();
binding.MaxConnections = 100;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Mode = NetNamedPipeSecurityMode.Transport;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
//binding.ReceiveTimeout = new TimeSpan(0, 0, 10, 0);
this.serviceHost = new ServiceHost(typeof(LocalService), new Uri[] { new Uri(hostBaseAddress) });
this.serviceHost.AddServiceEndpoint(typeof(ILocalService), binding, localServiceEndPointAddress);
foreach (ServiceEndpoint ep in this.serviceHost.Description.Endpoints)
{
foreach (OperationDescription op in ep.Contract.Operations)
{
DataContractSerializerOperationBehavior dataContractBehavior =
op.Behaviors.Find<DataContractSerializerOperationBehavior>()
as DataContractSerializerOperationBehavior;
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
}
}
}
this.serviceHost.Open();
Since the mentioned Patch we get this Exception:
Cannot listen on pipe name 'net.pipe://localhost/OurCompanyName/' because another pipe endpoint is already listening on that name.
I have a WCF rest service define like this:
static void Main(string[] args)
{
try
{
DOMConfigurator.Configure();
string serviceUriStr = "http://127.0.0.1:9001/AFMServer";
Uri serviceUri = new Uri(serviceUriStr);
_webHost = new WebServiceHost(typeof(AftermathFacade), serviceUri);
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.None);
binding.MaxBufferPoolSize = 2000000000;
binding.MaxReceivedMessageSize = 2000000000;
//binding.ReceiveTimeout = TimeSpan.MaxValue;
//binding.CloseTimeout = TimeSpan.MaxValue;
//binding.SendTimeout = TimeSpan.MaxValue;
////binding.OpenTimeout = TimeSpan.MaxValue;
_webHost.AddServiceEndpoint(typeof(IAftermathFacade), binding, serviceUriStr);
ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior();
stb.MaxConcurrentCalls = 1000;
stb.MaxConcurrentInstances = 1000;
stb.MaxConcurrentSessions = 1000;
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
_webHost.Description.Behaviors.Add(smb);
_webHost.Description.Behaviors.Add(stb);
_webHost.Open();
_log.Info("Listening on Uri: " + serviceUri.AbsoluteUri);
Console.ReadLine();
}
catch (Exception ex)
{
_log.Error(ex.StackTrace);
}
}
Accessing this service from a client randomly throws a
"The requested address is not valid in this context"
exception. Sometimes it does, sometimes it does not and the remote call works.
What I noticed is that it seems to happen when to calls to the service are made too quickly.
for instance:
CallServiceMethod1();
CallSericeMethod2();
throws an exception.
However:
CallServiceMethod1();
sleep(1000);
CallSericeMethod2();
Works!
Any idea where the problem might be coming from?
Note that the service is hosted on a AWS micro instance (very poor performance). When I test locally on my box, the exception is not happening as often.
My particular problem is something like this:
I am able to invoke WCF service dynamically, but i need to provide certificate.
We don't want to use app.config.
Major issue is that WCF service is a 3rd party url.
Actually, i am getting an exception while invoke instance that certificate is not provided.
My code is something like this:
try
{
// Define the metadata address, contract name, operation name,
// and parameters.
// You can choose between MEX endpoint and HTTP GET by
// changing the address and enum value.
Uri mexAddress = new Uri("http://Some3rdPartyURL.svc?wsdl");//Some 3rd party url
// For MEX endpoints use a MEX address and a
// mexMode of .MetadataExchange
MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
string contractName = "IService";//"";//3rd party service name
string operationName = "SendMethod";//3rd party method name
object[] args = new object[] { "", "", "0" };//3rd party required parameters
//object[] operationParameters = new object[] { /*1, 2*/args };
// Get the metadata file from the service.
MetadataExchangeClient mexClient =
new MetadataExchangeClient(mexAddress, mexMode);
mexClient.ResolveMetadataReferences = true;
MetadataSet metaSet = mexClient.GetMetadata();
// Import all contracts and endpoints
WsdlImporter importer = new WsdlImporter(metaSet);
Collection<ContractDescription> contracts =
importer.ImportAllContracts();
ServiceEndpointCollection allEndpoints = importer.ImportAllEndpoints();
// Generate type information for each contract
ServiceContractGenerator generator = new ServiceContractGenerator();
var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
foreach (ContractDescription contract in contracts)
{
generator.GenerateServiceContractType(contract);
// Keep a list of each contract's endpoints
endpointsForContracts[contract.Name] = allEndpoints.Where(
se => se.Contract.Name == contract.Name).ToList();
}
if (generator.Errors.Count != 0)
throw new Exception("There were errors during code compilation.");
// Generate a code file for the contracts
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
// Compile the code file to an in-memory assembly
// Don't forget to add all WCF-related assemblies as references
CompilerParameters compilerParameters = new CompilerParameters(
new string[] {
"System.dll", "System.ServiceModel.dll",
"System.Runtime.Serialization.dll"});
compilerParameters.GenerateInMemory = true;
CompilerResults results = codeDomProvider.CompileAssemblyFromDom(
compilerParameters, generator.TargetCompileUnit);
if (results.Errors.Count > 0)
{
throw new Exception("There were errors during generated code compilation");
}
else
{
// Find the proxy type that was generated for the specified contract
// (identified by a class that implements
// the contract and ICommunicationbject)
Type clientProxyType = results.CompiledAssembly.GetTypes().First(
t => t.IsClass &&
t.GetInterface(contractName) != null &&
t.GetInterface(typeof(ICommunicationObject).Name) != null);
// Get the first service endpoint for the contract
ServiceEndpoint se = endpointsForContracts[contractName].First();
//se = endpointsForContracts[contractName].First();
WSHttpBinding wsBinding = this.fillWsHttpBinding();
string encodeValue = "MIIFazCCBFO****"; // here we should use the encoded certificate value generated by svcutil.exe.
X509Certificate2Collection supportingCertificates = new X509Certificate2Collection();
supportingCertificates.Import(Convert.FromBase64String(encodeValue));
X509Certificate2 primaryCertificate = supportingCertificates[0];
supportingCertificates.RemoveAt(0);
EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates);
EndpointIdentity identity = EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate);
var endpoint = new EndpointAddress(mexAddress, identity);
se.Binding = wsBinding;
se.Address = endpoint;
// Create an instance of the proxy
// Pass the endpoint's binding and address as parameters
// to the ctor
object instance = results.CompiledAssembly.CreateInstance(
clientProxyType.Name,
false,
System.Reflection.BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.Instance,
null,
new object[] { se.Binding, se.Address }, CultureInfo.CurrentCulture, null);
var methodInfo = instance.GetType().GetMethod(operationName);
var methodParams = methodInfo.GetParameters();
int count = args.Count();
object[] args2 = new object[count];
int i = 0;
foreach (var service1 in methodParams)
{
args2[i] = Convert.ChangeType(args[i], Type.GetType("System." + service1.ParameterType.Name));
i += 1;
}
Object retVal = instance.GetType().GetMethod(operationName).Invoke(instance, args2);/*Getting Error that certificate not provided???*/
}
}
catch (Exception ex)
{
string error = ex.ToString();
MessageBox.Show("Error Invoking Method: " + ex.Message);
}
private WSHttpBinding fillWsHttpBinding()
{
WSHttpBinding wsBinding = new WSHttpBinding();
wsBinding.CloseTimeout = new TimeSpan(0, 1, 0);
wsBinding.OpenTimeout = new TimeSpan(0, 10, 0);
wsBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
wsBinding.SendTimeout = new TimeSpan(0, 5, 30);
wsBinding.BypassProxyOnLocal = false;
wsBinding.TransactionFlow = false;
wsBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
wsBinding.MaxBufferPoolSize = 524288L;
wsBinding.MaxReceivedMessageSize = 10485760L;
wsBinding.MessageEncoding = WSMessageEncoding.Text;
wsBinding.TextEncoding = Encoding.UTF8;
wsBinding.UseDefaultWebProxy = true;
wsBinding.AllowCookies = false;
wsBinding.ReaderQuotas.MaxDepth = 32;
wsBinding.ReaderQuotas.MaxStringContentLength = 8192;
wsBinding.ReaderQuotas.MaxArrayLength = 10485760;
wsBinding.ReaderQuotas.MaxNameTableCharCount = 16384;
wsBinding.ReliableSession.Ordered = true;
wsBinding.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(10);
wsBinding.ReliableSession.Enabled = false;
wsBinding.Security.Mode = SecurityMode.Message;
wsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
wsBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
wsBinding.Security.Transport.Realm = "";
wsBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
wsBinding.Security.Message.NegotiateServiceCredential = true;
wsBinding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic256;
return wsBinding;
}
Where you are setting the certificate on the EndpointIdentity causes it to be used to validate the service's identity. You haven't set the client's credentials using instance.ClientCredentials.ClientCertificate.SetCertificate(...). You'll have to get the ClientCredentials from the ClientBase<> using reflection. See http://msdn.microsoft.com/en-us/library/ms732391%28v=vs.110%29.aspx.
var credentialProperty = instance.GetType().GetProperty("ClientCredentials");
var credentials = (ClientCredentials)credentialProperty.GetValue(instance, null);
credentials.ClientCertificate.SetCertficate(...);
You've also got two calls to EndpointIdentity.CreateX509CertificateIdentity, ignoring the output of one of them.
I have a net.Pipe WCF service that is hosted by IIS (right now, in my VS2010):
(Global.asax):
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ServiceHost svcHost = new ServiceHost(typeof(DoSomethingService), new Uri("net.pipe://localhost/helloworld"));
var serviceBinding = new NetNamedPipeBinding { MaxReceivedMessageSize = int.MaxValue, MaxConnections = 2048 };
var sect = new NamedPipeTransportSecurity { ProtectionLevel = ProtectionLevel.EncryptAndSign };
var sec = new NetNamedPipeSecurity { Mode = NetNamedPipeSecurityMode.Transport, Transport = sect };
serviceBinding.Security = sec;
svcHost.AddServiceEndpoint(typeof(IDoSomethingContract), serviceBinding, "");
svcHost.Open();
}
I have a console app client:
static void Main(string[] args)
{
var factory = new ChannelFactory<IDoSomethingContract>();
var defaultCredentials = factory.Endpoint.Behaviors.Find<ClientCredentials>();
factory.Endpoint.Behaviors.Remove(defaultCredentials);
factory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
factory.Credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
var serviceBinding = new NetNamedPipeBinding { MaxReceivedMessageSize = int.MaxValue, MaxConnections = 2048 };
var sect = new NamedPipeTransportSecurity { ProtectionLevel = ProtectionLevel.EncryptAndSign };
var sec = new NetNamedPipeSecurity { Mode = NetNamedPipeSecurityMode.Transport, Transport = sect };
serviceBinding.Security = sec;
var ep = new EndpointAddress("net.pipe://localhost/helloworld");
factory.Endpoint.Binding = serviceBinding;
var love = factory.CreateChannel(ep);
Console.WriteLine(love.Do());
Console.ReadKey();
}
Now everything works great when I run this as a user principal (and so I can use a PrincipalPermission inside my operation).
However, if I create myself a 'Network Service' command line prompt (using psexec), and try to run the client (with the service running, obviously), I get the EndpointNotFoundException exception.
Is there anything I need to do for Network Service to see my named pipe?
May be following articles useful for you
http://www.aspfree.com/c/a/asp-net/developing-a-wcf-service-library-and-hosting-it-as-wcf-web-service-using-vs2k8/
http://msdn.microsoft.com/en-us/library/dd728288%28v=vs.110%29.aspx
I was considering deleting this, but since somebody actually commented - I found the answer here:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/fcb7254a-15b5-4be4-bf67-34d500bdce2d/wcf-netpipe-endpoint-not-found-exception-whitout-elevated-user-rights-uac-enabled?forum=wcf
Basically, since I was running my dev server as my own account, the service was published in Session namespace. Once I published this on real IIS as Network Service, it was visible in the Global Namespace, so I could see it.