Support HTTP and HTTPS in a SOAP service reference - c#

I imported a SOAP service by using "Add -> Service reference" in Visual Studio. This generates a lot code that helps for consuming the service.
The SOAP has to call different endpoints for every different customer base using the application. Some are endpoints are in plain HTTP, and others are HTTPS. (All on internal network so HTTP is not a big deal). Every HTTP endpoint works fine, every HTTPS endpoint works, but the application wont support both.
This configuration supports HTTPS:
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.ServiceV201209Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.Security.Mode = BasicHttpSecurityMode.Transport;
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
Removing the "Security.Mode" line will support HTTP.
I have trouble switching between the modes. The best that I could do was check wether the end point was HTTP or HTTPS, and change the binding configuration during runtime. But this made it work only on the second SOAP call after switching endpoints. That is horrible so I scrapped that.
Is there a simple configuration that definitely supports both?

Related

Error comsuming WCF service metadata?

We have a WCF webservice selfhosted in a windows service application.
It works fine to generate and call a simple method from both a .NET winform application and SoapUI(after changin to correct IP and set authentication header).
The problem is that when trying to add the WSDL to a BizTalk we get "Error consuming WCF service metadata. Object reference not set to an instance of an object". and nothing more.
I have search internet and there is some that have hade the same problem and they are talking about namespace problems. I have checked the generated .NET winform proxy but there is no warnings or anything that might suggest problems with namespaces? I notice in the WSDL that some namespaces tags do only have a "" value but not sure if that might be a problem.
Is there any way to get more information from Biztalk about where the error might be in this massive WSDL?
When browsing the WSDL from another computer it will use localhost(instead of the IP och DNS) so I have to use WSDL singelfile to genereate the proxy and I have to manually change the URL to the specific IP in the client to get it working. This should however not be the problem.
Here is how the service is started :
_serviceHost = new ServiceHost(typeof(T), new Uri(baseEndpoint.Address));
ServiceThrottlingBehavior throttleBehavior = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 200,
MaxConcurrentInstances = 2147483647,
MaxConcurrentSessions = 2000,
};
_serviceHost.Description.Behaviors.Add(throttleBehavior);
_serviceHost.Description.Behaviors.Add(smb.HttpGetEnabled = true);
foreach (var endpointDescription in _additionalServiceEndpoints)
{
var endpoint = new ServiceEndpoint(ContractDescription, endpointDescription.Binding, new EndpointAddress(endpointDescription.Address));
_endpointBehaviors.ForEach(c => endpoint.EndpointBehaviors.Add(c));
_serviceHost.AddServiceEndpoint(endpoint);
}
_serviceBehaviors.ForEach(c => _serviceHost.Description.Behaviors.Add(c));
ServiceAuthorizationBehavior serviceAuthorizationBehavior = _serviceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
if (serviceAuthorizationBehavior == null)
{
_serviceHost.Description.Behaviors.Add(new ServiceAuthorizationBehavior());
}
if (_authorizationPolicies.Count > 0)
{
serviceAuthorizationBehavior.ExternalAuthorizationPolicies = new ReadOnlyCollection<IAuthorizationPolicy>(_authorizationPolicies.ToArray());
serviceAuthorizationBehavior.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
serviceAuthorizationBehavior.ServiceAuthorizationManager = new CustomServiceAuthorizationManager();
}
((ServiceBehaviorAttribute)_serviceHost.Description.Behaviors[typeof(ServiceBehaviorAttribute)]).MaxItemsInObjectGraph = 2147483647;
((ServiceBehaviorAttribute)_serviceHost.Description.Behaviors[typeof(ServiceBehaviorAttribute)]).IncludeExceptionDetailInFaults = true;
_serviceHost.Open();
Have you tried the solution which is described in this forum thread: https://social.msdn.microsoft.com/Forums/en-US/19105b96-5ec4-40f1-972b-2e04f889061f/biztalk-2010-error-consuming-wcf-service-metadata-object-reference-not-set-to-an-instance-of-an?forum=biztalkgeneral
It points to a blog post located here: https://blogs.msdn.microsoft.com/kerreybpi/2009/02/05/biztalk-error-wcf-service-consuming-wizard/
The solution might be (from the link above):
1, Generate WSDL and XSD files by using svcutil tool
svcutil.exe /t:metadata http://yourservice
2, Verify nodes in WSDL file, make sure the node has a target namespace define. It doesn't matter what attribute value you use, maybe just looks like http://any.
3, Select Metadata Files(WSDL and XSD) as the source
4, Everything should be fine.
I just had the same error when trying to consume .svc?singleWsdl. When I consume as just .svc into the "Add Generated Items" wizard then it works.

