Fehlerbehandlung in ASP.NET Core-Web-APIsHandle errors in ASP.NET Core web APIs

Dieser Artikel beschreibt die Verarbeitung und Anpassung der Fehlerbehandlung bei ASP.NET Core-Web-APIs.This article describes how to handle and customize error handling with ASP.NET Core web APIs.

Anzeigen oder Herunterladen von Beispielcode (VorgehensWeise zum Herunterladen)View or download sample code (How to download)

Seite mit Ausnahmen für EntwicklerDeveloper Exception Page

Die Seite mit Ausnahmen für Entwickler ist ein nützliches Tool, um detaillierte Stapelüberwachungen für Serverfehler zu erhalten.The Developer Exception Page is a useful tool to get detailed stack traces for server errors. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren.It uses DeveloperExceptionPageMiddleware to capture synchronous and asynchronous exceptions from the HTTP pipeline and to generate error responses. Betrachten Sie zur Darstellung die folgende Controlleraktion:To illustrate, consider the following controller action:

[HttpGet("{city}")]
public WeatherForecast Get(string city)
{
    if (!string.Equals(city?.TrimEnd(), "Redmond", StringComparison.OrdinalIgnoreCase))
    {
        throw new ArgumentException(
            $"We don't offer a weather forecast for {city}.", nameof(city));
    }
    
    return GetWeather().First();
}

Führen Sie den folgenden curl-Befehl aus, um die vorherige Aktion zu testen:Run the following curl command to test the preceding action:

curl -i https://localhost:5001/weatherforecast/chicago

Die Seite mit Ausnahmen für Entwickler zeigt in ASP.NET Core 3.0 und später eine Nur-Text-Antwort an, wenn der Client keine HTML-formatierte Ausgabe anfordert.In ASP.NET Core 3.0 and later, the Developer Exception Page displays a plain-text response if the client doesn't request HTML-formatted output. Die folgende Ausgabe wird angezeigt:The following output appears:

HTTP/1.1 500 Internal Server Error
Transfer-Encoding: chunked
Content-Type: text/plain
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2019 16:13:16 GMT

System.ArgumentException: We don't offer a weather forecast for chicago. (Parameter 'city')
   at WebApiSample.Controllers.WeatherForecastController.Get(String city) in C:\working_folder\aspnet\AspNetCore.Docs\aspnetcore\web-api\handle-errors\samples\3.x\Controllers\WeatherForecastController.cs:line 34
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Host: localhost:44312
User-Agent: curl/7.55.1

Legen Sie den HTTP-Anforderungsheader Accept auf den Medientyp text/html fest, um stattdessen eine HTML-formatierte Antwort anzuzeigen.To display an HTML-formatted response instead, set the Accept HTTP request header to the text/html media type. Zum Beispiel:For example:

curl -i -H "Accept: text/html" https://localhost:5001/weatherforecast/chicago

Beachten Sie den folgenden Auszug aus der HTTP-Antwort:Consider the following excerpt from the HTTP response:

In ASP.NET Core 2.2 und früher wird auf der Seite mit Ausnahmen für Entwickler eine HTML-formatierte Antwort angezeigt.In ASP.NET Core 2.2 and earlier, the Developer Exception Page displays an HTML-formatted response. Beachten Sie beispielsweise den folgenden Auszug aus der HTTP-Antwort:For example, consider the following excerpt from the HTTP response:

HTTP/1.1 500 Internal Server Error
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Fri, 27 Sep 2019 16:55:37 GMT

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title>Internal Server Error</title>
        <style>
            body {
    font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
    font-size: .813em;
    color: #222;
    background-color: #fff;
}

Die HTML-formatierte Antwort wird beim Durchführen von Tests in Tools wie etwa Postman nützlich.The HTML-formatted response becomes useful when testing via tools like Postman. Auf der folgenden Bildschirmaufnahme sind jeweils die in Klartext verfassten und die HTML-formatierte Antworten in Postman dargestellt:The following screen capture shows both the plain-text and the HTML-formatted responses in Postman:

