Envío y recepción de errores

Los errores de SOAP transportan información de condición de errores desde un servicio a un cliente y, en caso de comunicación dúplex, desde un cliente a un servicio de manera interoperable. Normalmente, un servicio define el contenido del error personalizado y especifica qué operaciones pueden devolverlos. (Para más información, consulte Definición y especificación de errores). En este tema, se analiza cómo un servicio o cliente dúplex puede enviar esos errores cuando se ha producido la condición de error correspondiente y cómo una aplicación de servicio o cliente administra estos errores. Para información general sobre los procesos de control de errores en las aplicaciones de Windows Communication Foundation (WCF), consulte Especificación y control de errores en contratos y servicios.

Envío de errores SOAP

Los errores de SOAP declarados son aquéllos en los que una operación tiene System.ServiceModel.FaultContractAttribute que especifica un tipo de error de SOAP personalizado. Los errores de SOAP no declarados son aquéllos que no se especifican en el contrato para una operación.

Envío de errores declarados

Para enviar un error de SOAP declarado, detecte la condición de error para la que el error de SOAP es adecuado y genere una nueva System.ServiceModel.FaultException<TDetail>, donde el parámetro de tipo es un nuevo objeto del tipo especificado en FaultContractAttribute para esa operación. El ejemplo de código siguiente muestra el uso de FaultContractAttribute para especificar que la operación SampleMethod puede devolver un error de SOAP con el tipo de detalle 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 transportar la información de error GreetingFault al cliente, detecte la condición de error adecuada y genere una nueva System.ServiceModel.FaultException<TDetail> de tipo GreetingFault con un nuevo objeto GreetingFault como el argumento, como en el ejemplo de código siguiente. Si el cliente es una aplicación cliente WCF, experimenta esto como una excepción administrada donde el tipo es System.ServiceModel.FaultException<TDetail> de 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

Envío de errores no declarados

Enviar errores no declarados puede ser muy útil para diagnosticar y depurar rápidamente los problemas en las aplicaciones WCF, pero su utilidad como herramienta de depuración es limitada. Más generalmente, se recomienda al depurar que utilice la propiedad ServiceDebugBehavior.IncludeExceptionDetailInFaults. Al establecer este valor en true, los clientes experimentan tales errores como excepciones FaultException<TDetail> de tipo ExceptionDetail.

Importante

Dado que las excepciones administradas pueden exponer información interna de la aplicación, establecer ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o ServiceDebugBehavior.IncludeExceptionDetailInFaults en true puede permitir que los clientes WCF obtengan información sobre las excepciones de operaciones de servicio internas, incluida la información de identificación personal u otro tipo de información confidencial.

Por consiguiente, establecer ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o ServiceDebugBehavior.IncludeExceptionDetailInFaults en true solo está recomendado como una manera de depurar temporalmente una aplicación de servicio. Además, el WSDL de un método que devuelve excepciones administradas no controladas de esta manera no contiene el contrato para la FaultException<TDetail> de tipo ExceptionDetail. Los clientes deben contar con la posibilidad de que se produzca un error de SOAP desconocido (devuelto a los clientes WCF como objetos System.ServiceModel.FaultException) para obtener correctamente la información de depuración.

Para enviar un error de SOAP no declarado, genere un objeto de System.ServiceModel.FaultException (esto es, no del tipo genérico FaultException<TDetail>) y pase la cadena al constructor. Esto se expone a las aplicaciones cliente WCF como una excepción System.ServiceModel.FaultException generada donde la cadena está disponible llamando al método FaultException<TDetail>.ToString.

Nota

Si declara un error de SOAP de tipo cadena y, a continuación, lo genera en su servicio como una FaultException<TDetail> donde el tipo del parámetro es una System.String, el valor de la cadena está asignado a la propiedad FaultException<TDetail>.Detail, y no está disponible desde FaultException<TDetail>.ToString.

Control de errores

En clientes WCF, los errores de SOAP que se producen durante la comunicación que son de interés para las aplicaciones cliente se generan como excepciones administradas. Aunque hay muchas excepciones que pueden producirse durante la ejecución de cualquier programa, las aplicaciones que usan el modelo de programación de cliente WCF pueden esperar administrar excepciones de uno de los dos tipos siguientes como resultado de la comunicación.

Los objetos TimeoutException se producen cuando una operación supera el período de tiempo de espera especificado.

Los objetos CommunicationException se producen cuando hay alguna condición de error de comunicación recuperable en el servicio o el cliente.

La clase CommunicationException tiene dos tipos derivados importantes: FaultException y el tipo FaultException<TDetail> genérico

Las excepciones FaultException se producen cuando un agente de escucha recibe un error que no se espera o especifica en el contrato de operación; normalmente esto sucede cuando se depura la aplicación y el servicio tiene la propiedad ServiceDebugBehavior.IncludeExceptionDetailInFaults establecida en true.

Las excepciones FaultException<TDetail> se producen en el cliente cuando se recibe un error de SOAP especificado en el contrato de la operación en respuesta a una operación bidireccional (es decir, un método con un atributo OperationContractAttribute con IsOneWay establecido en false).

Nota

Cuando un servicio WCF tiene la propiedad ServiceBehaviorAttribute.IncludeExceptionDetailInFaults o ServiceDebugBehavior.IncludeExceptionDetailInFaults establecida en true, el cliente experimenta esto como una FaultException<TDetail> no declarada de tipo ExceptionDetail. Los clientes pueden detectar este error concreto o administrar el error en un bloque de detección de FaultException.

Normalmente, solo las excepciones FaultException<TDetail>, TimeoutException y CommunicationException son de interés para los clientes y servicios.

Nota

Por supuesto, se producen otras excepciones. Entre las excepciones no esperadas se incluyen errores catastróficos como System.OutOfMemoryException; normalmente las aplicaciones no deberían detectar ese tipo de métodos.

Detectar excepciones de errores en el orden correcto

Puesto que FaultException<TDetail> deriva de FaultException, y FaultException deriva de CommunicationException, es importante detectar estas excepciones en el orden apropiado. Por ejemplo, si tiene un bloque de intento/detección en el que primero detecta CommunicationException, todos los errores SOAP especificados y no especificados se administran ahí; los bloques de detección posteriores para administrar una excepción FaultException<TDetail> personalizada nunca se invocan.

Recuerde que una operación puede devolver un número indefinido de errores especificados. Cada error es de un tipo único y se ha de administrar separadamente.

Administre las excepciones al cerrar el canal

La mayor parte del análisis anterior tiene que ver con los errores enviados en el curso del procesamiento de mensajes de aplicaciones, es decir, mensajes enviados explícitamente por el cliente cuando la aplicación de cliente llama a las operaciones en el objeto de cliente WCF.

Incluso con la disposición de objetos locales, el objeto puede elevar o enmascara excepciones que tienen lugar durante el proceso de reciclaje. Algo similar puede producirse al utilizar objetos de cliente WCF. Al llamar a operaciones, está enviando mensajes a través de una conexión establecida. Cerrar el canal puede producir excepciones si la conexión no se puede cerrar limpiamente o ya está cerrada, aun cuando todas las operaciones hayan devuelto correctamente.

Normalmente, los canales de objeto de cliente se cierran de una de las siguientes maneras:

  • Cuando se recicla el objeto de cliente WCF.

  • Cuando la aplicación de cliente llama ClientBase<TChannel>.Close.

  • Cuando la aplicación de cliente llama ICommunicationObject.Close.

  • Cuando la aplicación de cliente llama a una operación que es una operación de finalización para una sesión.

En todos los casos, cerrar el canal indica al canal que comience a cerrar todos los canales subyacentes que puedan estar enviando mensajes para admitir una funcionalidad compleja en el nivel de la aplicación. Por ejemplo, cuando un contrato requiere sesiones, un enlace intenta establecer una sesión mediante el intercambio de mensajes con el canal del servicio hasta que se establezca una sesión. Cuando se cierre el canal, el canal de la sesión subyacente notifica al servicio que la sesión se ha terminado. En este caso, si el canal ya se ha anulado, cerrado o es inutilizable (por ejemplo, cuando se desconecta un cable de red), el canal de cliente no puede informar al canal del servicio que se finaliza la sesión y es posible que se produzca una excepción.

Anule el canal si fuese necesario

Dado que cerrar el canal también puede producir excepciones, se recomienda que además de detectar las excepciones de error en el orden correcto, es importante anular el canal que se utilizó para realizar la llamada en el bloque de detección.

Si el error transporta información de error específica de una operación y sigue siendo posible que otros puedan utilizarla, no hay ninguna necesidad de anular el canal (aunque estos casos son aislados). En el resto de casos, se recomienda que anule el canal. Para un ejemplo que muestra todos estos puntos, consulte Excepciones esperadas.

El siguiente ejemplo de código muestra cómo administrar las excepciones de errores de SOAP en una aplicación de cliente básica, incluyendo un error declarado y uno no declarado.

Nota

Este código de ejemplo no utiliza la construcción using. Dado que el cierre de canales puede producir excepciones, se recomienda que las aplicaciones creen primero un cliente WCF, y, a continuación, abran, utilicen y cierren el cliente WCF en el mismo bloque de intento. Para detalles, consulte Introducción a un cliente WCF y Uso de Close y Abort para liberar los recursos del 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 también