Handling Remote Exceptions in WCF

Publicado por Fabio A. Falavinha
07/4/2014
Categoria:
Tags: , , , ,

Fala pessoALL!

Neste artigo explicarei uma forma realizar o tratamento de exceções remotas com WCF aplicando dois conceitos: Orientação a Objeto e Orientação a Aspectos.

Quando utilizamos WCF (Windows Communication Foundation) toda a exceção disparada do serviço é convertida em FaultException. Porém, para que possamos recuperar em detalhe a causa específica do problema, é necessário a criação do objeto Detail. Este objeto servirá para distinguir os tipos de erros que aconterão no serviço.

Mas o problema principal do controle de exceções remotas não é capturar a exceção e sim exibí-la para o cliente de uma forma simples e transparente. Para isso, vou criar um modelo de abstração para que vocês possam entender os variados tipos de erros a serem exibidos para o cliente.

Primeiro criaremos as classes que representarão o detalhe da exceção através dos tipos: Warning e Error:

public interface IFaultHandler
{
    void Fire(ITargetView targetView);
}

[DataContract]
public class WarnFaultHandler : IFaultHandler
{
   [DataMember]
   public string Message { get; set; }

   public void Fire(ITargetView targetView)
   {
      targetView.ShowWarning(Message);
   }
}

[DataContract]
public class ErrorFaultHandler : IFaultHandler
{
    [DataMember]
    public string Message { get; set; }

    [DataMember]
    public string StackTrace { get; set; }

    public void Fire(ITargetView targetView)
    {
       targetView.ShowError(Message, StackTrace);
    }
}

Agora criaremos as exceções para os tipos: Erros e Warnings.
public abstract class UserException : Exception
{
    public abstract IFaultHandler FaultHandler { get; set; }
}

public class ErrorException : UserException
{
    public override IFaultHandler FaultHandler { get; set; }

    public ErrorException(Exception error)
    {
        FaultHandler = new ErrorFaultHandler()
        {
            Message = error.Message,
            StackTrace = error.StackTrace
        };
    }
}

public class WarnException : UserException
{
    public override IFaultHandler FaultHandler { get; set; }

    public WarnException(string message)
    {
        FaultHandler = new WarnFaultHandler()
        {
            Message = message
        };
    }
}

Com as classes acima podemos capturar exceções do tipo UserException e exibí-las para o usuário de uma forma transparente sem nenhum tipo de conhecimento a respeito do IFaultHandler associado a ela.

Este modelo permite que novos tipos sejam criados e o desenvolvedor possa disparar as exceções sem se preocupar como elas serão exibidas para o cliente, pois tudo depende da implementação da interface ITargetView.

public interface ITargetView
{
   void ShowError(string message, string stackTrace);
   void ShowWarning(string message);
}

Se a sua aplicação for Web, Console ou Desktop implemente a interface ITargetView para que o controle de exceção possa disparar contra esta interface para exibir os detalhes do erro (fault).

Alguns exemplos de implementação do ITargetView:

public class ConsoleView : ITargetView
{
   public void ShowError(string message, string stackTrace)</div>
{
       Console.WriteLine("[ERROR] {0}{1}{2}", message, Environment.NewLine, stackTrace);
   }

   public void ShowWarning(string message)
   {
       Console.WriteLine("[WARN] {0}", message);
   }
}
public class WinFormsView : ITargetView
{
     public void ShowError(string message, string stackTrace)
     {
         MessageBox.Show(string.Concat(message, Environment.NewLine, stackTrace), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     }

     public void ShowWarning(string message)
     {
         MessageBox.Show(message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
     }
}

Para exemplificar o uso do modelo acima, criarei um serviço RESTful usando WCF para adicionar o nome de contatos. Portanto, vou criar o serviço ContactService com a interface de contrato IContactService.

[ServiceContract]
public interface IContactService
{
    [OperationContract]
    [FaultContract(typeof(ErrorFaultHandler))]
    [FaultContract(typeof(WarnFaultHandler))]
    void AddContact(string name);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ContactService : IContactService
{
    private readonly List<Contact> contacts = new List<Contact>();

    [WebInvoke(Method = "POST", UriTemplate = "/contact/{name}")]
    public void AddContact(string name)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                 throw new WarnException("Name is required");
            }
            contacts.Add(new Contact()
            {
                Name = name
            });
        }
        catch (UserException ex)
        {
            throw new WebFaultException<IFaultHandler>(ex.FaultHandler, HttpStatusCode.InternalServerError);
        }
    }
}

Vejam que o serviço está realizando o controle de exceção através de um try/catch. Um possível refactoring neste código é usar o conceito de AOP. Leia o artigo Tratamento de exceções usando AOP escrito por Ricardo Tomoiti, onde ele demonstra como implementar e configurar um aspecto (IThrowsAdvice) para capturar as exceções.

Quando pensamos em tratar exceções logo vem a imagem de um bloco de código dentro de um try/catch. Com o modelo explicado neste artigo e com o uso de AOP, podemos livrar o código de blocos com try/catch, independente se a exceção é remota ou local, pois o controle de exceções deve vir como um modelo de abstração conforme a necessidade da arquitetura e não apenas quando um erro é encontrado por falta de tratamento da mesma.

Use o código deste artigo e evolua-o conforme a sua necessidade, criando mais fault handlers e custom exceptions para que você possa pensar no controle de exceção como um elemento de sua arquitetura e não apenas como um bloco de try/catch.

1 Comentário





Desenvolvido por hacklab/ com WordPress