Undantagshantering i ASP.NET webb-API

I den här artikeln beskrivs fel- och undantagshantering i ASP.NET webb-API.

HttpResponseException

Vad händer om en Web API-kontrollant genererar ett ohanterat undantag? Som standard översätts de flesta undantag till ett HTTP-svar med statuskod 500, internt serverfel.

Typen HttpResponseException är ett specialfall. Det här undantaget returnerar all HTTP-statuskod som du anger i undantagskonstruktorn. Följande metod returnerar till exempel 404, Hittades inte, om id-parametern inte är giltig.

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

Om du vill ha mer kontroll över svaret kan du även skapa hela svarsmeddelandet och inkludera det med 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;
}

Undantagsfilter

Du kan anpassa hur webb-API hanterar undantag genom att skriva ett undantagsfilter. Ett undantagsfilter körs när en kontrollantmetod genererar ett ohanterat undantag som inte är ett HttpResponseException-undantag . HttpResponseException-typen är ett specialfall eftersom den är särskilt utformad för att returnera ett HTTP-svar.

Undantagsfilter implementerar gränssnittet System.Web.Http.Filters.IExceptionFilter . Det enklaste sättet att skriva ett undantagsfilter är att härleda från klassen System.Web.Http.Filters.ExceptionFilterAttribute och åsidosätta metoden OnException .

Anmärkning

Undantagsfilter i ASP.NET webb-API liknar dem i ASP.NET MVC. De deklareras dock i ett separat namnområde och fungerar separat. I synnerhet hanterar klassen HandleErrorAttribute som används i MVC inte undantag som genereras av webb-API-kontrollanter.

Här är ett filter som konverterar NotImplementedException-undantag till HTTP-statuskod 501, Inte implementerad:

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

Egenskapen Svar för objektet HttpActionExecutedContext innehåller HTTP-svarsmeddelandet som ska skickas till klienten.

Registrera undantagsfilter

Det finns flera sätt att registrera ett undantagsfilter för webb-API:

  • Efter åtgärd
  • Efter styrenhet
  • Globalt

Om du vill tillämpa filtret på en specifik åtgärd lägger du till filtret som ett attribut i åtgärden:

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

Om du vill tillämpa filtret på alla åtgärder på en kontrollant lägger du till filtret som ett attribut i kontrollantklassen:

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

Om du vill tillämpa filtret globalt på alla webb-API-kontrollanter lägger du till en instans av filtret i samlingen GlobalConfiguration.Configuration.Filters . Undantagsfilter i den här samlingen gäller för alla åtgärder för webb-API-kontrollanter.

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

Om du använder projektmallen "ASP.NET MVC 4 Web Application" för att skapa projektet placerar du konfigurationskoden för webb-API:et WebApiConfig i klassen, som finns i mappen App_Start:

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

        // Other configuration code...
    }
}

HttpError

HttpError-objektet ger ett konsekvent sätt att returnera felinformation i svarstexten. I följande exempel visas hur du returnerar HTTP-statuskod 404 (hittades inte) med en HttpError i svarstexten.

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 är en tilläggsmetod som definierats i klassen System.Net.Http.HttpRequestMessageExtensions . Internt skapar CreateErrorResponse en HttpError-instans och skapar sedan en HttpResponseMessage som innehåller HttpError.

Om metoden lyckas i det här exemplet returneras produkten i HTTP-svaret. Men om den begärda produkten inte hittas innehåller HTTP-svaret en HttpError i begärandetexten. Svaret kan se ut så här:

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

Observera att HttpError serialiserades till JSON i det här exemplet. En fördel med att använda HttpError är att det går igenom samma process för innehållsförhandling och serialisering som alla andra starkt skrivna modeller.

HttpError- och modellverifiering

För modellverifiering kan du skicka modelltillståndet till CreateErrorResponse för att inkludera valideringsfelen i svaret:

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

    // Implementation not shown...
}

Det här exemplet kan returnera följande svar:

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

Mer information om modellverifiering finns i Modellverifiering i ASP.NET webb-API.

Använda HttpError med HttpResponseException

Föregående exempel returnerar ett HttpResponseMessage-meddelande från kontrollantåtgärden, men du kan också använda HttpResponseException för att returnera en HttpError. På så sätt kan du returnera en starkt skriven modell i det normala framgångsfallet, samtidigt som du returnerar HttpError om det finns ett fel:

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