.Net Service References to JADE Web Services
Posted: Tue Mar 01, 2011 10:28 am
I am working on a Silverlight project in Visual Studio. Apparently because there's a requirement that all Web Service calls in Silverlight applications be asynchronous they've disabled the .Net 2.0 style Web References, so the only option now is the newer Service References. In the past we've always used Web References with Jade Web Services because they're easy and they just work. Now for the first time we're forced to use Service References, and this is what I've discovered:
Web Service options
If I add a Service Reference that has RPC/encoded selected in JADE, it doesn't create any proxy methods or objects.
It also doesn't like SOAP 1.2 - if I add a service reference with SOAP 1.2 selected, I get this error:
I ended up using Bare, because with RPC/Literal I get this warning:
Using the WSDL URL of the Web Service is more convenient than exporting and import a WSDL file, because you can just right click on the Service Reference and click Update. However about 2/3 of the time I do this JADE replies with:
Fault handling
When JADE has an exception in a Web Services method, it sends that to the client as a SOAP Fault. We're even throwing certain exceptions intentionally in our Web Service to communicate some types of information with the client.
Apparently as a security measure, the browser doesn't allow Silverlight access to the contents of messages that don't have a HTTP result code of 200 (success). SOAP Faults use a HTTP failure result code (404, 500 etc.), so instead of a FaultException which contains information from the server about what the error was, we get a generic CommunicationException that doesn't contain any information from the SOAP fault.
The solution could be to modify the Web Service to always use code 200 (which would be incorrect according to the SOAP spec but would at least work), or to modify the client to accept messages with failure codes. Since I can't modify JADE, I modified the client using the instructions found here. You just put this code in your Silverlight app before you use Web Services:
This tells Silverlight to use an alternative HTTP stack, which doesn't restrict access to the content of messages with failure result codes. The only down side is that since we're no longer using the browser's HTTP stack, there could be problems communicating with secure services.
However, even with this fix, the Visual Studio debugger still breaks when a FaultException is thrown. You can't catch the exception with an exception handler because it's thrown in System.ServiceModel, not in your code. It turns out that you can basically ignore this - just telling the debugger to continue allows your app to continue running as if nothing happened and you can access the FaultException in e.Error in your completed event handler. Of course having to hit continue every time your server sends an expected or handled SOAP fault would be annoying, so there's a workaround for it - you tell the debugger to not break on FaultExceptions:
Web Service options
If I add a Service Reference that has RPC/encoded selected in JADE, it doesn't create any proxy methods or objects.
It also doesn't like SOAP 1.2 - if I add a service reference with SOAP 1.2 selected, I get this error:
Document/Literal seems to require a very verbose notation for parameters where each method only has one parameter, which is an object - you have to create the object and set the parameter values as properties of this object. Both RPC/Literal and Bare use the more normal notation where you can set the parameter values is if it was a normal method call.Custom tool warning: Endpoint 'PMAPIServiceSoap' at address 'http://deepthought/pmapi55/jadehttp.dll ... Name=PMAPI' is not compatible with Silverlight 4. Skipping...
I ended up using Bare, because with RPC/Literal I get this warning:
It seems to be OK to use session handling, though I haven't got as far as testing that yet.Custom tool warning: Style Document on header sessionId does not match expected style Rpc.
Using the WSDL URL of the Web Service is more convenient than exporting and import a WSDL file, because you can just right click on the Service Reference and click Update. However about 2/3 of the time I do this JADE replies with:
This doesn't cause any real problem, it just requires hitting Update a few times before it works.The application is too busy. This request cannot be processed at this time due to heavy usage - please try again shortly.
Fault handling
When JADE has an exception in a Web Services method, it sends that to the client as a SOAP Fault. We're even throwing certain exceptions intentionally in our Web Service to communicate some types of information with the client.
Apparently as a security measure, the browser doesn't allow Silverlight access to the contents of messages that don't have a HTTP result code of 200 (success). SOAP Faults use a HTTP failure result code (404, 500 etc.), so instead of a FaultException which contains information from the server about what the error was, we get a generic CommunicationException that doesn't contain any information from the SOAP fault.
The solution could be to modify the Web Service to always use code 200 (which would be incorrect according to the SOAP spec but would at least work), or to modify the client to accept messages with failure codes. Since I can't modify JADE, I modified the client using the instructions found here. You just put this code in your Silverlight app before you use Web Services:
Code: Select all
WebRequest.RegisterPrefix("http://", Net.Browser.WebRequestCreator.ClientHttp)
However, even with this fix, the Visual Studio debugger still breaks when a FaultException is thrown. You can't catch the exception with an exception handler because it's thrown in System.ServiceModel, not in your code. It turns out that you can basically ignore this - just telling the debugger to continue allows your app to continue running as if nothing happened and you can access the FaultException in e.Error in your completed event handler. Of course having to hit continue every time your server sends an expected or handled SOAP fault would be annoying, so there's a workaround for it - you tell the debugger to not break on FaultExceptions:
- Debug menu
- Exception
- Common Language Runtime Exceptions
- System.ServiceModel
- Uncheck FaultException and FaultException'1
Code: Select all
Private Sub loginComplete(ByVal sender As Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)
If Not e.Error Is Nothing AndAlso TypeOf e.Error Is FaultException Then
Dim fe As FaultException = DirectCast(e.Error, FaultException)
Dim mf As System.ServiceModel.Channels.MessageFault = fe.CreateMessageFault
If mf.HasDetail Then
Dim reader As XmlDictionaryReader = mf.GetReaderAtDetailContents
Dim iErrorCode As Integer, sErrorItem As String, sErrorText As String
While reader.Read()
If reader.IsStartElement("errorCode") Then
iErrorCode = reader.ReadElementContentAsInt
ElseIf reader.IsStartElement("errorItem") Then
sErrorItem = reader.ReadElementContentAsString
ElseIf reader.IsStartElement("errorText") Then
sErrorText = reader.ReadElementContentAsString
End If
End While
' check iErrorCode and do something appropriate
End If
End If
End Sub