As part of the protocol definition, SOAP provides the mechanism to notify a caller about the failures. SOAP Fault messages are specially designed for this purpose and are structured in extensible fashion. This means that as for callee, it may feel absolutely free to express its own failures as custom defined payloads and use SOAP Fault messages as transport for its transmission on the wire. Таким образом, совершенно естественным для WebServic'a будет определить, как должны выглядеть его exception. Казалось бы не менее естественным ожидать от инфраструктуры WebSerices соответствующей поддержки SOAP Faults : например, прекрасно смотрится WSDL, где при описании Fault methods используются XSD types, включенные в WSDL schemas section. Имея такой WSDL, для parser'a не будет проблемы создать соответствующие клиентский код, в котором все exceptions представлены классами, поддающимися анализу из catch-block. WSDL 1.1 standard (section 3.6) предполагает подобные конструкции, причем wsdl:fault определяется для каждого exposed method в отдельности с единственным ограничением на single part.
Так дело выглядит в теории, а когда она касается .NET (в частности ASMX) практики, оказывается, что C# не позволяет синтаксически выразить тот факт, что метод в ходе своего исполнения может выдавать exception определенного типа. Именно в этом месте, когда WSDL строится из кода C#, такая возможность была бы весьма полезной. (ср. с throws in C++ и java). Ту же идею в C# можно выразить, используя attributes, например :
[
WebMethod]
[WebMethodExceptions(typeof(MyException))]
public string ProcessRequest(RequestItem item)
{
}
но этого почему-то не сделано, а добавлять собственный WebMethodExceptionsAttribute class совершенно бессмысленно, поскольку .NET parser (wsdl.exe) его никак узнать не может.
С другой стороны, .NET предлагает class - SoapException - который может облегчить работу с SOAP Faults. Здесь рассматривается пример, кот. демонстрирует, как вручную построить внутренний XML для SOAP Fault, отправить его клиенту, получить (catch) его на клиенте, и наконец, провести процедуру по разборке полученного SOAP Fault, чтобы добраться до переданного XML. Понятно, что последний шаг, ради которого, собственно, все это и создавалось, проводится, предполагая, что "телепатически" клиенту известна не только структура полученного XML, но и его namespace. Завораживающе!
Но и это не всё! Даже если каким-то образом WSDL будет построен с wsdl:fault элементами, BizTalk (кроме R2) все-равно затруднится работать с пойманными exceptions ни как с объектами, ни как с сообщениями, построенными по schemas. В R2 ситуация значительно лучше, во всяком случае, переданный <details> элемент не проглатывается, но schemas для exceptions по-прежнему не регенрируются. Поэтому самое лучшее, что можно сделать с полученным из <details> XML (известным в XLANG/s как exSOAP.Details.OuterXML) - сохранить его где-нибудь в базе, как, например делает ESB Guidance, для последующего анализа.
С exception, пойманным в оркестрации, разобрались. А что делать с теми, которые генерируются из send port'a? Прежде всего, без "Failed message routing", exception в WebService приводит к подвешиванию (suspend, resumable) соответствующего pipeline, что не имеет никакого смысла для случаев, когда порт вызывался из оркестрации (В самом деле, оркестрация уже давно завершилась). В BizTalk 2004 единственной возможностью справиться с такими подвешиваниями был NACK. NACK это просьба к BizTalk генерировать специальный тип сообщений вместо того, чтобы подвешивать исполняющийся сервис. NACK можно запросить только из оркестрации; при этом создается дополнительный instance subscription, с помощью которого удается "подавить" exception из send port, но, во-первых, такие уведомления требуют специальной обработки (и, по-моему, не применимы к WebServices), а во-вторых, они возвращаются только в вызвавшую оркестрацию, не давая возможности обработать такие exceptions общим образом.
Видимо, в связи с этим в BizTalk 2006 и BizTalk 2006 R2 предлагается другая возможность - Failed message routing. Эта опция может быть включена как для receice, так и для send портов. При этом engine реагирует на нее так : генерируется специальное сообщение типа ErrorReport, в контекстные properties которого записываются специальные значения для следующих properties ( within 'http://schemas.microsoft.com/BizTalk/2005/error-report' namespace):
ErrorReport.ErrorType (always = "FailedMessage" )
ErrorReport.FailureCode
ErrorReport.FailureCategory
ErrorrReport.Description
ErrorReport.SendPortName
ErrorReport.RoutingFailureReportID
ErrorReport.MessageType
ErrorReport.InboundTransportLocation
ErrorReport.OutboundTransportLocation
ErrorReport.ProcessingServer (only for R2)
Все эти properties являются promoted, т.е. на них можно подписаться независимым подписчиком, тем самым получая желаемую обработку всех исключений из исходного порта. Не надо путать эти сообщения с NACK, т.е. никто не сказал, что NACK не может содержать ErrorReport properties, но сообщения от оркестрации, как правило, не будут содержать ErrorReport properties. Это "как правило" означает, что мы не будем строить из оркестрации вручную, как, например, предложено
здесь.
Но в любом случае у нас появляется возможность сосредоточить в одном handler'e как обработку exceptions из оркестрации, так и exceptions из портов. Для оркестраций, сообщение должно включать пойманный exception и какой-нибудь custom promoted property, для портов - подписка осуществляется по ErrorReport.FailureCode is exists или ErrorReport.ErrorType = "FailedMessage"
Особенно интерсно значение этого FailureCode. Хотелось бы ожидать, что это магическое число каким-то образом будет связано с номером ошибки, которую присвоит exception адаптер, но увы... Единственный документированный код для FailureCode - 0xc0c01675 - это SOAP Client timeout. Остальные номера, по всей видимости, все-таки как-то зависят от адаптера, но пока мне неизвестно, как именно.
P.S. Над очень похожим вопросом - Robust Error Handling - всего год тому назад работал Matt Meleski, а параллельно с ним и группа P&P, включившая Exception Handling Framework в ESB Guidance.