Testen auf der Seite mit Ausnahmen für Entwickler in Postman

Warnung

Aktivieren Sie die Seite mit Ausnahmen für Entwickler nur dann, wenn die App in der Entwicklungsumgebung ausgeführt wird.Enable the Developer Exception Page only when the app is running in the Development environment. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen.You don't want to share detailed exception information publicly when the app runs in production. Weitere Informationen zum Konfigurieren der Umgebungen finden Sie unter Verwenden von mehreren Umgebungen in ASP.NET Core.For more information on configuring environments, see Verwenden von mehreren Umgebungen in ASP.NET Core.

AusnahmehandlerException handler

In nicht zur Entwicklung dienenden Umgebungen kann Middleware zur Ausnahmebehandlung verwendet werden, um eine Fehlernutzlast zu erzeugen:In non-development environments, Exception Handling Middleware can be used to produce an error payload:

  1. Rufen Sie UseExceptionHandler in Startup.Configure auf, um die Middleware zu verwenden:In Startup.Configure, invoke UseExceptionHandler to use the middleware:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error");
            app.UseHsts();
        }
    
        app.UseHttpsRedirection();
        app.UseMvc();
    }
    
  2. Konfigurieren Sie eine Controlleraktion, um auf die /error-Route zu antworten:Configure a controller action to respond to the /error route:

    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error")]
        public IActionResult Error() => Problem();
    }
    
    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error")]
        public ActionResult Error([FromServices] IHostingEnvironment webHostEnvironment)
        {
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
            var isDev = webHostEnvironment.IsDevelopment();
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = isDev ? $"{ex.GetType().Name}: {ex.Message}" : "An error occurred.",
                Detail = isDev ? ex.StackTrace : null,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    }
    

Die oben stehende Error-Aktion sendet eine mit RFC 7807 konforme Nutzlast an den Client.The preceding Error action sends an RFC 7807-compliant payload to the client.

Middleware zur Ausnahmebehandlung kann auch eine detailliertere Ausgabe der Inhaltsaushandlung in der lokalen Entwicklungsumgebung bieten.Exception Handling Middleware can also provide more detailed content-negotiated output in the local development environment. Führen Sie die folgenden Schritte aus, um für ein konsistentes Nutzlastformat in Entwicklungs- und Produktionsumgebungen zu sorgen:Use the following steps to produce a consistent payload format across development and production environments:

  1. Registrieren Sie umgebungsspezifische Middleware zur Ausnahmebehandlung in Startup.Configure:In Startup.Configure, register environment-specific Exception Handling Middleware instances:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseExceptionHandler("/error-local-development");
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseExceptionHandler("/error-local-development");
        }
        else
        {
            app.UseExceptionHandler("/error");
        }
    }
    

    Im oben stehenden Code ist die Middleware mit Folgendem registriert:In the preceding code, the middleware is registered with:

    • Route /error-local-development in der Entwicklungsumgebung.A route of /error-local-development in the Development environment.
    • Route /error in einer nicht zur Entwicklung dienenden Umgebung.A route of /error in environments that aren't Development.
  2. Wenden Sie das Attributrouting auf Controlleraktionen an:Apply attribute routing to controller actions:

    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error-local-development")]
        public IActionResult ErrorLocalDevelopment(
            [FromServices] IWebHostEnvironment webHostEnvironment)
        {
            if (webHostEnvironment.EnvironmentName != "Development")
            {
                throw new InvalidOperationException(
                    "This shouldn't be invoked in non-development environments.");
            }
    
            var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
    
            return Problem(
                detail: context.Error.StackTrace,
                title: context.Error.Message);
        }
    
        [Route("/error")]
        public IActionResult Error() => Problem();
    }
    
    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error-local-development")]
        public IActionResult ErrorLocalDevelopment(
            [FromServices] IHostingEnvironment webHostEnvironment)
        {
            if (!webHostEnvironment.IsDevelopment())
            {
                throw new InvalidOperationException(
                    "This shouldn't be invoked in non-development environments.");
            }
    
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
    
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = ex.GetType().Name,
                Detail = ex.StackTrace,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    
        [Route("/error")]
        public ActionResult Error(
            [FromServices] IHostingEnvironment webHostEnvironment)
        {
            var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
            var ex = feature?.Error;
            var isDev = webHostEnvironment.IsDevelopment();
            var problemDetails = new ProblemDetails
            {
                Status = (int)HttpStatusCode.InternalServerError,
                Instance = feature?.Path,
                Title = isDev ? $"{ex.GetType().Name}: {ex.Message}" : "An error occurred.",
                Detail = isDev ? ex.StackTrace : null,
            };
    
            return StatusCode(problemDetails.Status.Value, problemDetails);
        }
    }
    

