Ошибки .NET WCF, генерирующие неправильные значения кода ошибки SOAP 1.1
-
09-06-2019 - |
Вопрос
Я экспериментирую с использованием FaultException и FaultException<T>, чтобы определить наилучший шаблон использования в наших приложениях.Нам необходимо поддерживать WCF, а также потребителей/клиентов служб, отличных от WCF, включая клиентов SOAP 1.1 и SOAP 1.2.
К вашему сведению:использование FaultExceptions с wsHttpBinding приводит к семантике SOAP 1.2, тогда как использование FaultExceptions с BasicHttpBinding приводит к семантике SOAP 1.1.
Я использую следующий код для создания исключения FaultException<FaultDetails>:
throw new FaultException<FaultDetails>(
new FaultDetails("Throwing FaultException<FaultDetails>."),
new FaultReason("Testing fault exceptions."),
FaultCode.CreateSenderFaultCode(new FaultCode("MySubFaultCode"))
);
Класс FaultDetails — это простой тестовый класс, который содержит строковое свойство «Сообщение», как вы можете видеть ниже.
При использовании wsHttpBinding ответ:
<?xml version="1.0" encoding="utf-16"?>
<Fault xmlns="http://www.w3.org/2003/05/soap-envelope">
<Code>
<Value>Sender</Value>
<Subcode>
<Value>MySubFaultCode</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="en-US">Testing fault exceptions.</Text>
</Reason>
<Detail>
<FaultDetails xmlns="http://schemas.datacontract.org/2004/07/ClassLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Message>Throwing FaultException<FaultDetails>.</Message>
</FaultDetails>
</Detail>
Это выглядит правильно в соответствии со спецификациями SOAP 1.2.Основным/корневым «Кодом» является «Отправитель», который имеет «Подкод» «MySubFaultCode».Если потребитель/клиент службы использует WCF, FaultException на стороне клиента также имитирует ту же структуру: errorException.Code.Name имеет значение «Sender», а errorException.Code.SubCode.Name — «MySubFaultCode».
При использовании BasicHttpBinding ответ:
<?xml version="1.0" encoding="utf-16"?>
<s:Fault xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode>s:MySubFaultCode</faultcode>
<faultstring xml:lang="en-US">Testing fault exceptions.</faultstring>
<detail>
<FaultDetails xmlns="http://schemas.datacontract.org/2004/07/ClassLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Message>Throwing FaultException<FaultDetails>.</Message>
</FaultDetails>
</detail>
</s:Fault>
Это выглядит неправильно.Глядя на спецификации SOAP 1.1, я ожидал увидеть, что «код ошибки» будет иметь значение «s:Client.MySubFaultCode», когда я использую FaultCode.CreateSenderFaultCode(new FaultCode("MySubFaultCode")).Также клиент WCF получает неправильную структуру.FaultException.Code.Name имеет значение «MySubFaultCode», а не «Sender», а errorException.Code.SubCode имеет значение null вместо errorException.Code.SubCode.Name, являющегося «MySubFaultCode».Кроме того, errorException.Code.IsSenderFault имеет значение false.
Аналогичная проблема при использовании FaultCode.CreateReceiverFaultCode(new FaultCode("MySubFaultCode")):
- работает как положено для SOAP 1.2
- генерирует «s:MySubFaultCode» вместо «s:Server.MySubFaultCode», а errorException.Code.IsReceiverFault имеет значение false для SOAP 1.1.
Этот элемент также был опубликован кем-то другим на http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=669420&SiteID=1 в 2006 году и никто на него не ответил.Мне очень трудно поверить, что никто еще не сталкивался с этим.
Вот еще у кого-то похожая проблема: http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=3883110&SiteID=1&mode=1
Ошибка Microsoft Connect: https://connect.microsoft.com/wcf/feedback/ViewFeedback.aspx?FeedbackID=367963
Описание того, как должны работать неисправности: http://blogs.msdn.com/drnick/archive/2006/12/19/creating-faults-part-3.aspx
Я делаю что-то не так или это действительно ошибка в WCF?
Решение
Это мой текущий обходной путь:
/// <summary>
/// Replacement for the static methods on FaultCode to generate Sender and Receiver fault codes due
/// to what seems like bugs in the implementation for basicHttpBinding (SOAP 1.1). wsHttpBinding
/// (SOAP 1.2) seems to work just fine.
///
/// The subCode parameter for FaultCode.CreateReceiverFaultCode and FaultCode.CreateSenderFaultCode
/// seem to take over the main 'faultcode' value in the SOAP 1.1 response, whereas in SOAP 1.2 the
/// subCode is correctly put under the 'Code->SubCode->Value' value in the XML response.
///
/// This workaround is to create the FaultCode with Sender/Receiver (SOAP 1.2 terms, but gets
/// translated by WCF depending on the binding) and an agnostic namespace found by using reflector
/// on the FaultCode class. When that NS is passed in WCF seems to be able to generate the proper
/// response with SOAP 1.1 (Client/Server) and SOAP 1.2 (Sender/Receiver) fault codes automatically.
///
/// This means that it is not possible to create a FaultCode that works in both bindings with
/// subcodes.
/// </summary>
/// <remarks>
/// See http://stackoverflow.com/questions/65008/net-wcf-faults-generating-incorrect-soap-11-faultcode-values
/// for more details.
/// </remarks>
public static class FaultCodeFactory
{
private const string _ns = "http://schemas.microsoft.com/ws/2005/05/envelope/none";
/// <summary>
/// Creates a sender fault code.
/// </summary>
/// <returns>A FaultCode object.</returns>
/// <remarks>Does not support subcodes due to a WCF bug.</remarks>
public static FaultCode CreateSenderFaultCode()
{
return new FaultCode("Sender", _ns);
}
/// <summary>
/// Creates a receiver fault code.
/// </summary>
/// <returns>A FaultCode object.</returns>
/// <remarks>Does not support subcodes due to a WCF bug.</remarks>
public static FaultCode CreateReceiverFaultCode()
{
return new FaultCode("Receiver", _ns);
}
}
К сожалению, я не вижу способа использовать подкоды, не нарушая работу клиентов SOAP 1.1 или 1.2.
Если вы используете синтаксис Code.SubCode, вы можете создавать значения кодов ошибок, совместимые с SOAP 1.1, но это нарушает SOAP 1.2.
Если вы используете правильную поддержку подкода в .NET (либо через статические методы FaultCode, либо через одну из перегрузок), это нарушает SOAP 1.1, но работает в SOAP 1.2.
Другие советы
Ответ от Microsoft:
Как обсуждалось в http://msdn.microsoft.com/en-us/library/ms789039.aspx, в спецификации Soap 1.1 описаны два метода для получения пользовательских кодов неисправностей:
(1) Используя обозначение «точка», как вы описываете
(2) Определение совершенно новых кодов неисправностей
К сожалению, следует избегать обозначения «точка», поскольку его использование не рекомендуется в спецификации базового профиля WS-I.По сути, это означает, что не существует реального эквивалента подкода ошибки Soap 1.2 при использовании Soap 1.1.
Таким образом, при генерации ошибок вам необходимо учитывать MessageVersion, определенную в привязке, и соответствующим образом генерировать коды ошибок.
Поскольку «отправитель» и «получатель» не являются допустимыми кодами ошибок для Soap 1.1, а реального эквивалента субкода ошибки не существует, не следует использовать методы CreateSenderFaultCode и CreateReceiverFaultCode при создании пользовательских кодов ошибок для Soap 1.1.
Вместо этого вам нужно будет определить свой собственный код неисправности, используя собственное пространство имен и имя:
FaultCode customFaultCode = новый FaultCode(localName, errorNamespace);