Different services on different ports in WCF

I'm in a situation where I want to use WCF to expose two different interfaces:
Internal (IPC communication)
External (Http REST)
The external interface shouldn't be able to see or use the internal interface so what I was thinking about were to host the two services on different ports e.g. (8000 for internal and 8001 for external) and then block all external communication on port 8000.
Moreover, I tried fiddling around with using named pipes for IPC communication, and I ran into an issue. If the unexpected situation occurs, that the service crashes or goes offline the client would also have to be restarted to be able to establish the communication with the service via the named pipes. Is this normal behavior and can it be avoided?
I have the following code so far:
// Service Host
var host = new ServiceHost(serviceContract, new Uri(_address));
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
var behaviour = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behaviour.InstanceContextMode = InstanceContextMode.Single;
// Local Endpoint
host.AddServiceEndpoint(typeof(ILocalServiceContract), new BasicHttpBinding(), "Local");
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
// External Endpoint
var webHttpBidning = new WebHttpBinding { TransferMode = TransferMode.Streamed };
var externalEndPoint = host.AddServiceEndpoint(typeof(IExternalServiceContract), webHttpBidning, "External");
externalEndPoint.Behaviors.Add(new WebHttpBehavior());
They are currently both hosted on the same port which I want to avoid, I'm fairly new to WCF and I could really use some guidance for best practice and what I'm doing wrong. As mentioned earlier the 'BasicHttpBinding' could maybe be replaced with a named pipe if my issue can be resolved.
Looking forward to hear to the experts and if you need any clearification feel free to ask :)
I solved the issue with the following code on server. On the clients which communicate with the server via named pipes, I used "WcfClientProxyGenerator" library to generate fault tolerant client proxies.
_namedPipeAddress = "net.pipe://localhost/";
_httpAddress = "http://localhost:8000";
var host = new ServiceHost(serviceContract, new Uri(_namedPipeAddress), new Uri(_httpAddress));
host.Description.Behaviors.Add(new ServiceMetadataBehavior { });
var behaviour = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behaviour.InstanceContextMode = InstanceContextMode.Single;
behaviour.IncludeExceptionDetailInFaults = true;
// Local Endpoint
host.AddServiceEndpoint(typeof(ILocalServiceContract), new NetNamedPipeBinding(), "Local");
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexNamedPipeBinding(), "mex");
//// External Endpoint
var webHttpBidning = new WebHttpBinding { TransferMode = TransferMode.Streamed };
var externalEndPoint = host.AddServiceEndpoint(typeof(IExternalServiceContract), webHttpBidning, new Uri(_httpAddress));
externalEndPoint.Behaviors.Add(new WebHttpBehavior());
For further improvements to this solution feel free to comment :)

How can I test if the DataContractSerializer was effectively replaced by XmlProtoSerializer?

I have a series of self-hosted WCF services (using netTcpBinding) that I want to enhance by using the fantastic protobuf-net serializer.
I configured both the client and the service endpoints with the custom behavior extensions as instructed in the documentation of the ProtoEndpointBehavior class (see here). My tests ran fine from the very first try, however, I'm very skeptical of WCF-stuff running fine at the very first try ;)
Is there a simple way in which I can assert that the default DataContractSerializer was replaced by the XmlProtoSerializer?
I would really favor a test that can be also coded as part of a unit test. I wouldn't like the protobuf-net library to be inadvertently disabled by careless tampering of the .config file.
If you call your service in wcf test client, you will see < proto/> tag in the body of response
If you configure your client to use proto behaviour and call service with "inadvertently disabled protobuf", your answer will be null because of different serializers. You can check it in your test
var address = new EndpointAddress( url );
var binding = GetBinding( address.Uri );
var factory = new ChannelFactory<TService>( binding, address );
factory.Endpoint.EndpointBehaviors.Add( CreateProtobufEndpointBehavior() );
var client = factory.CreateChannel();
var answer = client.GetSomeInt();
AssertAnswerIsNotNull(answer);

Getting WSHttpBinding working through an http proxy

