Creating and Handling Faults in Silverlight
Microsoft Silverlight will reach end of support after October 2021. Learn more.
Silverlight version 4 enables support for the Windows Communication Foundation (WCF) SOAP fault programming model, which allows the service to communicate error conditions to the client. In previous versions of Silverlight, if an error occurred on the service, it would register as an HTTP 500 response code and details about the error would not be accessible to the Silverlight client.
Exceptions on the service are converted into SOAP faults before being sent by the service, and then they are converted from SOAP faults back into fault exceptions after they are received by the Silverlight client. The fault message contains a standard set of properties and an extensible detail object, whose content is controlled by the service. Depending on the contents of the detail object, a fault can be either defined or undefined.
Undeclared faults are useful when debugging a service. The fault message can contain relevant information about the server exception, such as the exception type, the message, and a stack trace.
Declared faults are useful in production environments when the client logic needs to be driven by the error condition. The service developer defines a fault type, which is included in the fault message and controls exactly what information the fault type carries on the wire.
The fault type is exposed in the service metadata, so Silverlight clients can have a strongly typed experience when working with the fault type and build their logic around it.
For more information about handling SOAP faults in WCF, see Specifying and Handling Faults in Contracts and Services.
Configuring WCF SOAP Faults for Use with Silverlight Clients
Receiving SOAP faults is not supported in the default configuration, due to Web browser limitations. When the service does send a fault, an exception is thrown on the client, but it does not specify any information about the fault that has occurred. However, there are two ways to enable SOAP fault consumption:
Modify the HTTP status code: You can modify your service to return SOAP faults with an HTTP status code of 200, Silverlight 4 so that faults will be processed successfully. How to do this is outlined below. Note that this will make the service non-compliant with the SOAP protocol, because 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 will still remain SOAP-compliant.
Use the alternative client HTTP stack: You can register an alternative HTTP stack by using the RegisterPrefix method. See below for an outline of how to do this. Silverlight 4 provides the option of using a client HTTP stack which, unlike the default 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.
HTTP status codes
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 send faults that will be accessible to a Silverlight client, a WCF service must modify the way it sends its fault messages. The key change needed is for WCF to return fault messages 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.
The modification on the server can be made by defining a WCF endpoint behavior for Silverlight faults. The following code sample shows how to do this.
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();
}
}
The code for this WCF endpoint behavior needs to be configured for the service in the WCF service configuration file. The modification needed is shown in the following code sample. Note that some sections have been omitted for clarity.
<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>
After this modification is made to the WCF service, faults will be accessible to Silverlight clients that access this service.
Client HTTP stack
You can register an alternative HTTP stack by using the RegisterPrefix method. Place the following line of code at the beginning of your Silverlight application (for example, on the constructor for the MainPage class) and the application will use the new client stack for all communication to http:// services:
bool registerResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
If the registration returns true (which it should, unless the prefix has been previously registered), then you don't need any special behaviors on the WCF service. All faults should be received correctly by the Silverlight application.
Using Undeclared 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 and are discussed in the next section. Undeclared SOAP faults are not specified in the contract for an operation. These SOAP faults are used only for debugging and are discussed in this section.
Undeclared SOAP faults are useful for debugging a service. It is simple to enable these undefined faults to propagate 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"/>
In the Silverlight client code, the fault can be handled inside the Web service operation response callback.
void proxy_CalculateCompleted(object sender, CalculateCompletedEventArgs e)
{
if (e.Error == null)
{
// In case of success
}
else if (e.Error is FaultException<ExceptionDetail>)
{
FaultException<ExceptionDetail> fault = e.Error as FaultException<ExceptionDetail>;
// fault.Detail.Type contains the server exception type.
// fault.Detail.Message contains the server exception message.
// fault.Detail.StackTrace contains the server stack trace.
}
}
Note: |
---|
This WCF configuration should never be deployed in production environments, because it exposes sensitive server-side data such as the exception type and stack trace, which presents a security risk. |
Using Defined Faults to Drive Logic
To use faults in production, you define a fault type in the WCF service, which can contain only information that is safe to expose publicly. For example, a math service could define the following fault type.
public enum Operation
{
Add,
Subtract,
Multiply,
Divide
}
public class ArithmeticFault
{
public Operation Operation { get; set; }
public string Description { get; set; }
}
The operation in which a fault might occur must be marked with the System.ServiceModel.FaultContractAttribute attribute, so that WCF exposes the type in the service metadata.
[OperationContract]
[FaultContract(typeof(ArithmeticFault))]
public int Calculate(Operation op, int a, int b)
{
// ...
}
The System.ServiceModel.FaultException<T> class is used to send the fault on the wire.
ArithmeticFault fault = new ArithmeticFault();
fault.Operation = Operation.Divide;
fault.Description = "Cannot divide by zero.";
throw new FaultException<ArithmeticFault>(fault);
When generating a proxy to this WCF service for Silverlight, Visual Studio 2010 will generate code for the ArithmeticFault class because it was exposed in the service metadata. This enables the class to be used inside the Silverlight client to drive programming logic.
void proxy_CalculateCompleted(object sender, CalculateCompletedEventArgs e)
{
if (e.Error == null)
{
// In case of success
}
else if (e.Error is FaultException<ArithmeticFault>)
{
FaultException<ArithmeticFault> fault = e.Error as FaultException<ArithmeticFault>;
// fault.Detail.Operation as defined on the service
// fault.Detail.Description as defined on the service
}
}