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.