Silverlight 3 and WCF Faults
On a previous project that utilised WCF and Silverlight 2, I began implementing an error handling strategy that was going to rely on certain FaultExceptions being propagated to the Silverlight client. Well it turned out that Silverlight 2 does not support FaultExceptions so that was not going to fly and a custom solution was built.
Well Silverlight 3 does change this to some degree. Silverlight 3 now supports to the notion of Fault Contracts and FaultExceptions. In your Silverlight client code, if I have a WCF service called ThrowExceptionMethod (which as you may have guessed throws a FaultException) you can do something like this:
First wire up the service call:
private void Button_Click_1(object sender, RoutedEventArgs e) { TestRef.TestServiceClient client = new TestSilverlightApplication3.TestRef.TestServiceClient(); client.ThrowExceptionMethodCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_ThrowExceptionMethodCompleted); client.ThrowExceptionMethodAsync(); }
Then handle any error situation in the completed event:
void client_ThrowExceptionMethodCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { if (e.Error == null) MessageBox.Show("Call sucessfull"); else { string msg; FaultException<ExceptionDetail> fex = e.Error as FaultException<ExceptionDetail>; if (fex != null) msg = string.Format("Fault Exception recevied. Reason: {0}, Message: {1}", fex.Reason, e.Error.Message); else msg = string.Format("Error recevied of type: {0}, Message: {1}",e.Error.GetType(),e.Error.Message); MessageBox.Show(msg); } }
Here you can see that we test the ‘e.Error’ property to see if its an exception of type FaultException, otherwise the error will flow through generally as a CommunicationException as per normal WCF behaviour.
That all sounds easy, however you still need to tell the WCF service (or more specifically WCF itself) to return the fault using a HTTP status code of 200 (OK) instead of a HTTP status code of 500 (server error) which is the default. If you don’t do this, then the error will always come through as a CommunicationException .
You need a little bit of code and configuration for this. First you need to define a custom behaviour like so:
public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior { public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector(); endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector); } public class SilverlightFaultMessageInspector : IDispatchMessageInspector { public void BeforeSendReply(ref Message reply, object correlationState) { if (reply.IsFault) { HttpResponseMessageProperty property = new HttpResponseMessageProperty(); // Here the response code is changed to 200. property.StatusCode = System.Net.HttpStatusCode.OK; reply.Properties[HttpResponseMessageProperty.Name] = property; } } public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { // Do nothing to the incoming message. return null; } } // The following methods are stubs and not relevant. public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void Validate(ServiceEndpoint endpoint) { } public override System.Type BehaviorType { get { return typeof(SilverlightFaultBehavior); } } protected override object CreateBehavior() { return new SilverlightFaultBehavior(); } }
Then some config:
<system.serviceModel> <extensions> <behaviorExtensions> <add name=”silverlightFaults” type=”Microsoft.Silverlight.Samples.SilverlightFaultBehavior, SilverlightFaultBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”/> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name=”SilverlightFaultBehavior”> <silverlightFaults/> </behavior> </endpointBehaviors> </behaviors> <services> <service name=”Calculator.Web.Service”> <endpoint address=”” binding=”basicHttpBinding” contract=”Calculator.Web.Service” behaviorConfiguration=”SilverlightFaultBehavior” /> </service> </services> </system.serviceModel>
So, its still not quite there yet, but its better. It would be nice if this status code behaviour were part of the default Silverlight service template and that it was a provided behaviour, rather than a roll your own custom one.
Note: The custom behaviour, as well as more information on this subject, can and was taken from this article here.