Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Retrieve WebService Exceptions from a Silverlight Client

0.00/5 (No votes)
23 Jan 2010 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here