Статьи

Шаблоны содержания сервисных ошибок (часть II)

В предыдущей статье из серии статей «Шаблоны содержимого ошибок служб» мы установили некоторые распространенные проблемы и проблемы при работе со стандартными исключениями в средах обмена сообщениями (в частности, с веб-службами и сообщениями SOAP). В этой второй серии статей представлен набор предлагаемых решений и методик для решения этих проблем. В частности, мы представляем шаблон резидентной ошибки, шаблон объединенного формата и шаблон смешанного использования. Каждый из этих шаблонов решает различные проблемы и сценарии исключений. Применяя проверенные методы проектирования, подобные тем, которые описаны в этих трех шаблонах, мы можем полностью контролировать условия исключений. Это приводит к гораздо более сложному и гибкому проектированию корпоративных решений. Кроме того, как шаблоны,эти методы проектирования повторяются в том смысле, что они могут применяться к различным архитектурам решений, а также к средам и сценариям обмена сообщениями.Эта статья написана
Рональдом Мерфи и опубликована в журнале SOA в августе 2009 года.

Шаблон ошибок резидентных

ответов Шаблон ошибок резидентных ответов работает вокруг ограничений использования ошибок, предоставляя возможность моделирования всей информации об ошибках как части стандартного ответа операции. Сообщения. Несмотря на то, что это с некоторой глубиной отправляет моделирование ошибок с точки зрения WSDL, они могут устранить многие из ограничений, которые мы выявили при моделировании на основе ошибок

1. Для данной веб-службы или группы служб, которые вы разрабатываете, вы можете определить больший «наименьший общий знаменатель». Вы можете стандартизировать более богатую структуру ошибок, чтобы использовать ее для всех ваших ошибок. Вы все еще можете иногда расширять эту структуру с помощью дополнительной информации, используемой некоторыми службами или операциями.
2. Имея структуру ошибок в ваших ответах, вы можете смешивать обычные данные с выводом ответа. Это учитывает предупреждения.
3. Ваша стандартная структура ошибок может предусматривать несколько ошибок. Рано или поздно вы, вероятно, захотите этого в более крупном веб-сервисе. Например, сложные операции могут легко привести к нескольким ошибкам.

Одной из функций, которую вы можете отказаться при использовании резидентных ошибок, является автоматическое повторное удаление ошибок как исключений на стороне клиента. Многие инструментарии SOAP предоставляют эту функцию, а в некоторых клиентских прикладных средах это может быть удобным способом прекратить обработку. Но жертва невелика в обмен на гибкость, которую вы можете получить при обработке предупреждений и множественных ошибок.

Вот пример шаблона WSDL для резидентных ошибок. Сначала мы объявляем базовый тип ответа для стандартного хранения информации об ошибках во всех ответах для всех операций нашего сервиса. (Это может также содержать любую другую стандартизированную информацию для ответов, такую ​​как отметка времени, когда был сгенерирован ответ, версия веб-службы или операции и т. Д.)   

<xs:complexType name="BaseServiceResponseType">
<xs:sequence>
<xs:element name="Status" type="tns:ServiceStatusType"/>
<xs:element name="ErrorList" type="tns:ServiceErrorType"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

 

Далее мы объявляем ответ для FindItem специально, исходя из базового типа ответа:

<xs:element name="FindItemResponse">
<xs:complexType>
<xs:complexContent>
<!-- Derived from base response type -->
<xs:extension base="tns:BaseServiceResponseType">
<xs:sequence>
<!-- Result data -->
<xs:element name="Item" type="tns:ItemType"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>

 

Обратите внимание, что этот ответ теперь содержит данные об ошибках и результатах. В этом примере данные об ошибках извлекаются из базового класса ответа, а данные результата объявляются специально для этого типа ответа.

Следующее объявление WSDL ссылается на элемент FindItemResponse как ответное сообщение. В отличие от примера сбоя, он не ссылается на сообщение об ошибке:

<wsdl:operation name="FindItem">
<wsdl:input ... />
<wsdl:output message="tns:FindItemResponse"/>
</wsdl:operation>

 

Влияние резидентного стиля ошибок ответа на клиентское программное обеспечение может варьироваться в зависимости от того, как построены клиенты. Сгенерированные клиенты, такие как некоторые инструментальные средства тестирования, основанные на вашем WSDL, могут вообще не понимать вашу ошибку без настройки — и такая настройка невозможна для всех доступных инструментов тестирования.

