wcfierrorhandleriservicebehaviors

WCF Custom Error Handler to conditionally return xml or json?


The open311 protocol requires support for endpoints in the following format:

The first endpoint accepts xml requests, the second json requests.

The protocol further specifies errors should be returned in the request format (xml or json).

When implementing a ServiceBehaviorErrorHandler I found it impossible to determine the request format (xml or json) within the ProvideFault method. All examples either return json or xml.

How can I determine the request format so I can return the error message in that same format? My implementation:

    /// <summary>
/// From http://www.codeproject.com/Articles/43621/Extending-WCF-Part-I
/// </summary>
public class ExtendedServiceErrorHandler : IErrorHandler, IServiceBehavior
{
    #region IErrorHandler Members

    bool IErrorHandler.HandleError( Exception error )
    {
        return ( error is Open311Exception );
    }

    /*
     * TODO: HTTP error codes are required, but the code in the response body shouldn't necessarily match the HTTP error code, 
     * so that more specific and unique error code identifiers can be used. 
     * The HTTP error codes should always be 404 for resources that don't exist, 
     * 403 for errors because of wrong or missing api_key and basically 
     * 400 for any other error where the request can not be fulfilled as expected. 
     * Multiple errors codes and descriptions can be returned in the body (the response is an array).
     */
    void IErrorHandler.ProvideFault( Exception error, MessageVersion version, ref Message fault )
    {
        var ajaxErrors = new AjaxErrors();
        var open311Error = error as Open311Exception;
        if( null != open311Error )
        {
            ajaxErrors.Add( new AjaxError()
                {
                    Code = open311Error.Code,
                    Message = open311Error.Message
                } );
        }
        else
        {
            ajaxErrors.Add( new AjaxError()
                {
                    Code = 400,
                    Message = error.Message
                } );
        }
        var contentType = "application/json"; // TODO: how do we know?
        // WebOperationContext.Current.IncomingRequest.ContentType doesn't work
        WebContentFormat webContentFormat;
        switch( contentType )
        {
            case "application/json":
                fault = Message.CreateMessage( version, string.Empty, ajaxErrors, new DataContractJsonSerializer( ajaxErrors.GetType() ) );
                webContentFormat = WebContentFormat.Json;
                break;
            case "application/xml":
                fault = Message.CreateMessage( version, string.Empty, ajaxErrors, new DataContractSerializer( ajaxErrors.GetType() ) );
                webContentFormat = WebContentFormat.Xml;
                break;
            default:
                fault = Message.CreateMessage( version, string.Empty, ajaxErrors, new DataContractSerializer( ajaxErrors.GetType() ) );
                webContentFormat = WebContentFormat.Raw;
                break;
        }
        var wbf = new WebBodyFormatMessageProperty( webContentFormat );
        fault.Properties.Add( WebBodyFormatMessageProperty.Name, wbf );
        WebOperationContext.Current.OutgoingResponse.ContentType = contentType;
        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK; //  HttpStatusCode.BadRequest;
    }

    #endregion

    #region IServiceBehavior Members

    void IServiceBehavior.AddBindingParameters( ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters )
    {
        // nothing to do?
    }

    void IServiceBehavior.ApplyDispatchBehavior( ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase )
    {
        var errorHandler = new ExtendedServiceErrorHandler();

        foreach( ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers )
        {
            channelDispatcher.ErrorHandlers.Add( errorHandler );
        }
    }

    void IServiceBehavior.Validate( ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase )
    {
        // nothing to do?
    }

    #endregion
}

Solution

  • This is what you’re looking for I believe: http://msdn.microsoft.com/en-us/library/vstudio/ee513227(v=vs.100).aspx

    Windows Communication Foundation (WCF) Web HTTP error handling enables you to return errors from WCF Web HTTP services that specify an HTTP status code and return error details using the same format as the operation (for example, XML or JSON).