I have a WCF service and a client that uses that service. They use WSHttpBinding with MTOM message encoding. The client doesn't use an app.config file, and does all of the endpoint configuration in code.
It all works rather nicely in a normal environment, however I now have some users who are attempting to connect to the service from behind an http proxy. Whenever they try to connect, they get a 407 (Proxy Authentication Required) warning.
I've managed to set up my own testing environment using a virtual machine with a private network that connects to a proxy, so I can simulate what they are seeing.
I've tried setting the system.net useDefaultCredentials property in app.config, but that doesn't seem to have any effect. I've examined the packets being sent, and they don't contain any Proxy-Authentication headers. Looking at web traffic through the proxy, they do use that header.
I've also tried hard coding the proxy server into the client, but that gives a "cannot connect to server" exception. Examining the packets, the client sends out 3 small ones to the proxy, none of which are an http request, and that's it. I'm not sure what it's doing there.
I even went so far as to add a message inspector to the client, and manually inject the required header into the requests, but that's not showing up in the header.
I'm hitting the end of my rope here, and I really need a solution. Any ideas on what I'm missing here, or a solution?
This ( WCF Service with wsHttpBinding - Manipulating HTTP request headers ) seems promising, but I'm still stuck.
EDIT:
Here's a portion of the code.
var binding = new WSHttpBinding();
binding.MaxReceivedMessageSize = 100000000;
binding.MessageEncoding = WSMessageEncoding.Mtom;
binding.ReaderQuotas.MaxArrayLength = 100000000;
binding.OpenTimeout = new TimeSpan(0, 2, 0);
binding.ReceiveTimeout = new TimeSpan(0, 2, 0);
binding.SendTimeout = new TimeSpan(0, 2, 0);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.UseDefaultWebProxy = true;
// I've also tried setting this to false, and manually specifying the binding.ProxyAddress
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
var endpointAddress = new EndpointAddress(new Uri(hostUrl));
clientProxy_ = new UpdaterServiceProxy(binding, endpointAddress);
// this behavior was the attempt to manually add the Proxy-Authentication header
//clientProxy_.Endpoint.Behaviors.Add(new MyEndpointBehavior());
clientProxy_.ClientCredentials.UserName.UserName = userName;
clientProxy_.ClientCredentials.UserName.Password = password;
clientProxy_.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
System.ServiceModel.Security.X509CertificateValidationMode.ChainTrust;
// do stuff...
After a lot of experimentation I've made some progress. It turns out that I can manually specify the proxy, and it will work.
WebProxy proxy = new WebProxy("http://x.x.x.x:3128", false);
proxy.Credentials = new NetworkCredential("user", "pass");
WebRequest.DefaultWebProxy = proxy;
That code appears just before I instantiate my clientProxy_. I had tried this previously, but it silently failed because I hadn't specified the port. I still cannot get it to pick up the proxy settings specified in the Windows Internet Settings. I tried setting proxy.Credentials = CredentialCache.DefaultNetworkCredentials; but that also seemed to have no effect.
I'm also running into a problem now with the client trying to validate the service's certificate using ChainTrust, but the requests for the chained certificates are not using the proxy settings. Since this is a more specific question, I wrote it up separately here: Certificate validation doesn't use proxy settings for chaintrust
So I'm still hoping for more help.
UPDATE:
I ended up just adding a network configuration dialog to my application so the user could specify their proxy settings. I've been unable to get the automatic proxy settings to work properly.

Using a C# Service Reference SOAP Client with different Endpoint URIs

I have a SOAP Webservice that is available on multiple servers, thus having multiple endpoints. I want to avoid adding multiple Service References (C# SOAP Port Clients) with different names just to talk to this services, since the API is exactly the same.
Is there a way to configure the Endpoint URI at runtime?
I use the following which works great:
ServiceReference1.wsSoapClient ws= new ServiceReference1.wsSoapClient();
ws.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://xxx/myservice.asmx");
I had trouble finding this one also. I finally just borrowed the configuration binding and did this:
private static wsXXXX.IwsXXXXClient wsXXXXClientByServer(string sServer)
{
// strangely, these two are equivalent
WSHttpBinding binding = new WSHttpBinding("WSHttpBinding_IwsXXXX");
// WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message, false);
EndpointAddress remoteAddress = new EndpointAddress(new Uri(string.Format("http://{0}:8732/wsXXXX/", sServer)), new UpnEndpointIdentity("PagingService#rl.gov"));
return new wsXXXX.IwsXXXXClient(binding, remoteAddress);
}

Categories

Resources