468 Stimmen

Bewährte Verfahren zur Fehlerrückgabe in ASP.NET Web API

Ich habe Bedenken hinsichtlich der Art und Weise, wie wir Fehler an den Kunden zurückgeben.

Geben wir einen Fehler sofort zurück, indem wir HttpResponseException wenn wir einen Fehler erhalten:

public void Post(Customer customer)
{
    if (string.IsNullOrEmpty(customer.Name))
    {
        throw new HttpResponseException("Customer Name cannot be empty", HttpStatusCode.BadRequest) 
    }
    if (customer.Accounts.Count == 0)
    {
         throw new HttpResponseException("Customer does not have any account", HttpStatusCode.BadRequest) 
    }
}

Oder wir sammeln alle Fehler und senden sie an den Kunden zurück:

public void Post(Customer customer)
{
    List<string> errors = new List<string>();
    if (string.IsNullOrEmpty(customer.Name))
    {
        errors.Add("Customer Name cannot be empty"); 
    }
    if (customer.Accounts.Count == 0)
    {
         errors.Add("Customer does not have any account"); 
    }
    var responseMessage = new HttpResponseMessage<List<string>>(errors, HttpStatusCode.BadRequest);
    throw new HttpResponseException(responseMessage);
}

Dies ist nur ein Beispiel-Code, es spielt keine Rolle, entweder Validierung Fehler oder Server-Fehler, ich möchte nur die beste Praxis, die Vor- und Nachteile der einzelnen Ansatz zu kennen.

351voto

gdp Punkte 7694

Für mich schicke ich in der Regel eine HttpResponseException und setze den Statuscode entsprechend, abhängig von der ausgelösten Ausnahme, und ob die Ausnahme fatal ist oder nicht, bestimmt, ob ich die HttpResponseException sofort.

Letztendlich ist es eine API, die Antworten und keine Ansichten zurücksendet, daher denke ich, dass es in Ordnung ist, eine Nachricht mit der Ausnahme und dem Statuscode an den Verbraucher zurückzusenden. Ich habe derzeit nicht benötigt, um Fehler zu akkumulieren und senden Sie sie zurück, da die meisten Ausnahmen sind in der Regel aufgrund falscher Parameter oder Anrufe usw.

Ein Beispiel in meiner Anwendung ist, dass der Client manchmal nach Daten fragt, aber es sind keine Daten verfügbar, so dass ich eine benutzerdefinierte NoDataAvailableException und an die Web-API-App weiterleiten, die dann in meinem benutzerdefinierten Filter eine entsprechende Nachricht mit dem korrekten Statuscode zurücksendet.

Ich bin mir nicht zu 100 % sicher, was die beste Methode dafür ist, aber das funktioniert bei mir derzeit, also mache ich es so.

Update :

Seit ich diese Frage beantwortet habe, sind einige Blogbeiträge zu diesem Thema geschrieben worden:

https://weblogs.asp.net/fredriknormen/asp-net-web-api-exception-handling

(diese hat einige neue Funktionen in den Nightly Builds) https://learn.microsoft.com/archive/blogs/youssefm/error-handling-in-asp-net-webapi

Aktualisierung 2

Update zu unserem Fehlerbehandlungsprozess, wir haben zwei Fälle:

  1. Bei allgemeinen Fehlern wie nicht gefunden oder ungültigen Parametern, die an eine Aktion übergeben werden, geben wir eine HttpResponseException um die Verarbeitung sofort zu beenden. Zusätzlich werden wir bei Modellfehlern in unseren Aktionen das Modellstatus-Wörterbuch an die Request.CreateErrorResponse Erweiterung und verpacken sie in eine HttpResponseException . Das Hinzufügen des Modellstatus-Wörterbuchs führt zu einer Liste der Modellfehler, die im Antwortkörper gesendet wird.

  2. Bei Fehlern, die in höheren Schichten auftreten, also bei Serverfehlern, lassen wir die Ausnahme in die Web-API-App einfließen. Hier haben wir einen globalen Ausnahmefilter, der sich die Ausnahme ansieht, sie mit ELMAH protokolliert und versucht, ihr einen Sinn zu geben, indem er den korrekten HTTP-Statuscode und eine relevante freundliche Fehlermeldung als Körper wieder in eine HttpResponseException . Bei Ausnahmen, die wir nicht erwarten, erhält der Kunde den standardmäßigen internen Serverfehler 500, aber aus Sicherheitsgründen eine allgemeine Meldung.

Aktualisierung 3

