Подтвердить что ты не робот

WCF IErrorHandler возвращает FaultException для SOAP и WebHttpException для конечных точек POX и Json

У меня есть набор веб-сервисов SOAP, которые обертывают исключения, используя IErrorHandler, в частности:

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);
    }
}

Теперь я добавил дополнительные конечные точки для Json и Pox, которые работают нормально, если не возникает исключение. В случае конечной точки Json исключение FaultException возвращается как XML.

Я знаю из других сообщений SO, что в случае REST я бы лучше выбрал исключение WebHttpException:

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

Или переопределение свойств ответного сообщения в ProvideFault, таким образом:

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);

Однако MSDN имеет несколько интересных замечаний о WebHttpException, а именно:

При использовании конечной точки REST WCF (WebHttpBinding и WebHttpBehavior или WebScriptEnablingBehavior) установлен код состояния HTTP в ответе соответственно. Однако WebFaultException может использоваться с не-REST endpoints и ведет себя как регулярное исключение FaultException.

При использовании конечной точки REST WCF формат ответа сериализованного ошибка определяется так же, как и ответ на отказ. Для большего информация о форматировании WCF REST, см. форматирование WCF REST.

Поэтому мне было бы необходимо преобразовать мой текущий метод ProvideFault, чтобы предоставить новое исключение WebHttpException (обертывание любых существующих исключений или исключений FaultExceptions), а затем SOAP все равно будет работать.

Кто-нибудь хотел бы нанести удар по тому, что будет выглядеть (.Net4.0 btw)? Я хочу, чтобы один обработчик ошибок правил для всех!

4b9b3361

Ответ 1

У меня создалось впечатление, что использование webHttpBinding было способом получить функциональность "все-в-одном" JSON/POX/SOAP в отличие от использования отдельных привязок для каждого (т.е. wsHttpBinding, basicHttpBinding и т.д.). Итак, не могли бы вы просто выбросить WebHttpException, а затем дать вам все необходимые сведения об ошибках независимо от технологии?

Ответ 2

В приложении REST, над которым я работаю, я создал новый класс, полученный из WebFaultException<T>, который прикрепляет некоторые дополнительные данные к исключенным сервисам. Вызов метода CreatingMessageFault() в экземпляре производного класса позволяет мне возвращать выбранные данные исключений из метода ProvideFault() обработчика ошибок в качестве ошибки SOAP, позволяя WCF определять правильный формат сообщения.

Я использую webHttpBinding для привязки всех, кроме некоторых сторонних служб.

Изменить: добавлен пример кода

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 );
    }
}

и

/// <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) { }
}

то в вашей службе вы можете передать исключение для передачи данных о недостатках вашему клиенту:

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