Однако на практике большинство клиентов будут приложениями, специально созданными для вашей службы, вами или вашими клиентами. Клиенты обычно должны понимать большую часть формата ответа вашей службы, чтобы использовать ее в первую очередь. Если вы предоставите в своем ответе стандартизированную структуру ошибок «наименьший общий знаменатель», вы можете направить клиентов к централизованной функции программного обеспечения, которая будет декодировать и отображать этот формат ошибок как минимум.

Резидентные ошибки являются разумным подходом, когда стандартные инструменты и стандартизированные клиенты не представляют препятствия. На самом деле, чем менее стандартизирована ваша экосистема веб-служб, тем больше вероятность того, что вы захотите использовать ошибки, возникающие в ответе. Например, использование службы в стиле REST рассматривает служебные сообщения как пустую полезную нагрузку, обмен которой осуществляется через HTTP — нет конверта SOAP, в котором содержится ваша ошибка. Вам нужно будет либо предоставить простую полезную нагрузку сообщения, похожего на ошибку, либо просто стандартизировать один тип сообщения, объединяющий информацию об ответах и ​​ошибках.

Какая информация должна поступать в ответ резидентских ошибок? Поскольку этот шаблон дает вам возможность моделировать предупреждения наряду с критическими (завершающими) ошибками, вы можете захотеть создать условия для их различения. Клиенты могут захотеть обрабатывать предупреждения более поверхностно, регистрируя их или отображая в ненавязчивой информационной области. Напротив, ошибки обычно должны завершать любые невыполненные действия конечного пользователя на стороне клиента. В ваших ошибках может быть указан следующий перечислимый тип, чтобы вы могли различать эти два вида ошибок:

<xs:simpleType name="SeverityType">
<xs:restriction base="xs:token">
<xs:enumeration value="Critical"/>
<xs:enumeration value="Warning"/>
</xs:restriction>
</xs:simpleType>

 

Мы также предлагаем вам предоставить не только строковое сообщение об ошибке в вашей ошибке, но также уникальный код (предпочтительно целое число) для обозначения каждого конкретного экземпляра ошибки. Клиенты могут захотеть предпринять специальные действия для определенных ошибок, например, если выясняется, что ошибка часто приводит к ошибке, они могут ввести обходной код или специальную обработку (дополнительное ведение журнала и т. Д.). В большинстве случаев кодировщики приложений не будут сталкиваться с проблемой отслеживания всех ваших кодов ошибок, но этот подход действительно может помочь в крайнем случае. Это также может помочь процессу технической поддержки, если вы сможете точно определить конкретную ошибку. Следующее определение объединяет наши рекомендации для минимальной структуры ошибок:

<xs:complexType name="ServiceErrorType">
<xs:sequence>
<xs:element name="ErrorMessageString" type="xs:string"/>
<xs:element name="ErrorCode" type="xs:int"/>
<xs:element name="Severity" type="tns:SeverityType"
minOccurs="0"/>
</xs:sequence>
</xs:complexType>

 

Что касается самих ответов, так как шаблон ошибки резидентного ответа требует, чтобы эти структуры содержали такое разнообразие данных, вы захотите, чтобы клиентские приложения могли легко различать, что они получают. Подумайте о наличии перечисленного элемента статуса во всех ваших ответах. Это может иметь, как минимум, значения «успех» и «неудача». Также полезно указать «предупреждение», что означает «успешное выполнение с предупреждениями». Вот пример определения этого перечисления:

<xs:simpleType name="ServiceStatusType">
<xs:restriction base="xs:token">
<xs:enumeration value="Success"/>
<xs:enumeration value="Failure"/>
<xs:enumeration value="Warning"/>
</xs:restriction>
</xs:simpleType>

 

Этот перечислимый тип представлен в базовом типе ответа, который мы впервые показали выше в этом разделе.

Последнее слово предостережения: если вы выбираете подход с учетом резидентных ошибок, вы обычно найдете случаи ошибок программирования сервера, которые по-прежнему приводят к сбоям SOAP: исключительные ситуации с ошибками могут быть перехвачены вашим механизмом SOAP и автоматически сообщаться о сбоях, а также о любых проблемах в Сам двигатель наверняка будет выброшен как неисправность по умолчанию. Многие решения могут быть настроены на определенном уровне, чтобы перенаправить поток обратно на уровень отклика, но вам придется исследовать эту область, если вы отклоняетесь от ошибок.

