开发者

WCF IErrorHandler to return FaultException to SOAP and WebHttpException to POX and Json endpoints

开发者 https://www.devze.com 2023-03-31 04:33 出处:网络
I have a set of SOAP webservices that are wrapping exceptions using IErrorHandler, specifically: public sealed class ErrorHandler : IErrorHandler

I have a set of SOAP webservices that are wrapping exceptions using IErrorHandler, specifically:

public sealed class ErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        // don't wrap existing fault exceptions
        if ((error is FaultException)) return;

        // our basic service fault
        var businessFault = new BusinessFault { FaultMessage = error.Message, FaultReference = "Internal" };

        // Resource based faultReason
        var faultReason = new FaultReason(Properties.Resources.BusinessFaultReason);
        var faultcode = FaultCodeFactory.CreateVersionAwareSenderFaultCode(InternalFaultCodes.BusinessFailure.ToString(), Service.Namespace);

        var faultException = new FaultException<BusinessFault>(
            businessFault,
            faultReason,
            faultcode);

        // Create message fault
        var messageFault = faultException.CreateMessageFault();

        // Create message using Message Factory method
        fault = Message.CreateMessage(version, messageFault, faultException.Action);
    }
}

I have now added extra endpoints for Json and Pox which work fine, unless 开发者_StackOverflow中文版an exception occurs. In the case of the Json endpoint the FaultException is returned as XML.

I am aware from other SO posts that in the case of REST I would be better throwing a WebHttpException:

throw new WebFaultException<BusinessFault>(detail, HttpStatusCode.BadRequest);

Or overriding the response message properties in ProvideFault, thus:

var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

var rmp = new HttpResponseMessageProperty
{
    StatusCode = System.Net.HttpStatusCode.BadRequest,
    StatusDescription = "See fault object for more information."
};
fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);

However, MSDN has some interesting remarks about WebHttpException namely:

When using a WCF REST endpoint (WebHttpBinding and WebHttpBehavior or WebScriptEnablingBehavior) the HTTP status code on the response is set accordingly. However, WebFaultException can be used with non-REST endpoints and behaves like a regular FaultException.

When using a WCF REST endpoint, the response format of the serialized fault is determined in the same way as a non-fault response. For more information about WCF REST formatting, see WCF REST Formatting.

It would suggest therefore that I need to convert my current ProvideFault method to provide a new WebHttpException (wrapping any existing Exceptions or FaultExceptions) and then SOAP would still work as well.

Would anyone like to take a stab at what that would look like (.Net4.0 btw)? I want one error handler to rule them all!


I was under the impression that using webHttpBinding was a way to get the "all-in-one" functionality of JSON/POX/SOAP as opposed to using separate bindings for each (i.e. wsHttpBinding, basicHttpBinding etc.). So wouldn't you be able to just throw the WebHttpException and then have that give you all the error details you needed regardless of the technology?


In a REST application I'm working on, I created a new class derived from WebFaultException<T> that attaches some additional data to caught service exceptions. Calling the CreatingMessageFault() method on the instance of the derived class let me return my selected exception data from the ProvideFault() method of the error handler as the SOAP fault, letting WCF determine the correct message format.

I am using webHttpBinding to bind all but some third-party services.

Edit: Added code example

public class ErrorHandler : IErrorHandler, IServiceBehavior
{       
    public virtual void ProvideFault( Exception error, MessageVersion version, ref Message fault )
    {
        // Include next level of detail in message, if any.
        MyFaultException myFaultException =
            ((error is MyFaultException) &&
                ((MyFaultException)error).Detail != null)
            ? new MyFaultException(error.Message + " - " +
                    ((MyFaultException)error).Detail.Message, error)
            : new MyFaultException( error.Message, error );
        MessageFault messageFault = myFaultException.CreateMessageFault();
        fault = Message.CreateMessage( version, messageFault, myFaultException.Action );
    }
}

and

/// <summary>
/// Class used to return exception data from my WCF services.
/// </summary>
/// <remarks>
/// This class is used by a web service to pass exception data back and a
/// data object to the client. This class inherits WebFaultException, which
/// is handled specially by the WCF WebServiceHost2 service class and
/// generates a WebException on the client.
/// </remarks>
public class MyFaultException : WebFaultException<BusinessFault>
{
public class MyFaultException : WebFaultException<BusinessFault>
{
    public MyFaultException(string message)
        : this(HttpStatusCode.BadRequest, message) { }

    public MyFaultException(HttpStatusCode statusCode, string message)
        : base(new BusinessFault(message), statusCode) { }
}

then in your service, you can throw the exception to pass fault data to your client:

        try
        {
            // Successful operation proceeds normally.
        }
        catch (ApplicationException e)
        {
            // Failure generates MyFaultException.
            throw new MyFaultException("Operation failed with " + e.Message);
        }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号