Сбои при отправке и получении

Ошибки SOAP передают сведения об ошибке от службы клиенту и, в дуплексном случае, от клиента службе совместимым способом. Как правило, служба определяет пользовательское содержимое ошибки и указывает операции, которые могут возвращать такие ошибки. (Дополнительные сведения см. в разделе Определение и указание ошибок.) В этом разделе описывается, как служба или дуплексный клиент может отправлять эти ошибки, когда произошло соответствующее условие ошибки, и как клиент или приложение службы обрабатывает эти ошибки. Общие сведения об обработке ошибок в приложениях Windows Communication Foundation (WCF) см. в разделе "Указание и обработка ошибок в контрактах и службах".

Отправка ошибок SOAP

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

Отправка объявленных ошибок

Чтобы отправить объявленную ошибку SOAP, необходимо обнаружить условия ошибки, в которых возникает ошибка SOAP, и создать новое исключение System.ServiceModel.FaultException<TDetail>, в котором параметр типа является новым объектом типа, указанным в атрибуте FaultContractAttribute для этой операции. В следующем примере кода показано, как с помощью атрибута FaultContractAttribute указать, что операция SampleMethod может возвращать ошибку SOAP с типом по умолчанию GreetingFault.

[OperationContract]
[FaultContractAttribute(
  typeof(GreetingFault),
  Action="http://www.contoso.com/GreetingFault",
  ProtectionLevel=ProtectionLevel.EncryptAndSign
  )]
string SampleMethod(string msg);
<OperationContract, FaultContractAttribute(GetType(GreetingFault), Action:="http://www.contoso.com/GreetingFault", ProtectionLevel:=ProtectionLevel.EncryptAndSign)> _
Function SampleMethod(ByVal msg As String) As String

Для передачи клиенту сведений об ошибке GreetingFault необходимо перехватить ошибку и создать новое исключение System.ServiceModel.FaultException<TDetail> типа GreetingFault с новым объектом GreetingFault в качестве аргумента; см. пример кода ниже. Если клиент является клиентским приложением WCF, он испытывает это как управляемое исключение, в котором тип имеет System.ServiceModel.FaultException<TDetail> тип GreetingFault.

throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred. You said: " + msg));
    Throw New FaultException(Of GreetingFault)(New GreetingFault("A Greeting error occurred. You said: " & msg))
End If

Отправка необъявленных ошибок

Отправка необъявленных ошибок может быть очень полезной для быстрой диагностики и отладки проблем в приложениях WCF, но его полезность в качестве средства отладки ограничена. В общем случае при отладке рекомендуется использовать свойство ServiceDebugBehavior.IncludeExceptionDetailInFaults. Если свойству присвоено значение true, клиенты получают такие ошибки в виде исключений FaultException<TDetail> типа ExceptionDetail.

Внимание

Так как управляемые исключения могут предоставлять сведения о внутренних приложениях, параметры ServiceBehaviorAttribute.IncludeExceptionDetailInFaults или ServiceDebugBehavior.IncludeExceptionDetailInFaults разрешить true клиентам WCF получать сведения об исключениях внутренних операций службы, включая личную или другую конфиденциальную информацию.

Поэтому задавать для свойства ServiceBehaviorAttribute.IncludeExceptionDetailInFaults или ServiceDebugBehavior.IncludeExceptionDetailInFaults значение true рекомендуется только для временной отладки приложения службы. Кроме того, WSDL для метода, который возвращает такие необработанные управляемые исключения, не содержит контракт для исключения FaultException<TDetail> типа ExceptionDetail. Клиенты должны ожидать возможность неизвестного сбоя SOAP (возвращенного клиентам WCF в качестве System.ServiceModel.FaultException объектов) для правильного получения сведений об отладке.

Чтобы отправить необъявленную ошибку SOAP, необходимо создать объект System.ServiceModel.FaultException (не универсального типа FaultException<TDetail>) и передать строку конструктору. Это предоставляется клиентским приложениям WCF в качестве исключения System.ServiceModel.FaultException , в котором строка доступна путем вызова FaultException<TDetail>.ToString метода.

Примечание.

Если объявлена ошибка SOAP строкового типа, а затем она выдана службе в виде объекта FaultException<TDetail> с параметром типа System.String, значение строки присваивается свойству FaultException<TDetail>.Detail и недоступно в свойстве FaultException<TDetail>.ToString.

Обработка ошибок

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

Объекты TimeoutException выдаются по истечении заданного времени ожидания.

Объекты CommunicationException выдаются в случае возникновения устранимой ошибки передачи данных в службе или клиенте.

Класс CommunicationException имеет два важных производных типа, FaultException и универсальный тип FaultException<TDetail>.

Исключения FaultException выдаются, когда прослушиватель получает неожиданную или не указанную в контракте операции ошибку. Обычно это происходит во время отладки приложения, когда свойству ServiceDebugBehavior.IncludeExceptionDetailInFaults службы присвоено значение true.

Исключения FaultException<TDetail> выдаются в клиенте при получении ошибки, указанной в контракте операции, в ответ на двустороннюю операцию (т.е. метод с атрибутом OperationContractAttribute, у которого свойству IsOneWay присвоено значение false).

Примечание.

Если служба WCF имеет ServiceBehaviorAttribute.IncludeExceptionDetailInFaults значение или ServiceDebugBehavior.IncludeExceptionDetailInFaults свойство, заданное true клиентом, это не является необъявленным FaultException<TDetail> типом ExceptionDetail. Клиенты могут либо перехватить эту определенную ошибку, либо обработать ее в блоке catch для FaultException.

Как правило, только исключения FaultException<TDetail>, TimeoutException и CommunicationException представляют интерес для клиентов и служб.

Примечание.

Конечно, возникают и другие исключения. К неожиданным исключениям относятся неустранимые ошибки, например System.OutOfMemoryException. Как правило, приложения не должны перехватывать такие методы.

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

Поскольку класс FaultException<TDetail> является производным класса FaultException, а класс FaultException является производным класса CommunicationException, важно перехватывать эти исключения в нужном порядке. Например, если в блоке try/catch сначала перехватывается исключение CommunicationException, обработка всех объявленных и необъявленных ошибок SOAP будет выполняться в этом блоке. Все последующие блоки catch, предназначенные для обработки пользовательского исключения FaultException<TDetail>, не вызываются никогда.

Помните, что одна операция может возвращать любое количество объявленных ошибок. Каждая ошибка имеет уникальный тип и должна обрабатываться отдельно.

Обработка исключений при закрытии канала

Большая часть предыдущего обсуждения связана с ошибками, отправленными в ходе обработки сообщений приложения, то есть сообщения, явно отправленные клиентом при вызове операций клиентского приложения с объектом клиента WCF.

Даже при удалении локального объекта могут быть созданы или скрыты исключения, возникающие в процессе перезапуска. При использовании клиентских объектов WCF может произойти что-то подобное. При вызове операций сообщения передаются по установленному подключению. Закрытие канала может приводить к исключениям, если не удается аккуратно закрыть подключение или если оно уже закрыто, даже при надлежащем возврате всех операций.

Как правило, каналы объекта клиента закрываются одним из следующих способов.

  • При перезапуске клиентского объекта WCF.

  • При вызове метода ClientBase<TChannel>.Close клиентским приложением.

  • При вызове метода ICommunicationObject.Close клиентским приложением.

  • При вызове клиентским приложением операции, которая является завершающей операцией сеанса.

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

Прерывание работы канала при необходимости

Поскольку при закрытии канала также могут создаваться исключения, рекомендуется в дополнение к перехвату исключений ошибок в правильном порядке прерывать канал, использованный в вызове, в блоке catch.

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

В следующем примере кода демонстрируется обработка исключений ошибок SOAP в простом клиентском приложении, в том числе объявленных и необъявленных ошибок.

Примечание.

В этом примере кода не используется конструкция using. Так как закрывающие каналы могут вызывать исключения, рекомендуется сначала создавать клиент WCF, а затем открывать, использовать и закрывать клиент WCF в том же блоке проб. Дополнительные сведения см. в обзоре клиента WCF и использовании закрытия и прерывания для выпуска клиентских ресурсов WCF.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
  public static void Main()
  {
    // Picks up configuration from the config file.
    SampleServiceClient wcfClient = new SampleServiceClient();
    try
    {
      // Making calls.
      Console.WriteLine("Enter the greeting to send: ");
      string greeting = Console.ReadLine();
      Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));

      Console.WriteLine("Press ENTER to exit:");
      Console.ReadLine();

      // Done with service.
      wcfClient.Close();
      Console.WriteLine("Done!");
    }
    catch (TimeoutException timeProblem)
    {
      Console.WriteLine("The service operation timed out. " + timeProblem.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException<GreetingFault> greetingFault)
    {
      Console.WriteLine(greetingFault.Detail.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException unknownFault)
    {
      Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (CommunicationException commProblem)
    {
      Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
      Console.ReadLine();
      wcfClient.Abort();
    }
  }
}

Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation

Public Class Client
    Public Shared Sub Main()
        ' Picks up configuration from the config file.
        Dim wcfClient As New SampleServiceClient()
        Try
            ' Making calls.
            Console.WriteLine("Enter the greeting to send: ")
            Dim greeting As String = Console.ReadLine()
            Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))

            Console.WriteLine("Press ENTER to exit:")
            Console.ReadLine()

            ' Done with service. 
            wcfClient.Close()
            Console.WriteLine("Done!")
        Catch timeProblem As TimeoutException
            Console.WriteLine("The service operation timed out. " & timeProblem.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch greetingFault As FaultException(Of GreetingFault)
            Console.WriteLine(greetingFault.Detail.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch unknownFault As FaultException
            Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch commProblem As CommunicationException
            Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
            Console.ReadLine()
            wcfClient.Abort()
        End Try
    End Sub
End Class

См. также