The root of the problem was that we had only implemented one Endpoint, using the WSHttpBinding. This was great for any clients running .NET 3.0 or later, but this binding doesn't provide any backwards compatibility for clients running a framework earlier than .NET 3.0.
Endpoint Comparison Chart (plus Client .NET Framework column)
(This comparison chart was created by Aaron Skonnard and the original can be found here: http://www.pluralsight.com/community/blogs/aaron/archive/2007/03/22/46560.aspx)So the answer seems simple enough right? If I need .NET 2.0 clients to connect to my WCF web services then all I need to do is to add an Endpoint that uses the BasicHttpBinding and voila, I should be good to go right? Wrong - that is where my troubles started.
WSHttpBinding vs. BasicHttpBinding
There are some MAJOR differences between the two bindings and for all intents & purposes you should stay away from using the BasicHttpBinding whenever possible. However in some cases (like the one I found myself in) you need that backwards compatibility with .NET 2.0 clients and the BasicHttpBinding is the only way to get it. I would only recommend using the BasicHttpBinding in an intranet scenario however & in conjunction with Windows Authentication.A great article on the differences between the bindings can be found here: http://www.topxml.com/rbnews/WSCF/WCF/re-89303_WCF---BasicHttpBinding-compared-to-WSHttpBinding-at-SOAP-packet-level-.aspx
A great post on how to use Windows Authentication with the BasicHttpBinding can be found here: http://www.rickgaribay.net/archive/2007/04/04/recipe-wcf-basichttpbinding-with-windows-authentication.aspx
NOTE: I am writing a follow up post on getting the BasicHttpBinding with Windows Authentication and a .NET 2.0 client.
Creating Multiple WCF Endpoints
So according to this article: http://www.devx.com/codemag/Article/33655/1763/page/4 adding an Endpoint that uses BasicHttpBinding should be fairly straight forward & easy. I would end up with something like this:In cases where you might want to expose multiple endpoints for the same .svc endpoint, you can use relative addressing. This example illustrates a case where clients can access the same Service.svc endpoint using BasicHttpBinding or WSHttpBinding to support different Web service protocols. The <endpoint> configuration for the service uses relative addressing, appending "/Soap11" and "/Soap12" to the endpoint address:Ok, great, no problemo. I just need to add an Endpoint that uses the BasicHttpBinding and assign it's address attribute a value. To simplify things, I created a quick test WCF Service host & a client (both are downloadable at the bottom of this post.)
<service name="HelloIndigo.HelloIndigoService" > <endpoint address="Soap11" contract="HelloIndigo.IHelloIndigoService" binding="basicHttpBinding"/> <endpoint address="Soap12" contract="HelloIndigo.IHelloIndigoService" binding="wsHttpBinding"/> </service>
Clients address each service endpoint in this way:
http://localhost/IISHost/Service.svc/Soap11 http://localhost/IISHost/Service.svc/Soap12
1: <services>
2: <service name="Service" behaviorConfiguration="ServiceBehavior">
3: <!-- Service Endpoints -->
4: <endpoint
5: address="ws"
6: binding="wsHttpBinding"
7: contract="IService" />
8: <endpoint
9: address="basic"
10: binding="basicHttpBinding"
11: contract="IService" />
12: <endpoint
13: address="mex"
14: binding="basicHttpBinding"
15: contract="IMetadataExchange" />
16: </service>
17: </services>
And based on the articles I read my client svc URIs should look like this:
http://localhost:12345/WCFMultipleEndpointTest/Service.svc/ws
http://localhost:12345/WCFMultipleEndpointTest/Service.svc/basic
(the 12345 will be replaced by whatever dynamic port number the built in Visual Studio host assigns when you run the host locally.)
Will The Real URI Please Stand Up
So since we added endpoint address attributes to all of our endpoints and we have different URIs now for each endpoint we should use the new URIs when trying to connect to our services right? Wrong.Even though you now have different URIs for each endpoint, you still use the base ".svc" URI when connecting clients to the service. I.E. to connect to the BasicHttpBinding Enpoint you would use
"http://localhost:12345/WCFMultipleEndpointTest/Service.svc"
and to connect to the WsHttpBinding Endpoint you would also use
"http://localhost:12345/WCFMultipleEndpointTest/Service.svc"
When you add a reference to these endpoints using Visual Studio a client connection will actually be specified for both endpoints if you are adding a Service Reference. If you are adding a Web Reference a connection will be configured for the BasicHttpBinding endpoint only as it is the only endpoint the Web Reference is compatible with.
Example client web.config Endpoint configurations:
1: <client>
2: <endpoint address="http://localhost:1564/WCFMultipleEndpointTest/Service.svc/ws" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService" contract="TestServiceReference.IService" name="WSHttpBinding_IService">
3: <identity>
4: <userPrincipalName value="youridentity@domainname.com"/>
5: </identity>
6: </endpoint>
7: <endpoint address="http://localhost:1564/WCFMultipleEndpointTest/Service.svc/basic" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService" contract="TestServiceReference.IService" name="BasicHttpBinding_IService"/>
8: </client>
Notice that even though you used the base .svc URI to address the web service, svcutil.exe created proxy classes to connect to both endpoints using the full address of each endpoint
Client Code
Here is the sample code that connects to the WsHttpBinding & BasicHttpBinding Endpoints using the proxy created by svcutil.exe and the code that connects to the BasicHttpBinding using the proxy created by wsdl.exe.' Create a web service client using .NET 3.0+ & the WSHttpBindingDim wsServiceClient As New TestServiceReference.ServiceClient("WSHttpBinding_IService")
Try' Open a connection to the web service' Call the web service method and get the responseDim wsResponse As String = wsServiceClient.GetData(255)
' Write out the responseResponse.Write("<strong>Service Reference WsHttpBinding GetData Call:</strong> " + wsResponse)Catch fx As System.ServiceModel.FaultException
' Show the fault we encounteredResponse.Write("WsHttpBinding call failed. Fault Exception")Response.Write("<br /><br />")Response.Write(fx.Message)
Response.Write("<br /><br />")Response.Write(fx.StackTrace)
Catch ex As Exception
' Show the error we encounteredResponse.Write("WsHttpBinding call failed. General Exception")Response.Write("<br /><br />")Response.Write(ex.Message)
Response.Write("<br /><br />")Response.Write(ex.StackTrace)
Finally' If the connection was opened, close itIf (wsServiceClient.State = System.ServiceModel.CommunicationState.Opened) Then
wsServiceClient.Close()
End If
' If the connection was faulted, abort itIf (wsServiceClient.State = System.ServiceModel.CommunicationState.Faulted) Then
wsServiceClient.Abort()
End If
End Try
Response.Write("<br /><br />")' Create a web service client using .NET 3.0+ & the BasicHttpBindingDim basicServiceClient As New TestServiceReference.ServiceClient("BasicHttpBinding_IService")
Try' Open a connection to the web servicebasicServiceClient.Open()
' Call the web service method and get the responseDim basicResponse As String = basicServiceClient.GetData(100)
' Write out the responseResponse.Write("<strong>Service Reference BasicHttpBinding GetData Call:</strong> " + basicResponse)Catch fx As System.ServiceModel.FaultException
' Show the fault we encounteredResponse.Write("BasicHttpBinding call failed. Fault Exception")Response.Write("<br /><br />")Response.Write(fx.Message)
Response.Write("<br /><br />")Response.Write(fx.StackTrace)
Catch ex As Exception
' Show the error we encounteredResponse.Write("BasicHttpBinding call failed. General Exception")Response.Write("<br /><br />")Response.Write(ex.Message)
Response.Write("<br /><br />")Response.Write(ex.StackTrace)
Finally' If the connection was opened, close itIf (basicServiceClient.State = System.ServiceModel.CommunicationState.Opened) Then
basicServiceClient.Close()
End If
' If the connection was faulted, abort itIf (basicServiceClient.State = System.ServiceModel.CommunicationState.Faulted) Then
basicServiceClient.Abort()
End If
End Try
Response.Write("<br /><br />")' Create a web service client using .NET 2.0 & the BasicHttpBinding - This is the only binding that will work for .NET 2.0' clients and is provided for backwards compatibilityTry' Create the web service clientDim basicWebClient As New TestServiceReferenceBackwardsCompatibility.Service()
' Call the web serviceResponse.Write("<strong>Web Refrence BasicHttpBinding GetData Call:</strong> " + basicWebClient.GetData(3, True))
Catch ex As Exception
' Show the error we encounteredResponse.Write("Web Reference BasicHttpBinding call failed. General Exception")Response.Write("<br /><br />")Response.Write(ex.Message)
Response.Write("<br /><br />")Response.Write(ex.StackTrace)
End TryCode Download
- Aaron
http://www.churchofficeonline.com
4 comments:
Solved my problem, Thanks a lot
loudest engagements coal ontological recognize renamed leavis senda fins hong destined
masimundus semikonecolori
Should it really be a space in the url after Service.svc ?
No, the space was a typo. Thanks for catching that, I removed it. :)
Post a Comment