Шаблон объединенного формата

Один гипотетически интересный шаблон содержания ошибки состоит в том, чтобы просто объединить структуры данных ответа и ошибки и использовать одну и ту же структуру для обоих. Нам неизвестен какой-либо стандарт веб-службы, запрещающий такое использование, хотя, похоже, он отклоняется от духа концепции ошибок, и, вероятно, некоторые наборы инструментов SOAP могут нарушить этот подход.

Чтобы использовать объединенные форматы ответа / отказа, для каждой операции ваш WSDL объявлял бы сообщения «output» и «fault» одного и того же типа схемы XML. Этот общий тип является структурой, содержащей как обычную информацию об ответе, так и информацию об ошибке, поэтому он должен использовать определения, аналогичные разделу с резидентными ошибками, такими как
BaseServiceResponseType и
FindItemResponse, Тип может варьироваться от операции к операции, но для любой данной операции он будет служить как структурой отказа, так и структурой вывода. Ключевой поворот в объединенном подходе является дублированной ссылкой на сообщение отклика в
обоих WSDL — : Выходная и WSDL: неисправности определение, такие как:

<wsdl:operation name="FindItem">
<wsdl:input ... />
<wsdl:output message="tns:FindItemResponse"/>
<wsdl:fault message="tns:FindItemResponse"/>
</wsdl:operation>

 

Однако при ближайшем рассмотрении у этого подхода есть проблемы. Теоретически он объединяет обработку как для сервера, так и для клиента, обеспечивая равномерную обработку данных. Но помните, что реализации SOAP должны возвращать сообщения об ошибках как ошибки HTTP 500, и что большинство серверов приложений также возвращают ошибки HTTP 500 в других ситуациях. Конкретный клиент не обязательно обладает интеллектом для отделения ошибок от ошибок нехватки памяти или других проблем, не связанных с SOAP.

Если сервер нарушает правило WSI-BP и возвращает ошибки, используя обычную ошибку HTTP 200 (как это делают некоторые реализации), тогда у клиентов возникает другая проблема — они не могут реально отличить ошибки от нормального вывода. Клиенты должны будут искать наличие структуры ошибки, флага или какого-либо другого нестандартного указания на наличие ошибки. Фактически, с точки зрения клиента, объединенная структура «ошибка / ответ», возвращаемая с HTTP 200, является в основном подходом резидентной ошибки! Клиент не может сказать разницу.

В целом, стратегия объединения потоков ответа и информации о сбоях предъявляет слишком много требований к клиентской стороне, чтобы она действительно была лучшим выбором. Если вам нужно поддерживать множество клиентов и не контролировать их дизайн и реализацию, этот подход, вероятно, не самый лучший.

Модель смешанного использования

Многие крупномасштабные реализации могут оказаться в результате использования некоторой формы смешанного подхода, сочетания моделирования ошибок и резидентных ошибок. Смешение ошибок и резидентных ошибок может выглядеть как нежелательная гостевая стратегия, если разработчик моделей веб-служб запускается с использованием ошибок, а затем находит вариант использования, который требует предупреждений или полезных нагрузок с несколькими ошибками. Но предпочтительно, чтобы смешанное использование ошибок и ответов было запланировано в схемах как осознанный выбор проекта, где это уместно.

Ранее мы упоминали, что ошибки могут быть либо объявлены в WSDL, либо оставлены для генерации, если возникнут непредвиденные проблемы с обработкой. Это разделение также может применяться к сочетанию резидентных ошибок и неисправностей. Смоделируйте ожидаемую информацию об ошибках в качестве резидентных ошибок ответа и оставьте ошибки как «сквозные» меры для покрытия непредвиденных сбоев обработки. Смотрите рисунок 4.

 Рисунок 4: Обработка непредвиденных исключений с ошибками SOAP.