Kürzlich, nachdem wir Web API 2 übernommen haben, verwenden wir für die Rücksendung von allgemeinen Fehlern jetzt die IHttpActionResult Schnittstelle, insbesondere die eingebauten Klassen für die System.Web.Http.Results Namensraum wie NotFound, BadRequest, wenn sie passen, wenn nicht, erweitern wir sie, zum Beispiel ein NotFound-Ergebnis mit einer Antwortmeldung:

public class NotFoundWithMessageResult : IHttpActionResult
{
    private string message;

    public NotFoundWithMessageResult(string message)
    {
        this.message = message;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotFound);
        response.Content = new StringContent(message);
        return Task.FromResult(response);
    }
}

211voto

Manish Jain Punkte 9271

ASP.NET Web API 2 hat das Ganze sehr vereinfacht. Zum Beispiel, der folgende Code:

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

gibt den folgenden Inhalt an den Browser zurück, wenn das Element nicht gefunden wird:

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

Anregung: Geben Sie keinen HTTP-Fehler 500 aus, es sei denn, es liegt ein katastrophaler Fehler vor (z. B. eine WCF Fault Exception). Wählen Sie einen geeigneten HTTP-Statuscode, der den Zustand Ihrer Daten wiedergibt. (Siehe den apigee-Link unten.)

Links:

86voto

Daniel Little Punkte 16296

Es sieht so aus, als hätten Sie mehr Probleme mit der Validierung als mit Fehlern/Ausnahmen, also werde ich ein wenig über beide sagen.

Validierung

Controller-Aktionen sollten im Allgemeinen Eingabemodelle verwenden, bei denen die Validierung direkt am Modell deklariert wird.

public class Customer
{ 
    [Require]
    public string Name { get; set; }
}

Dann können Sie eine ActionFilter die automatisch Validierungsmeldungen an den Kunden zurücksendet.

public class ValidationActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var modelState = actionContext.ModelState;

        if (!modelState.IsValid) {
            actionContext.Response = actionContext.Request
                 .CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
        }
    }
} 

Weitere Informationen zu diesem Thema finden Sie unter http://ben.onfabrik.com/posts/automatic-modelstate-validation-in-aspnet-mvc

Fehlerbehandlung

Am besten ist es, eine Nachricht an den Client zurückzuschicken, die die aufgetretene Ausnahme darstellt (mit dem entsprechenden Statuscode).

Nach dem Auspacken müssen Sie Folgendes verwenden Request.CreateErrorResponse(HttpStatusCode, message) wenn Sie eine Nachricht angeben möchten. Dies bindet den Code jedoch an den Request Objekts, was Sie nicht tun müssen.

In der Regel erstelle ich eine eigene "sichere" Ausnahme, von der ich annehme, dass der Client weiß, wie sie zu behandeln ist, und verpacke alle anderen mit einem generischen 500-Fehler.

Die Verwendung eines Aktionsfilters zur Behandlung der Ausnahmen würde folgendermaßen aussehen:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        var exception = context.Exception as ApiException;
        if (exception != null) {
            context.Response = context.Request.CreateErrorResponse(exception.StatusCode, exception.Message);
        }
    }
}

Dann können Sie es global registrieren.

GlobalConfiguration.Configuration.Filters.Add(new ApiExceptionFilterAttribute());

Dies ist mein benutzerdefinierter Ausnahmetyp.

using System;
using System.Net;

namespace WebApi
{
    public class ApiException : Exception
    {
        private readonly HttpStatusCode statusCode;

        public ApiException (HttpStatusCode statusCode, string message, Exception ex)
            : base(message, ex)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode, string message)
            : base(message)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode)
        {
            this.statusCode = statusCode;
        }

        public HttpStatusCode StatusCode
        {
            get { return this.statusCode; }
        }
    }
}

Ein Beispiel für eine Ausnahme, die meine API auslösen kann.

public class NotAuthenticatedException : ApiException
{
    public NotAuthenticatedException()
        : base(HttpStatusCode.Forbidden)
    {
    }
}

40voto

tartakynov Punkte 2678

Sie können eine HttpResponseException auslösen

HttpResponseMessage response = 
    this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "your message");
throw new HttpResponseException(response);

33voto

Fabian von Ellerts Punkte 3997

Wenn Sie ASP.NET Web API 2 verwenden, ist es am einfachsten, die ApiController Short-Methode zu verwenden. Dies wird zu einem BadRequestResult führen.

return BadRequest("message");

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X