Verwenden von Ausnahmen zum Ändern der AntwortUse exceptions to modify the response

Der Inhalt der Antwort kann außerhalb des Controllers geändert werden.The contents of the response can be modified from outside of the controller. In der ASP.NET 4.x-Web-API war die einzige Möglichkeit hierfür die Verwendung des Typs HttpResponseException.In ASP.NET 4.x Web API, one way to do this was using the HttpResponseException type. ASP.NET Core enthält keinen entsprechenden Typ.ASP.NET Core doesn't include an equivalent type. Die Unterstützung für HttpResponseException kann mit folgenden Schritten hinzugefügt werden:Support for HttpResponseException can be added with the following steps:

  1. Erstellen Sie einen bekannten Ausnahmetyp namens HttpResponseException:Create a well-known exception type named HttpResponseException:

    public class HttpResponseException : Exception
    {
        public int Status { get; set; } = 500;
    
        public object Value { get; set; }
    }
    
  2. Erstellen Sie einen Aktionsfilter namens HttpResponseExceptionFilter:Create an action filter named HttpResponseExceptionFilter:

    public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
    {
        public int Order { get; set; } = int.MaxValue - 10;
    
        public void OnActionExecuting(ActionExecutingContext context) { }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception is HttpResponseException exception)
            {
                context.Result = new ObjectResult(exception.Value)
                {
                    StatusCode = exception.Status,
                };
                context.ExceptionHandled = true;
            }
        }
    }
    

    Im vorangehenden Filter wird die magische Zahl 10 vom maximalen ganzzahligen Wert subtrahiert.In the preceding filter, the magic number 10 is subtracted from the maximum integer value. Wenn Sie diese Zahl subtrahieren, können andere Filter am Ende der Pipeline ausgeführt werden.Subtracting this number allows other filters to run at the very end of the pipeline.

  3. Fügen Sie den Aktionsfilter in Startup.ConfigureServices der Filtersammlung hinzu:In Startup.ConfigureServices, add the action filter to the filters collection:

    services.AddControllers(options =>
        options.Filters.Add(new HttpResponseExceptionFilter()));
    
    services.AddMvc(options =>
            options.Filters.Add(new HttpResponseExceptionFilter()))
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    
    services.AddMvc(options =>
            options.Filters.Add(new HttpResponseExceptionFilter()))
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    

Antwort auf ÜberprüfungsfehlerValidation failure error response