На самом деле, «сквозной» подход к использованию неисправностей может быть путем наименьшего сопротивления. Напомним, что движкам SOAP нравится обрабатывать свои состояния ошибок как сбои, как мы обсуждали в разделе резидентных ошибок ответа. Вы можете определить сферу «ожидаемых» ошибок как свою собственную бизнес-логику, а область «непредвиденных» ошибок — как инфраструктуру SOAP и, возможно, любое поддерживающее программное обеспечение (уровень данных и т. Д.) Под своей бизнес-логикой. Все ошибки, которые вы непосредственно контролируете, становятся резидентными ошибками, а все остальное становится ошибкой SOAP. Лучшее из обоих миров! — Почти. Одной из распространенных областей обработки, приводящих к сбоям в механизмах SOAP, является анализ XML-содержимого. Любые синтаксические ошибки, встречающиеся во время синтаксического анализа, могут быть зарегистрированы как ошибки,так что клиентам, возможно, придется обрабатывать некоторые из своих ошибок форматирования как ошибки, а другие (бизнес-ограничения) ошибки как ошибки резидента. В любом случае, существует разница в этих двух типах ошибок данных, поскольку синтаксические ошибки обычно указывают на неправильно закодированные клиентские приложения. Таким образом, в конечном счете, трудно найти «ошибку» с подходом!

Формат двойной ошибки в подходе смешанного использования действительно является его основным недостатком, рассматриваемым с точки зрения клиентов. Этим клиентским приложениям придется иметь дело с различными ошибочными состояниями — в первую очередь, с ошибками, связанными с транспортом и сервером приложений, а затем из-за выбора смешанной стратегии, как сбоев, так и ошибок резидентных ответов. Увы, это действительно только небольшое дополнительное осложнение в и без того сложном мире обработки ошибок.

У нас есть один совет, чтобы сгладить путь смешанной обработки ошибок для клиентов: если возможно, стандартизируйте свои структуры ошибок в обеих формах ошибок. Повторяющийся элемент ошибки, по крайней мере с серьезностью, кодом ошибки и строкой сообщения, обычно является хорошей структурой верхнего уровня. Для ваших ответов вы можете определить базовый тип ответа, который также всегда содержит один и тот же повторяющийся элемент ошибки (вместе с полем общего состояния, как мы обсуждали ранее). Для любых неисправностей, которые вы моделируете сами, вы можете включить этот же повторяющийся элемент в свои подробные сведения о неисправностях. Это позволяет клиентам анализировать одну и ту же структуру при максимально возможном количестве ошибок.

В качестве окончательного утешения для клиентов большинство ошибок транспорта, сервера приложений и ошибок будут обнаруживаться на уровне HTTP через коды состояния HTTP 400 и 500. Клиентская обработка ошибок может сообщать о кодах состояния, а также некоторый текст из ответа HTTP — этого обычно будет достаточно для диагностики проблемы.

Заключение:

краткий обзор паттернов Вот краткое изложение различных подходов, которые мы обсудили:

В простых ситуациях с ошибками или в веб-службах в стиле RPC исключения и сбои могут быть хорошим решением для ваших отрицательных вариантов использования. Более того, механизмы отчетности на основе сбоев имеют то преимущество, что они формализованы в SOAP и WSDL, а исключения и сбои хорошо интегрируются друг с другом в большинстве основных сред программирования SOAP. Даже ваши стандартные инструменты SOAP приведут вас по пути, ориентированному на отказ. Так что, если у вас нет причудливой обработки ошибок и вы никогда не ожидаете, ошибки могут быть простым и практичным выбором.
В более сложных веб-сервисах, где мы хотим предоставить смеси ответов и ошибок, рассмотрите возможность использования резидентного шаблона ошибок. Сервисы в стиле REST подталкивают нас к этому выбору почти по определению, поскольку ошибки являются функцией SOAP. Основными недостатками подхода с учетом резидентных ошибок являются отсутствие стандартизированного метода спецификации в WSDL и тот факт, что некоторые инструменты, такие как инструменты тестирования, могут не обнаружить ваши ошибки без настройки.
Хотя теоретически вы можете свести сообщения об ошибках и сообщения об ошибках вместе в один тип сообщений, вы должны быть осторожны, поскольку это идет вразрез с традиционным использованием и может привести к путанице в некоторых клиентских приложениях.
Если вы не возражаете против того, чтобы клиенты подвергались немного большему разнообразию ошибок, чем они могли бы видеть в других случаях, вы можете возвращать как ошибки, так и резидентные ошибки ответа, позволяя вашим конкретным вариантам использования принимать решения. Обязательно документируйте обстоятельства, приводящие к каждому виду ошибок, чтобы клиентские приложения могли разумно реагировать.

Резюме для примера ошибки:

<!-- Request information for FindItem operation -->
<xs:element name="FindItemRequest">
<!-- (keywords to search for, etc.) -->
</xs:element>

<!-- Response information for FindItem operation - has result data -->
<xs:element name="FindItemResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="Item" type="tns:ItemType"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<!-- ItemNotFound fault, thrown by FindItem operation -->
<xs:element name="ItemNotFound">
<xs:complexType>
<xs:complexContent>
<xs:extension base="tns:BaseServiceFaultType">
<xs:sequence>
<!-- specific fault data here -->
</xs:sequence
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>

<!-- Base fault to be used across a series of faults -->
<xs:complexType name="BaseServiceFaultType">
<xs:sequence>
<xs:element name="ErrorDetails"
type="tns:ServiceErrorType"/>
</xs:sequence>
</xs:complexType>

<!-- Message definitions for FindItem request, response, and fault -->
<wsdl:message name="FindItemRequest">
<wsdl:part name="FindItemRequest" element="tns:FindItemRequest"/>
</wsdl:message>
<wsdl:message name="FindItemResponse">
<wsdl:part name="FindItemResponse"
element="tns:FindItemResponse"/>
</wsdl:message>
<wsdl:message name="ItemNotFound">
<wsdl:part name="ItemNotFound" element="tns:ItemNotFound"/>
</wsdl:message>

<!-- Operation definition for FindItem operation -->
<wsdl:operation name="FindItem">
<wsdl:input message="tns:FindItemRequest"/>
<wsdl:output message="tns:FindItemResponse"/>
<wsdl:fault message="tns:ItemNotFound"/>
</wsdl:operation>

 

Сводка для примера ошибки резидента:

 

<!-- Base response type to be used across all operations -->
<xs:complexType name="BaseServiceResponseType" abstract="true">
<xs:sequence>
<xs:element name="Status" type="tns:ServiceStatusType"/>
<xs:element name="ErrorList" type="tns:ServiceErrorType"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<!-- Response information for FindItem operation -->
<xs:element name="FindItemResponse">
<xs:complexType>
<xs:complexContent>
<!-- Derived from base response type -->
<xs:extension base="tns:BaseServiceResponseType">
<xs:sequence>
<!-- Result data -->
<xs:element name="Item" type="tns:ItemType"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>

<!-- Common enumeration to show result status of any operation -->
<xs:simpleType name="ServiceStatusType">
<xs:restriction base="xs:token">
<!-- The operation succeeded with no errors or warnings -->
<xs:enumeration value="Success"/>
<!-- The operation failed; processing must stop -->
<xs:enumeration value="Failure"/>
<!-- The operation succeeded with warnings present -->
<xs:enumeration value="Warning"/>
</xs:restriction>
</xs:simpleType>

<!-- Operation definition, with no fault declaration -->
<wsdl:operation name="FindItem">
<wsdl:input message="tns:FindItemRequest"/>
<wsdl:output message="tns:FindItemResponse"/>
</wsdl:operation>

<!-- Common type to carry error details -->
<xs:complexType name="ServiceErrorType">
<xs:sequence>
<xs:element name="ErrorMessageString" type="xs:string"/>
<xs:element name="ErrorCode" type="xs:int"/>
<xs:element name="Severity" type="tns:SeverityType"
minOccurs="0"/>
</xs:sequence>
</xs:complexType>

<!-- Common enumeration to distinguish errors from warnings -->
<xs:simpleType name="SeverityType">
<xs:restriction base="xs:token">
<!-- Critical: requestor must terminate execution -->
<xs:enumeration value="Critical"/>
<!-- Warning: processing can continue -->
<xs:enumeration value="Warning"/>
</xs:restriction>
</xs:simpleType>

 

Эта статья была первоначально опубликована в журнале The SOA Magazine ( www.soamag.com ), официально связанном с серией сервис-ориентированных вычислений Prentice Hall от Томаса Эрла ( www.soabooks.com ). Copyright © SOA Systems Inc. ( www.soasystems.com )