Enviando e recebendo 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 maneira interoperável. Normalmente, um serviço define o conteúdo de falha personalizado e especifica quais operações podem devolvê-los. (Para saber mais, confira Definir e especificar falhas.) Este tópico discute como um cliente de serviço ou 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 WCF (Windows Communication Foundation), confira Especificando e tratando falhas em contratos e serviços.
Envio de falhas de SOAP
Falhas de SOAP declaradas são aquelas em que uma operação tem um System.ServiceModel.FaultContractAttribute que especifica um tipo de falha de SOAP personalizada. Falhas de 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 de SOAP declarada, detecte a condição de erro da qual a falha de SOAP e lance um novo System.ServiceModel.FaultException<TDetail>, em que o parâmetro de tipo é um novo objeto do tipo especificado em FaultContractAttribute para essa operação. O exemplo de código a seguir mostra o uso de FaultContractAttribute para especificar que a operação SampleMethod
pode retornar uma falha de SOAP com o tipo de detalhes 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 informações de erro de GreetingFault
para o cliente, pegue a condição de erro apropriada e lance um novo System.ServiceModel.FaultException<TDetail> do tipo GreetingFault
com um novo objeto GreetingFault
como o argumento, como no exemplo de código a seguir. Se o cliente for um aplicativo cliente WCF, experimentará isso como uma exceção gerenciada em que 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 problemas rapidamente em aplicativos WCF, mas sua utilidade como ferramenta de depuração é limitada. Em geral, ao depurar, é recomendável que você use a propriedade ServiceDebugBehavior.IncludeExceptionDetailInFaults. Quando você define esse valor como true, os clientes experimentam falhas como exceções FaultException<TDetail> do tipo ExceptionDetail.
Importante
Como exceções gerenciadas podem expor informações internas do aplicativo, definir ServiceBehaviorAttribute.IncludeExceptionDetailInFaults ou ServiceDebugBehavior.IncludeExceptionDetailInFaults como true
pode permitir que os clientes do WCF obtenham informações sobre exceções de operação de serviço interna, incluindo informações pessoais identificáveis ou outras informações confidenciais.
Portanto, a configurar ServiceBehaviorAttribute.IncludeExceptionDetailInFaults ou ServiceDebugBehavior.IncludeExceptionDetailInFaults como true
é recomendado apenas como uma maneira de depurar temporariamente um aplicativo de serviço. Além disso, o WSDL para um método que retorna exceções gerenciadas sem tratamento dessa forma não contém o contrato do FaultException<TDetail> do tipo ExceptionDetail. Os clientes devem esperar a possibilidade de uma falha SOAP desconhecida (retornada aos clientes do WCF como objetos System.ServiceModel.FaultException) para obter as informações de depuração corretamente.
Para enviar uma falha de SOAP não declarada, gere um objeto System.ServiceModel.FaultException (ou seja, não o FaultException<TDetail> de tipo genérico) e passe a cadeia de caracteres para o construtor. Isso é exposto aos aplicativos cliente do WCF como uma exceção gerada System.ServiceModel.FaultException, em que a cadeia de caracteres está disponível chamando o método FaultException<TDetail>.ToString.
Observação
Se você declarar uma falha de SOAP da cadeia de caracteres de tipo e, em seguida, lançar isso em seu serviço como um FaultException<TDetail>, em que o parâmetro de tipo é um System.String, o valor da cadeia de caracteres será atribuído à propriedade FaultException<TDetail>.Detail e não estará disponível em FaultException<TDetail>.ToString.
Como lidar com falhas
Em clientes WCF, as falhas de 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 do cliente WCF podem esperar lidar com exceções dos dois tipos a seguir como resultado da comunicação.
Objetos TimeoutException são gerados quando uma operação excede o período de tempo limite especificado.
Objetos CommunicationException são gerados quando há alguma condição de erro de comunicação recuperável no serviço ou no cliente.
A classe CommunicationException tem dois tipos derivados importantes, FaultException e o tipo genérico FaultException<TDetail>.
As exceções FaultException são geradas quando um ouvinte recebe uma falha que não é esperada ou especificada no contrato de operação; geralmente isso ocorre quando o aplicativo está sendo depurado e o serviço tem a propriedade ServiceDebugBehavior.IncludeExceptionDetailInFaults definida como true
.
As exceções FaultException<TDetail> são geradas 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 atributo OperationContractAttribute, com IsOneWay definido como false
).
Observação
Quando um serviço WCF tem a propriedade ServiceBehaviorAttribute.IncludeExceptionDetailInFaults ou ServiceDebugBehavior.IncludeExceptionDetailInFaults definida como true
, o cliente experimenta isso como um tipo FaultException<TDetail> não declarado do tipo ExceptionDetail. Os clientes podem capturar essa falha específica ou manipular a falha em um bloco de captura para FaultException.
Normalmente, somente as exceções FaultException<TDetail>, TimeoutException e CommunicationException são de interesse de clientes e serviços.
Observação
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.
Capturar exceções de falha na ordem correta
Como FaultException<TDetail> deriva de FaultException, e FaultException deriva de CommunicationException, é importante capturar essas exceções na ordem adequada. Se, por exemplo, você tiver um bloco try/catch no qual você captura primeiro CommunicationException, todas as falhas de SOAP especificadas e não especificadas serão tratadas lá; todos os blocos de captura subsequentes para lidar com uma exceção FaultException<TDetail> personalizada nunca serão invocados.
Lembre-se de que uma operação pode retornar qualquer número de falhas especificadas. Cada falha é um tipo exclusivo e deve ser tratada separadamente.
Manipular exceções ao fechar o canal
A maior parte da discussão anterior tem a ver com falhas enviadas no curso do processamento de mensagens de aplicativo, ou seja, mensagens enviadas explicitamente pelo cliente quando o aplicativo cliente chama operações no objeto cliente WCF.
Mesmo com objetos locais descartando o objeto, é possível gerar ou mascarar exceções que ocorrem durante o processo de reciclagem. Algo semelhante pode ocorrer quando você usa objetos de cliente WCF. Ao chamar operações, você está enviando mensagens por uma conexão estabelecida. Fechar o canal pode gerar exceções se a conexão não puder ser fechada ou se já estiver fechada, mesmo que todas as operações tenham sido retornadas corretamente.
Normalmente, os canais de objeto cliente são fechados de uma das seguintes maneiras:
Quando o objeto do 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 de 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 dar suporte a funcionalidades complexas no nível do aplicativo. Por exemplo, quando um contrato exige sessões, uma associaçã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 estiver desconectado), o canal cliente não poderá informar ao canal de serviço que a sessão foi encerrada e uma exceção poderá ocorrer.
Anular o canal, se necessário
Como fechar o canal também pode gerar exceções, recomendamos que, além de capturar exceções de falha na ordem correta, você anule o canal usado para fazer a chamada no bloco de captura.
Se a falha transmitir informações de erro específicas a uma operação, e ainda for possível que outras pessoas possam usá-la, não será necessário anular o canal (embora esses casos sejam raros). Em todos os outros casos, recomendamos que anule o canal. Para obter um exemplo que demonstre todos esses pontos, confira Exceções esperadas.
O exemplo de código a seguir mostra como lidar com exceções de falha de SOAP em um aplicativo cliente básico, incluindo uma falha declarada e uma falha não declarada.
Observação
Este código de exemplo não usa o constructo using
. 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, confira a Visão geral do cliente WCF e Usar fechamento e anulação para liberar recursos de 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