Bei Web-API-Controllern antwortet MVC mit dem Antworttyp ValidationProblemDetails, wenn bei der Modellüberprüfung ein Fehler auftritt.For web API controllers, MVC responds with a ValidationProblemDetails response type when model validation fails. MVC verwendet die Ergebnisse von InvalidModelStateResponseFactory, um die Fehlerantwort auf einen Überprüfungsfehler zu erstellen.MVC uses the results of InvalidModelStateResponseFactory to construct the error response for a validation failure. Das folgende Beispiel verwendet die Factory, um den Standardantworttyp von SerializableError in Startup.ConfigureServices zu ändern:The following example uses the factory to change the default response type to SerializableError in Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var result = new BadRequestObjectResult(context.ModelState);

            // TODO: add `using System.Net.Mime;` to resolve MediaTypeNames
            result.ContentTypes.Add(MediaTypeNames.Application.Json);
            result.ContentTypes.Add(MediaTypeNames.Application.Xml);

            return result;
        };
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var result = new BadRequestObjectResult(context.ModelState);

            // TODO: add `using System.Net.Mime;` to resolve MediaTypeNames
            result.ContentTypes.Add(MediaTypeNames.Application.Json);
            result.ContentTypes.Add(MediaTypeNames.Application.Xml);

            return result;
        };
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var result = new BadRequestObjectResult(context.ModelState);

        // TODO: add `using using System.Net.Mime;` to resolve MediaTypeNames
        result.ContentTypes.Add(MediaTypeNames.Application.Json);
        result.ContentTypes.Add(MediaTypeNames.Application.Xml);

        return result;
    };
});

Fehlerantwort des ClientsClient error response

Ein Fehlerergebnis ist als Ergebnis mit dem HTTP-Statuscode 400 oder höher definiert.An error result is defined as a result with an HTTP status code of 400 or higher. Bei Web-API-Controllern transformiert MVC ein Fehlerergebnis in ein Ergebnis mit ProblemDetails.For web API controllers, MVC transforms an error result to a result with ProblemDetails.

Wichtig

ASP.NET Core 2.1 generiert eine Antwort mit Details zum Problem, die nahezu mit RFC 7807 konform ist.ASP.NET Core 2.1 generates a problem details response that's nearly RFC 7807-compliant. Wenn eine 100-prozentige Konformität wichtig ist, führen Sie ein Upgrade des Projekts auf ASP.NET Core 2.2 oder höher durch.If 100 percent compliance is important, upgrade the project to ASP.NET Core 2.2 or later.

Die Fehlerantwort kann auf eine der folgenden Arten konfiguriert werden:The error response can be configured in one of the following ways:

  1. Implementieren der ProblemDetailsFactoryImplement ProblemDetailsFactory
  2. Verwenden von ApiBehaviorOptions.ClientErrorMappingUse ApiBehaviorOptions.ClientErrorMapping

Implementieren Sie ProblemDetailsFactory.Implement ProblemDetailsFactory

MVC verwendet die Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory, um alle Instanzen von ProblemDetails und ValidationProblemDetails zu generieren.MVC uses Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory to produce all instances of ProblemDetails and ValidationProblemDetails. Hierzu gehören Clientfehlerantworten, Antworten auf Überprüfungsfehler sowie die Hilfsmethoden ControllerBase.Problem und ControllerBase.ValidationProblem.This includes client error responses, validation failure error responses, and the ControllerBase.Problem and ControllerBase.ValidationProblem helper methods.

Um die Antwort mit Problemdetails anzupassen, registrieren Sie eine benutzerdefinierte Implementierung von ProblemDetailsFactory in Startup.ConfigureServices:To customize the problem details response, register a custom implementation of ProblemDetailsFactory in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection serviceCollection)
{
    services.AddControllers();
    services.AddTransient<ProblemDetailsFactory, CustomProblemDetailsFactory>();
}

Die Fehlerantwort kann so konfiguriert werden wie im Abschnitt Verwenden von ApiBehaviorOptions.ClientErrorMapping beschrieben.The error response can be configured as outlined in the Use ApiBehaviorOptions.ClientErrorMapping section.

Verwenden von ApiBehaviorOptions.ClientErrorMappingUse ApiBehaviorOptions.ClientErrorMapping

Verwenden Sie die ClientErrorMapping-Eigenschaft zum Konfigurieren des Inhalts der ProblemDetails-Antwort.Use the ClientErrorMapping property to configure the contents of the ProblemDetails response. Der folgende Code in Startup.ConfigureServices aktualisiert beispielsweise die type-Eigenschaft für 404-Antworten:For example, the following code in Startup.ConfigureServices updates the type property for 404 responses:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });