Zpracování výjimek ve webovém rozhraní API ASP.NET

Tento článek popisuje zpracování chyb a výjimek ve webovém rozhraní API ASP.NET.

HttpResponseException

Co se stane, když řadič webového rozhraní API vyvolá nezachycenou výjimku? Ve výchozím nastavení se většina výjimek překládá do odpovědi HTTP se stavovým kódem 500, vnitřní chybou serveru.

Typ HttpResponseException je zvláštní případ. Tato výjimka vrátí veškerý stavový kód HTTP, který zadáte v konstruktoru výjimky. Například následující metoda vrátí hodnotu 404, Not Found, pokud parametr ID není platný.

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

Pokud chcete mít větší kontrolu nad odpovědí, můžete také vytvořit celou zprávu odpovědi a zahrnout ji do 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;
}

Filtry výjimek

Způsob, jakým webové rozhraní API zpracovává výjimky, můžete přizpůsobit tak, že zapíšete filtr výjimek. Filtr výjimek se spustí, když metoda kontroleru vyvolá neošetřenou výjimku, která není výjimka HttpResponseException . Typ HttpResponseException je speciální případ, protože je navržený speciálně pro vrácení odpovědi HTTP.

Filtry výjimek implementují rozhraní System.Web.Http.Filters.IExceptionFilter . Nejjednodušší způsob, jak napsat filtr výjimky, je odvodit třídu z System.Web.Http.Filters.ExceptionFilterAttribute a přepsat metodu OnException.

Poznámka:

Filtry výjimek ve webovém rozhraní API ASP.NET jsou podobné filtrům v ASP.NET MVC. Deklarují se však v samostatném oboru názvů a fungují samostatně. Konkrétně třída HandleErrorAttribute použitá v MVC nezpracovázuje výjimky vyvolané řadiči webového rozhraní API.

Tady je filtr, který převádí výjimky NotImplementedException na stavový kód HTTP 501, není implementováno:

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);
            }
        }
    }
}

Vlastnost Response objektu HttpActionExecutedContext obsahuje zprávu odpovědi HTTP, která se odešle klientovi.

Registrace filtrů výjimek

Existuje několik způsobů, jak zaregistrovat filtr výjimek webového rozhraní API:

  • Podle činnosti
  • Podle kontroleru
  • Globálně

Pokud chcete filtr použít na konkrétní akci, přidejte filtr jako atribut akce:

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

Pokud chcete filtr použít na všechny akce na kontroleru, přidejte filtr jako atribut třídy kontroleru:

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

Pokud chcete filtr použít globálně na všechny kontrolery webového rozhraní API, přidejte instanci filtru do kolekce GlobalConfiguration.Configuration.Filters . Filtry výjimek v této kolekci se vztahují na všechny akce kontroleru webového rozhraní API.

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

Pokud k vytvoření projektu použijete šablonu projektu "ASP.NET MVC 4", vložte konfigurační kód webového WebApiConfig rozhraní API do třídy, která se nachází ve složce App_Start:

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

        // Other configuration code...
    }
}

Chyba HTTP

Objekt HttpError poskytuje konzistentní způsob, jak v textu odpovědi vrátit informace o chybě. Následující příklad ukazuje, jak vrátit stavový kód HTTP 404 (Nenalezeno) s HttpError v těle odpovědi.

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 je rozšiřující metoda definovaná ve třídě System.Net.Http.HttpRequestMessageExtensions . CreateErrorResponse interně vytvoří instanci HttpError a pak vytvoří HttpResponseMessage, která obsahuje HttpError.

V tomto příkladu, pokud je metoda úspěšná, vrátí produkt v odpovědi HTTP. Pokud se ale požadovaný produkt nenajde, odpověď HTTP obsahuje v textu požadavku chybu Http . Odpověď může vypadat takto:

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"
}

Všimněte si, že v tomto příkladu se HttpError serializoval do formátu JSON. Jednou z výhod použití HttpError je, že prochází stejným procesem vyjednávání obsahu a serializace jako jakýkoli jiný model silného typu.

HttpError a ověřování modelu

Pro ověření modelu můžete předat stav modelu createErrorResponse, aby se do odpovědi zahrnuly chyby ověření:

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

    // Implementation not shown...
}

Tento příklad může vrátit následující odpověď:

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."
    ]
  }
}

Další informace o ověřování modelu najdete v tématu Ověření modelu v ASP.NET webovém rozhraní API.

Použití HttpError s HttpResponseException

Předchozí příklady vrátí zprávu HttpResponseMessage z akce kontroleru, ale můžete také použít HttpResponseException k vrácení HttpError. To vám umožní vrátit model silného typu v normálním případě úspěchu a přesto vrátit chybu HttpError , pokud dojde k chybě:

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;
    }
}