Partilhar via


Tratamento de exceções na API Web ASP.NET

Este artigo descreve o tratamento de erros e exceções na ASP.NET Web API.

HttpResponseException

O que acontece se um controlador Web API lançar uma exceção não detetada? Por defeito, a maioria das exceções é convertida numa resposta HTTP com o código de estado 500, Erro do Servidor Interno.

O tipo HttpResponseException é um caso especial. Esta exceção devolve qualquer código de estado HTTP que especifique no construtor de exceções. Por exemplo, o método seguinte devolve 404, Não Encontrado, se o parâmetro id não for válido.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item;
}

Para maior controlo sobre a resposta, pode também construir toda a mensagem de resposta e incluí-la com o HttpResponseException:

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent(string.Format("No product with ID = {0}", id)),
            ReasonPhrase = "Product ID Not Found"
        };
        throw new HttpResponseException(resp);
    }
    return item;
}

Filtros de exceção

Pode personalizar a forma como a Web API lida com exceções escrevendo um filtro de exceções. Um filtro de exceção é executado quando um método de controlo lança qualquer exceção não tratada que não seja uma exceção HttpResponseException. O tipo HttpResponseException é um caso especial, pois foi concebido especificamente para devolver uma resposta HTTP.

Os filtros de exceção implementam a interface System.Web.Http.Filters.IExceptionFilter . A forma mais simples de escrever um filtro de exceção é derivar da classe System.Web.Http.Filters.ExceptionFilterAttribute e sobrescrever o método OnException .

Observação

Os filtros de exceção na API Web ASP.NET são semelhantes aos do ASP.NET MVC. No entanto, são declarados num namespace separado e funcionam separadamente. Em particular, a classe HandleErrorAttribute usada no MVC não lida com exceções lançadas pelos controladores da Web API.

Aqui está um filtro que converte exceções NotImplementedException em código de estado HTTP 501, Não Implementado:

namespace ProductStore.Filters
{
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http.Filters;

    public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is NotImplementedException)
            {
                context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
            }
        }
    }
}

A propriedade Resposta do objeto HttpActionExecutedContext contém a mensagem de resposta HTTP que será enviada ao cliente.

Registar Filtros de Exceção

Existem várias formas de registar um filtro de exceções da Web API:

  • Por meio de uma ação
  • Por controlador
  • A nível global

Para aplicar o filtro a uma ação específica, adicione o filtro como um atributo à ação:

public class ProductsController : ApiController
{
    [NotImplExceptionFilter]
    public Contact GetContact(int id)
    {
        throw new NotImplementedException("This method is not implemented");
    }
}

Para aplicar o filtro a todas as ações num controlador, adicione o filtro como um atributo à classe controlador:

[NotImplExceptionFilter]
public class ProductsController : ApiController
{
    // ...
}

Para aplicar o filtro globalmente a todos os controladores Web API, adicione uma instância do filtro à coleção GlobalConfiguration.Configuration.Filters . Filtros de exceção nesta coleção aplicam-se a qualquer ação do controlador da Web API.

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotImplExceptionFilterAttribute());

Se usar o modelo de projeto "ASP.NET MVC 4 Web Application" para criar o seu projeto, coloque o código de configuração da sua Web API dentro da WebApiConfig classe, que está localizada na pasta App_Start:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());

        // Other configuration code...
    }
}

HttpError

O objeto HttpError fornece uma forma consistente de devolver informação de erro no corpo da resposta. O exemplo seguinte mostra como devolver o código de estado HTTP 404 (Não Encontrado) com um HttpError no corpo da resposta.

public HttpResponseMessage GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.OK, item);
    }
}

CreateErrorResponse é um método de extensão definido na classe System.Net.Http.HttpRequestMessageExtensions . Internamente, o CreateErrorResponse cria uma instância HttpError e depois cria uma HttpResponseMessage que contém o HttpError.

Neste exemplo, se o método for bem-sucedido, devolve o produto na resposta HTTP. Mas se o produto solicitado não for encontrado, a resposta HTTP contém um HttpError no corpo do pedido. A resposta pode ser a seguinte:

HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51

{
  "Message": "Product with id = 12 not found"
}

Note que o HttpError foi serializado para JSON neste exemplo. Uma vantagem de usar HttpError é que passa pelo mesmo processo de negociação de conteúdo e serialização como qualquer outro modelo fortemente tipado.

HttpError e Validação do Modelo

Para validação do modelo, pode passar o estado do modelo ao CreateErrorResponse, para incluir os erros de validação na resposta:

public HttpResponseMessage PostProduct(Product item)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }

    // Implementation not shown...
}

Este exemplo pode devolver a seguinte resposta:

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 320

{
  "Message": "The request is invalid.",
  "ModelState": {
    "item": [
      "Required property 'Name' not found in JSON. Path '', line 1, position 14."
    ],
    "item.Name": [
      "The Name field is required."
    ],
    "item.Price": [
      "The field Price must be between 0 and 999."
    ]
  }
}

Para mais informações sobre validação de modelos, consulte Validação de Modelos na ASP.NET Web API.

Usar HttpError com HttpResponseException

Os exemplos anteriores devolvem uma mensagem HttpResponseMessage da ação do controlador, mas também pode usar HttpResponseException para devolver um HttpError. Isto permite-lhe devolver um modelo fortemente tipado no caso normal de sucesso, enquanto ainda permite devolver HttpError se houver um erro.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        throw new HttpResponseException(
            Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
    }
    else
    {
        return item;
    }
}