Click here to Skip to main content
Click here to Skip to main content
Go to top

Retrieve WebService Exceptions from a Silverlight Client

, 23 Jan 2010
Rate this:
Please Sign up or sign in to vote.
This article shows how to retrieve exceptions from a Web Service to a Silverlight Client by creating a WCF endpoint behavior.

Introduction

By default, exceptions thrown in a WCF service are not returned to the Silverlight client, and everybody knows exception handling is critical for any enterprise solution, i.e., to notify users that a fault happens or for debugging purposes.

Before writing this article, I searched a lot about how to retrieve exceptions from Web Services to a Silverlight client. Most articles talk about implementing a custom error object whose attributes are populated with the error exception in the Web Service and the object passed from the Web Service to the Silverlight client. Although this can be quite a good solution, this adds additional overhead in retrieving the exception message from client side. What we want here is to retrieve the error directly from the e event handler, such as below:

private void customerService_Completed(object sender, 
             GenerateErrorCompletedEventArgs e)
{
   if (e.Error == null)
   {
     myOutput.Text = "Sucess";
   }
   else
   {
     myOutput.Text = e.Error.Message.ToString();
   }
}

Background

By default, WCF services return fault messages with an HTTP 500 response code. Due to limitations in the browser networking stack, the bodies of these messages are inaccessible within Silverlight, and consequently, the fault messages cannot be read by the client.

To override this constraint and to make Silverlight clients retrieve faults, the key is to make the WCF service return the fault message with an HTTP 200 response code instead of the HTTP 500 response code. This change enables Silverlight to read the body of the message, and also enables WCF clients of the same service to continue working using their normal fault-handling procedures; hence, no refactoring is needed for existing client codes.

Using the Code

The ServiceFaultBehavior class from ServiceFaultBehavior.cs in the attached source code implements the IEndpointBehavior interface used for writing custom endpoint behaviors. The main implementation is the ApplyDispatchBehavior method which applies the ServiceFaultMessageInspector to the WCF endpoint behavior.

using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

namespace Service
{
public class ServiceFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
   public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
               EndpointDispatcher endpointDispatcher)
   {
      ServiceFaultMessageInspector inspector = new ServiceFaultMessageInspector();
      endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
   }

   // 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(ServiceFaultBehavior); }
   }

   protected override object CreateBehavior()
   {
      return new ServiceFaultBehavior();
   }
}
}

It is the ServiceFaultMessageInspector class which changes the HTTP response from HTTP 500 to HTTP 200, if the reply is a Fault, by using its BeforeSendReply method.

using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;

namespace Service
{

/// <summary>
/// Change reponse code from HTTP 500 to HTTP 200 works with 
/// <see "Service.ServiceFaultBehavior" />
/// </summary>
public class ServiceFaultMessageInspector : 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;
     }
}
}

Configuration

Don't forget to configure the <system.serviceModel> section in the WCF Service web.config to apply the new WCF endpoint behavior to the service.

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="serviceFaults" 
        type="Service.ServiceFaultBehavior, 
        Service, 
        Version=1.0.0.0, 
        Culture=neutral, 
        PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ServiceFaultBehavior">
          <serviceFaults/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="Service.CustomerServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <services>
      <service behaviorConfiguration="Service.CustomerServiceBehavior" 
      name="Service.CustomerService">
        <endpoint address="" behaviorConfiguration="ServiceFaultBehavior" 
        binding="basicHttpBinding" contract="Service.CustomerService"/>
        <!--<endpoint address="mex" binding="mexHttpBinding" 
        contract="IMetadataExchange"/>-->
      </service>
    </services>
</system.serviceModel>

After this modification is made to the WCF service, faults will be accessible to Silverlight clients that access this service.

Retrieving Faults for Debugging

Two types of SOAP faults can be sent: Declared and Undeclared. Declared SOAP faults are those in which an operation has a FaultContractAttribute attribute that specifies a custom SOAP fault type. These SOAP faults are used in production. Undeclared SOAP faults are not specified in the contract for an operation. These SOAP faults are used only for debugging.

Undeclared SOAP faults are useful for debugging a service. It is simple to enable these undefined faults to propagate the complete information about an exception in the fault message sent from the service to the client. This is done by setting the includeExceptionDetailInFaults attribute of the <serviceDebug> configuration element to true.

<serviceDebug includeExceptionDetailInFaults="true"/>

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

ds47

Mauritius Mauritius
No Biography provided

Comments and Discussions

 
GeneralCan't get this to work PinmemberNeCroFire9-Dec-10 0:43 
GeneralRe: Can't get this to work PinmemberNeCroFire9-Dec-10 1:50 
Question[My vote of 1] you could not have copied it from msdn? Pinmemberjosephpotgieter1-Jul-10 3:50 
GeneralMy vote of 1 Pinmemberjosephpotgieter30-Jun-10 4:56 
GeneralSince Silverlight 3, one can also use the Client HTTP Stack PinmemberMichael Epner27-Jan-10 5:04 
QuestionHow do you know I was looking for such tips ? PinmemberCrusty Applesniffer25-Jan-10 21:37 
GeneralImpressive; I give it a 5 Pinmemberaccent12325-Jan-10 15:46 
GeneralI like it, so made it public and gave it a 5 vote PinmvpSacha Barber23-Jan-10 21:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 24 Jan 2010
Article Copyright 2010 by ds47
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid