Envio e recebimento de falhas

As falhas SOAP transmitem informações de condição de erro de um serviço para um cliente e, no caso duplex, de um cliente para um serviço de forma interoperável. Normalmente, um serviço define o conteúdo de falha personalizado e especifica quais operações podem retorná-lo. (Para obter mais informações, consulte Definição e especificação de falhas.) Este tópico discute como um serviço ou cliente duplex pode enviar essas falhas quando a condição de erro correspondente ocorreu e como um cliente ou aplicativo de serviço lida com essas falhas. Para obter uma visão geral do tratamento de erros em aplicativos do Windows Communication Foundation (WCF), consulte Especificando e manipulando falhas em contratos e serviços.

Enviando falhas SOAP

Falhas SOAP declaradas são aquelas em que uma operação tem um System.ServiceModel.FaultContractAttribute que especifica um tipo de falha SOAP personalizado. Falhas SOAP não declaradas são aquelas que não são especificadas no contrato para uma operação.

Envio de falhas declaradas

Para enviar uma falha SOAP declarada, detete a condição de erro para a qual a falha SOAP é apropriada e lance uma nova System.ServiceModel.FaultException<TDetail> em que o parâmetro type é um novo objeto do tipo especificado no FaultContractAttribute para essa operação. O exemplo de código a seguir mostra o uso de FaultContractAttribute para especificar que a SampleMethod operação pode retornar uma falha SOAP com o tipo de detalhe de 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

Para transmitir as GreetingFault informações de erro para o cliente, pegue a condição de erro apropriada e lance um novo System.ServiceModel.FaultException<TDetail> tipo com um novo GreetingFault objeto como argumento, como no exemplo de GreetingFault código a seguir. Se o cliente for um aplicativo cliente WCF, ele experimenta isso como uma exceção gerenciada onde o tipo é System.ServiceModel.FaultException<TDetail> do tipo 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

Envio de falhas não declaradas

O envio de falhas não declaradas pode ser muito útil para diagnosticar e depurar rapidamente problemas em aplicativos WCF, mas sua utilidade como ferramenta de depuração é limitada. Mais geralmente, ao depurar, é recomendável que você use a ServiceDebugBehavior.IncludeExceptionDetailInFaults propriedade. Quando você define esse valor como true, os clientes experimentam tais falhas como FaultException<TDetail> exceções do tipo ExceptionDetail.

Importante

Como as exceções gerenciadas podem expor informações internas do aplicativo, definir ServiceBehaviorAttribute.IncludeExceptionDetailInFaults ou ServiceDebugBehavior.IncludeExceptionDetailInFaults permitir true que os clientes WCF obtenham informações sobre exceções de operação de serviço interno, incluindo informações pessoalmente identificáveis ou outras informações confidenciais.

Portanto, a configuração ServiceBehaviorAttribute.IncludeExceptionDetailInFaults ou ServiceDebugBehavior.IncludeExceptionDetailInFaults para true só é recomendada como uma forma de depurar temporariamente um aplicativo de serviço. Além disso, o WSDL para um método que retorna exceções gerenciadas não tratadas dessa maneira não contém o contrato para o FaultException<TDetail> tipo ExceptionDetail. Os clientes devem esperar a possibilidade de uma falha SOAP desconhecida (retornada aos clientes WCF como System.ServiceModel.FaultException objetos) para obter as informações de depuração corretamente.

Para enviar uma falha SOAP não declarada, lance um System.ServiceModel.FaultException objeto (ou seja, não o tipo FaultException<TDetail>genérico) e passe a cadeia de caracteres para o construtor. Isso é exposto aos aplicativos cliente WCF como uma exceção lançada System.ServiceModel.FaultException , onde a cadeia de caracteres está disponível chamando o FaultException<TDetail>.ToString método.

Nota

Se você declarar uma falha SOAP do tipo string e, em seguida, lançar isso em seu serviço como um FaultException<TDetail> onde o parâmetro type é um System.String o valor da cadeia de caracteres é atribuído à FaultException<TDetail>.Detail propriedade e não está disponível em FaultException<TDetail>.ToString.

Tratamento de falhas

Em clientes WCF, falhas SOAP que ocorrem durante a comunicação que são de interesse para aplicativos cliente são geradas como exceções gerenciadas. Embora haja muitas exceções que podem ocorrer durante a execução de qualquer programa, os aplicativos que usam o modelo de programação de cliente WCF podem esperar lidar com exceções dos dois tipos a seguir como resultado da comunicação.

TimeoutException Os objetos são lançados quando uma operação excede o período de tempo limite especificado.

CommunicationException Os objetos são lançados quando há alguma condição de erro de comunicação recuperável no serviço ou no cliente.

A CommunicationException classe tem dois tipos FaultException derivados importantes e o tipo genérico FaultException<TDetail> .

FaultException exceções são lançadas quando um ouvinte recebe uma falha que não é esperada ou especificada no contrato de operação; Normalmente, isso ocorre quando o aplicativo está sendo depurado e o serviço tem a ServiceDebugBehavior.IncludeExceptionDetailInFaults propriedade definida como true.

FaultException<TDetail> Exceções são lançadas no cliente quando uma falha especificada no contrato de operação é recebida em resposta a uma operação bidirecional (ou seja, um método com um OperationContractAttribute atributo com IsOneWay definido como false).

Nota

Quando um serviço WCF tem a ServiceBehaviorAttribute.IncludeExceptionDetailInFaults propriedade ou ServiceDebugBehavior.IncludeExceptionDetailInFaults definida para true o cliente experimenta isso como um tipo ExceptionDetailnão declarado FaultException<TDetail> . Os clientes podem detetar essa falha específica ou lidar com a falha em um bloco de captura para FaultException.

Normalmente, apenas FaultException<TDetail>, TimeoutExceptione CommunicationException exceções são de interesse para clientes e serviços.

Nota

Outras exceções, é claro, ocorrem. Exceções inesperadas incluem falhas catastróficas como System.OutOfMemoryException, normalmente, os aplicativos não devem capturar esses métodos.

Exceções de falha de captura na ordem correta

Como FaultException<TDetail> deriva de FaultException, e FaultException deriva de CommunicationException, é importante pegar essas exceções na ordem adequada. Se, por exemplo, você tiver um bloco try/catch no qual você pega CommunicationExceptionpela primeira vez, todas as falhas SOAP especificadas e não especificadas são tratadas lá, quaisquer blocos de captura subsequentes para lidar com uma exceção personalizada FaultException<TDetail> nunca são invocados.

Lembre-se de que uma operação pode retornar qualquer número de falhas especificadas. Cada falha é um tipo único e deve ser tratada separadamente.

Lidar com exceções ao fechar o canal

A maior parte da discussão anterior tem a ver com falhas enviadas durante o processamento de mensagens de aplicativo, ou seja, mensagens enviadas explicitamente pelo cliente quando o aplicativo cliente chama operações no objeto de cliente WCF.

Mesmo com objetos locais, o descarte do objeto pode gerar ou mascarar exceções que ocorrem durante o processo de reciclagem. Algo semelhante pode ocorrer quando você usa objetos de cliente WCF. Quando você chama operações, você está enviando mensagens através de uma conexão estabelecida. Fechar o canal pode gerar exceções se a conexão não puder ser fechada corretamente ou já estiver fechada, mesmo que todas as operações retornem corretamente.

Normalmente, os canais de objeto do cliente são fechados de uma das seguintes maneiras:

  • Quando o objeto de cliente WCF é reciclado.

  • Quando o aplicativo cliente chama ClientBase<TChannel>.Close.

  • Quando o aplicativo cliente chama ICommunicationObject.Close.

  • Quando o aplicativo cliente chama uma operação que é uma operação de encerramento para uma sessão.

Em todos os casos, fechar o canal instrui o canal a começar a fechar quaisquer canais subjacentes que possam estar enviando mensagens para suportar funcionalidades complexas no nível do aplicativo. Por exemplo, quando um contrato requer sessões, uma vinculação tenta estabelecer uma sessão trocando mensagens com o canal de serviço até que uma sessão seja estabelecida. Quando o canal é fechado, o canal de sessão subjacente notifica o serviço de que a sessão foi encerrada. Nesse caso, se o canal já tiver abortado, fechado ou estiver inutilizável (por exemplo, quando um cabo de rede for desconectado), o canal cliente não poderá informar o canal de serviço de que a sessão foi encerrada e uma exceção pode resultar.

Abortar o canal, se necessário

Como fechar o canal também pode gerar exceções, então, recomenda-se que, além de capturar exceções de falha na ordem correta, seja importante abortar o canal que foi usado para fazer a chamada no bloco de captura.

Se a falha transmite informações de erro específicas de uma operação e continua a ser possível que outros possam usá-lo, não há necessidade de abortar o canal (embora esses casos sejam raros). Em todos os outros casos, recomenda-se que você aborte o canal. Para obter um exemplo que demonstra todos esses pontos, consulte Exceções esperadas.

O exemplo de código a seguir mostra como manipular exceções de falha SOAP em um aplicativo cliente básico, incluindo uma falha declarada e uma falha não declarada.

Nota

Este código de exemplo não usa a using construção. Como o fechamento de canais pode gerar exceções, é recomendável que os aplicativos criem um cliente WCF primeiro e, em seguida, abram, usem e fechem o cliente WCF no mesmo bloco de tentativa. Para obter detalhes, consulte Visão geral do cliente WCF e Usar fechar e abortar para liberar recursos do cliente 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

Consulte também