Receiving SOAP faults is not supported in the default configuration. This seems to be some Web browser limitation, but a nightmare for WCF developer.
When one service sends a fault message, an exception is thrown on the client. Unfortunately it does not specify any information about the fault that has occurred. Fortunately in SL3, there are two ways to enable SOAP fault consumption. This makes you feel good at least, because you probably think there are two possible solutions now. I will describe both in this post, but in my opinion no one is the the ultimate one (see conclusion below).
Using of Browser Network Stack
If you modify your service to return SOAP faults with an HTTP status code of 200, Silverlight 3 will successfully process faults. Note that this will make the service non-compliant with the SOAP protocol, since SOAP requires a response code in the 400 or 500 range for faults. If the service is a WCF service, you can create an endpoint behavior that plugs in a message inspector that changes the status code to 200. Then, you can create an endpoint specifically for Silverlight consumption, and apply the behavior there. Your other endpoints can remain SOAP-compliant.
For example, usually when service throws an FaultException the client will experience the Communication Exception as shown at the next picture:
Exactly this is the problem, because you will get an error like “Not found” for every possible fault exception thrown by the service. In Other words you will not be able to implement error handling.
To workaround this following WCF Behavior can help. To make it working do following.
1. Download the bits and copy the DLL Daenet.SLFaultCorrector.Dll in the bin folder of your service.
2. Create service configuration as shown at the next picture. Note that the configuration for service registers custom endpoint, defines an extra endpoint for Silverlight applications and related endpoint behavior, which will register an internal inspector for handling of faults.
Here is the configuration which contains all:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="silverlightFaultCorrector"
type="Daenet.SLFaultCorrector.SLFaultCorrector, Daenet.SLFaultCorrector,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<services>
<service name="Daenet.SecurityManager.Service.SecurityManagerService"
behaviorConfiguration="Daenet.SecurityManager.AdministrationService.AdminServiceBehavior">
<!- Endpoint for Standard SOAP clients -->
<endpoint address="" binding="basicHttpBinding"
contract="Daenet.SecurityManager.Service.ISecurityManagerService"
bindingConfiguration="SecMgrAdminService_IAdminService">
</endpoint>
<!- Endpoint for Silverlight clients –>
<endpoint address="SilverlightEndpoint" binding="basicHttpBinding"
behaviorConfiguration="SilverlightFaultBehavior"
contract="Daenet.SecurityManager.Service.ISecurityManagerService"
bindingConfiguration="SecMgrAdminService_IAdminService"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="SecMgrAdminService_IAdminService"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Daenet.SecurityManager.AdministrationService.AdminServiceBehavior">
<serviceMetadata httpGetEnabled="True" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="32" maxConcurrentSessions="1000" maxConcurrentInstances="32" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<!- Behavior which is used by Silverlight clients only-->
<behavior name="SilverlightFaultBehavior">
<silverlightFaultCorrector/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
</system.serviceModel>
As you see I have used two different endpoints above. One can be used by common SOAP capable clients. The second one should be used by Silverlight clients. If the first endpoint can be accessed by following URI http://localhost/SecurityManagerService/SecurityManagerService.svc, then the extra Silverlight endpoint would have this URI http://localhost/SecurityManagerService/SecurityManagerService.svc/SilverlightEndpoint. This works fine for IIS too.
Now your are done with the service. However extracting of one fault exception at the client side in SIlverlight is also a little issue. Following code snippet shows how to grab the service thrown fault in a case of Daenet.SLFaultCorrector":
FaultException fault = e.Error as FaultException;
if (fault != null)
{
if (fault.CreateMessageFault().GetDetail<SecurityManagerFault>() is SecurityManagerFault)
{
// Handle your fault exception here. In this case SecurityManagerFault.
}
else
{
// Handle none fault exceptions here
}
} The catched exception looks now a bit different than in the previous case:
One more thin at the end of this story. To be able to use typed fault exceptions the operation contract has to be styled for this purpose:
[OperationContract]
[FaultContract(typeof(SecurityManagerFault))]
void MyOperation( . . . );
Using of alternative Http Stack (ClientHttp since Silverlight 3)
Additionally since SL3 You can register an alternative HTTP stack using the RegisterPrefix method. Following line of code should be executed at the very beginning of the application to setup the new stack.
bool registerResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
Silverlight 3 provides the “client HTTP stack” which, unlike the “browser HTTP stack”, allows you to process SOAP-compliant fault messages. However, a potential problem of switching to the alternative HTTP stack is that information stored by the browser (such as authentication cookies) will no longer be available to Silverlight, and thus certain scenarios involving secure services might stop working, or require additional code to work. For more information, see HttpCookieContainerBindingElement.
I figured out that windows authentication which has been used in my services does not work anymore by using of new network stack. By default service proxies at the client do not send required authentication headers.
Conclusion:
If you use first method (the old one) you have to force your service to be incompatible with SOAP protocol (Additional endpoints are required). If you use the second method you will not be able to authenticate by using of NTLM or Kerberos.
Posted
Aug 22 2009, 12:17 PM
by
Damir Dobric