Fehlerbehandlung in ASP.NET Core-Web-APIs

Dieser Artikel beschreibt die Behandlung von Fehlern sowie die Anpassung der Fehlerbehandlung mit ASP.NET Core-Web-APIs.

Seite mit Ausnahmen für Entwickler

Auf der Seite mit Entwicklerausnahmen werden detaillierte Stapelüberwachungen für Serverfehler angezeigt. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren. Betrachten Sie beispielsweise die folgende Controlleraktion, die eine Ausnahme auslöst:

[HttpGet("Throw")]
public IActionResult Throw() =>
    throw new Exception("Sample exception.");

Wenn auf der Seite mit Ausnahmen für Entwickler eine unbehandelte Ausnahme erkannt wird, generiert sie eine Nur-Text-Standardantwort, die dem folgenden Beispiel ähnelt:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

System.Exception: Sample exception.
   at HandleErrorsSample.Controllers.ErrorsController.Get() in ...
   at lambda_method1(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

...

Wenn der Client eine HTML-formatierte Antwort anfordert, generiert die Seite mit Ausnahmen für Entwickler eine Antwort ähnlich dem folgenden Beispiel:

HTTP/1.1 500 Internal Server Error
Content-Type: text/html; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

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

h1 {
    color: #44525e;
    margin: 15px 0 15px 0;
}

...

Legen Sie den Accept-HTTP-Anforderungsheader auf text/html fest, um eine HTML-formatierte Antwort anzufordern.

Warnung

Aktivieren Sie die Seite für Entwicklerausnahmen nur, wenn die App in der Entwicklungsumgebung ausgeführt wird. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Ausnahmehandler

In Nicht-Entwicklungsumgebungen kann Middleware zur Ausnahmebehandlung verwendet werden, um Fehlernutzdaten zu generieren:

  1. Rufen Sie in Program.csUseExceptionHandler auf, um die Middleware zur Ausnahmebehandlung hinzuzufügen:

    var app = builder.Build();
    
    app.UseHttpsRedirection();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error");
    }
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    
  2. Konfigurieren Sie eine Controlleraktion, um auf die /error-Route zu antworten:

    [Route("/error")]
    public IActionResult HandleError() =>
        Problem();
    

Die oben stehende HandleError-Aktion sendet eine mit RFC 7807 konforme Nutzlast an den Client.

Warnung

Markieren Sie die Aktionsmethode für die Fehlerbehandlung nicht mit HTTP-Methodenattributen wie HttpGet. Durch explizite Verben könnte bei einigen Anforderungen verhindert werden, dass diese Aktionsmethode zum Einsatz kommt.

Für Web-APIs, die Swagger/OpenAPI verwenden, markieren Sie die Fehlerhandleraktion mit dem Attribut [ApiExplorerSettings], und legen Sie die IgnoreApi-Eigenschaft auf true fest. Diese Attributkonfiguration schließt die Fehlerhandleraktion aus der OpenAPI-Spezifikation der App aus:

[ApiExplorerSettings(IgnoreApi = true)]

Lassen Sie den anonymen Zugriff auf die Methode zu, wenn nicht authentifizierten Benutzern der Fehler angezeigt werden soll.

Die Middleware zur Ausnahmebehandlung kann auch in der Entwicklungsumgebung verwendet werden, um ein konsistentes Nutzdatenformat für alle Umgebungen zu erzeugen:

  1. Registrieren Sie umgebungsspezifische Middleware zur Ausnahmebehandlung in Program.cs:

    if (app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error-development");
    }
    else
    {
        app.UseExceptionHandler("/error");
    }
    

    Im oben stehenden Code ist die Middleware mit Folgendem registriert:

    • Route /error-development in der Entwicklungsumgebung.
    • Eine /error-Route in Nicht-Entwicklungsumgebungen.

  2. Fügen Sie Controlleraktionen sowohl für die Entwicklungs- als auch für die Nicht-Entwicklungsrouten hinzu:

    [Route("/error-development")]
    public IActionResult HandleErrorDevelopment(
        [FromServices] IHostEnvironment hostEnvironment)
    {
        if (!hostEnvironment.IsDevelopment())
        {
            return NotFound();
        }
    
        var exceptionHandlerFeature =
            HttpContext.Features.Get<IExceptionHandlerFeature>()!;
    
        return Problem(
            detail: exceptionHandlerFeature.Error.StackTrace,
            title: exceptionHandlerFeature.Error.Message);
    }
    
    [Route("/error")]
    public IActionResult HandleError() =>
        Problem();
    

Verwenden von Ausnahmen zum Ändern der Antwort

Der Inhalt der Antwort kann von außerhalb des Controllers mithilfe einer benutzerdefinierten Ausnahme und eines Aktionsfilters geändert werden:

  1. Erstellen Sie einen bekannten Ausnahmetyp namens HttpResponseException:

    public class HttpResponseException : Exception
    {
        public HttpResponseException(int statusCode, object? value = null) =>
            (StatusCode, Value) = (statusCode, value);
    
        public int StatusCode { get; }
    
        public object? Value { get; }
    }
    
  2. Erstellen Sie einen Aktionsfilter namens HttpResponseExceptionFilter:

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

    Der vorherige Filter gibt eine Order des maximalen ganzzahligen Werts minus 10 an. Durch diese Order können andere Filter am Ende der Pipeline ausgeführt werden.

  3. Fügen Sie den Aktionsfilter in Program.cs der Filtersammlung hinzu:

    builder.Services.AddControllers(options =>
    {
        options.Filters.Add<HttpResponseExceptionFilter>();
    });
    

Antwort auf Überprüfungsfehler

Bei Web-API-Controllern antwortet MVC mit dem Antworttyp ValidationProblemDetails, wenn bei der Modellüberprüfung ein Fehler auftritt. MVC verwendet die Ergebnisse von InvalidModelStateResponseFactory, um die Fehlerantwort auf einen Überprüfungsfehler zu erstellen. Im folgenden Beispiel wird die Standardfactory durch eine Implementierung ersetzt, die auch das Formatieren von Antworten als XML in Program.cs unterstützt:

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
            new BadRequestObjectResult(context.ModelState)
            {
                ContentTypes =
                {
                    // using static System.Net.Mime.MediaTypeNames;
                    Application.Json,
                    Application.Xml
                }
            };
    })
    .AddXmlSerializerFormatters();

Fehlerantwort des Clients

Ein Fehlerergebnis ist als Ergebnis mit dem HTTP-Statuscode 400 oder höher definiert. Bei Web-API-Controllern transformiert MVC ein Fehlerergebnis so, dass ProblemDetails erzeugt werden.

Die automatische Erstellung von ProblemDetails für Fehlerstatuscodes ist standardmäßig aktiviert, aber Fehlerantworten können auf eine der folgenden Arten konfiguriert werden:

  1. Verwenden des Diensts für Problemdetails
  2. Implementieren der ProblemDetailsFactory
  3. Verwenden von ApiBehaviorOptions.ClientErrorMapping

Standardantwort mit Problemdetails

Die folgende Program.cs-Datei wurde von den Webanwendungsvorlagen für API-Controller generiert:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Betrachten Sie den folgenden Controller, der BadRequest zurückgibt, wenn die Eingabe ungültig ist:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values2Controller : ControllerBase
{
    // /api/values2/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values2 /squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }
}

Eine Antwort mit Problemdetails wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:

  • Der Endpunkt /api/values2/divide wird mit einem Nenner von Null aufgerufen.
  • Der Endpunkt /api/values2/squareroot wird mit einem Radikanden kleiner als Null aufgerufen.

Der Text der Standardantwort mit Problemdetails weist die folgenden type-, title- und status-Werte auf:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "Bad Request",
  "status": 400,
  "traceId": "00-84c1fd4063c38d9f3900d06e56542d48-85d1d4-00"
}

Dienst für Problemdetails

ASP.NET Core unterstützt das Erstellen von Problemdetails für HTTP-APIs mithilfe von IProblemDetailsService. Weitere Informationen finden Sie im Abschnitt Dienst für Problemdetails.

Mit dem folgenden Code wird die App so konfiguriert, dass eine Problemdetailantwort für alle Fehlerantworten von HTTP-Client und -Server generiert wird, die noch keinen Textinhalt aufweisen:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

Betrachten Sie den API-Controller aus dem vorherigen Abschnitt, der BadRequest zurückgibt, wenn die Eingabe ungültig ist:

[Route("api/[controller]/[action]")]
[ApiController]
public class Values2Controller : ControllerBase
{
    // /api/values2/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values2 /squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }
}

Eine Antwort mit Problemdetails wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:

  • Es wird eine ungültige Eingabe bereitgestellt.
  • Der URI verfügt über keinen übereinstimmenden Endpunkt.
  • Es tritt eine unbehandelte Ausnahme auf.

Die automatische Erstellung einer ProblemDetails-Antwort für Fehlerstatuscodes ist deaktiviert, wenn die SuppressMapClientErrors-Eigenschaft auf true festgelegt ist:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressMapClientErrors = true;
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Wenn ein API-Controller BadRequest zurückgibt, wird unter Verwendung des vorherigen Codes ein HTTP 400-Antwortstatus ohne Antworttext zurückgegeben. SuppressMapClientErrors verhindert, dass eine ProblemDetails-Antwort erstellt wird, auch wenn WriteAsync für einen API-Controller-Endpunkt aufgerufen wird. WriteAsync wird später im Artikel erläutert.

Im nächsten Abschnitt wird gezeigt, wie Sie den Text der Problemdetailantwort mithilfe von CustomizeProblemDetails anpassen, um eine hilfreichere Antwort zurückzugeben. Weitere Anpassungsoptionen finden Sie unter Anpassen von Problemdetails.

Anpassen von Problemdetails mit CustomizeProblemDetails

Der folgende Code verwendet ProblemDetailsOptions, um CustomizeProblemDetails festzulegen:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddProblemDetails(options =>
        options.CustomizeProblemDetails = (context) =>
        {

            var mathErrorFeature = context.HttpContext.Features
                                                       .Get<MathErrorFeature>();
            if (mathErrorFeature is not null)
            {
                (string Detail, string Type) details = mathErrorFeature.MathError switch
                {
                    MathErrorType.DivisionByZeroError =>
                    ("Divison by zero is not defined.",
                                          "https://wikipedia.org/wiki/Division_by_zero"),
                    _ => ("Negative or complex numbers are not valid input.",
                                          "https://wikipedia.org/wiki/Square_root")
                };

                context.ProblemDetails.Type = details.Type;
                context.ProblemDetails.Title = "Bad Input";
                context.ProblemDetails.Detail = details.Detail;
            }
        }
    );

var app = builder.Build();

app.UseHttpsRedirection();

app.UseStatusCodePages();

app.UseAuthorization();

app.MapControllers();

app.Run();

Der aktualisierte API-Controller:

[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // /api/values/divide/1/2
    [HttpGet("{Numerator}/{Denominator}")]
    public IActionResult Divide(double Numerator, double Denominator)
    {
        if (Denominator == 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.DivisionByZeroError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Numerator / Denominator);
    }

    // /api/values/squareroot/4
    [HttpGet("{radicand}")]
    public IActionResult Squareroot(double radicand)
    {
        if (radicand < 0)
        {
            var errorType = new MathErrorFeature
            {
                MathError = MathErrorType.NegativeRadicandError
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }

        return Ok(Math.Sqrt(radicand));
    }

}

Der folgende Code enthält MathErrorFeature und MathErrorType, die mit dem vorherigen Beispiel verwendet werden:

// Custom Http Request Feature
class MathErrorFeature
{
    public MathErrorType MathError { get; set; }
}

// Custom math errors
enum MathErrorType
{
    DivisionByZeroError,
    NegativeRadicandError
}

Eine Antwort mit Problemdetails wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:

  • Der Endpunkt /divide wird mit einem Nenner von Null aufgerufen.
  • Der Endpunkt /squareroot wird mit einem Radikanden kleiner als Null aufgerufen.
  • Der URI verfügt über keinen übereinstimmenden Endpunkt.

Der Text der Problemdetailantwort enthält Folgendes, wenn ein squareroot-Endpunkt mit einem Radikanden kleiner als Null aufgerufen wird:

{
  "type": "https://en.wikipedia.org/wiki/Square_root",
  "title": "Bad Input",
  "status": 400,
  "detail": "Negative or complex numbers are not allowed."
}

Anzeigen oder Herunterladen von Beispielcode

Implementieren Sie ProblemDetailsFactory.

MVC verwendet die Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory, um alle Instanzen von ProblemDetails und ValidationProblemDetails zu generieren. Diese Factory wird für Folgendes verwendet:

Um die Antwort mit Problemdetails anzupassen, registrieren Sie eine benutzerdefinierte Implementierung von ProblemDetailsFactory in Program.cs:

builder.Services.AddControllers();
builder.Services.AddTransient<ProblemDetailsFactory, SampleProblemDetailsFactory>();

Verwenden Sie ApiBehaviorOptions.ClientErrorMapping

Verwenden Sie die ClientErrorMapping-Eigenschaft zum Konfigurieren des Inhalts der ProblemDetails-Antwort. Der folgende Code in Program.cs aktualisiert beispielsweise die Link-Eigenschaft für 404-Antworten:

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

Zusätzliche Ressourcen

Dieser Artikel beschreibt die Behandlung von Fehlern sowie die Anpassung der Fehlerbehandlung mit ASP.NET Core-Web-APIs.

Seite mit Ausnahmen für Entwickler

Auf der Seite mit Entwicklerausnahmen werden detaillierte Stapelüberwachungen für Serverfehler angezeigt. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren. Betrachten Sie beispielsweise die folgende Controlleraktion, die eine Ausnahme auslöst:

[HttpGet("Throw")]
public IActionResult Throw() =>
    throw new Exception("Sample exception.");

Wenn auf der Seite mit Ausnahmen für Entwickler eine unbehandelte Ausnahme erkannt wird, generiert sie eine Nur-Text-Standardantwort, die dem folgenden Beispiel ähnelt:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

System.Exception: Sample exception.
   at HandleErrorsSample.Controllers.ErrorsController.Get() in ...
   at lambda_method1(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

...

Wenn der Client eine HTML-formatierte Antwort anfordert, generiert die Seite mit Ausnahmen für Entwickler eine Antwort ähnlich dem folgenden Beispiel:

HTTP/1.1 500 Internal Server Error
Content-Type: text/html; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

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

h1 {
    color: #44525e;
    margin: 15px 0 15px 0;
}

...

Legen Sie den Accept-HTTP-Anforderungsheader auf text/html fest, um eine HTML-formatierte Antwort anzufordern.

Warnung

Aktivieren Sie die Seite für Entwicklerausnahmen nur, wenn die App in der Entwicklungsumgebung ausgeführt wird. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Ausnahmehandler

In Nicht-Entwicklungsumgebungen kann Middleware zur Ausnahmebehandlung verwendet werden, um Fehlernutzdaten zu generieren:

  1. Rufen Sie in Program.csUseExceptionHandler auf, um die Middleware zur Ausnahmebehandlung hinzuzufügen:

    var app = builder.Build();
    
    app.UseHttpsRedirection();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error");
    }
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    
  2. Konfigurieren Sie eine Controlleraktion, um auf die /error-Route zu antworten:

    [Route("/error")]
    public IActionResult HandleError() =>
        Problem();
    

Die oben stehende HandleError-Aktion sendet eine mit RFC 7807 konforme Nutzlast an den Client.

Warnung

Markieren Sie die Aktionsmethode für die Fehlerbehandlung nicht mit HTTP-Methodenattributen wie HttpGet. Durch explizite Verben könnte bei einigen Anforderungen verhindert werden, dass diese Aktionsmethode zum Einsatz kommt.

Für Web-APIs, die Swagger/OpenAPI verwenden, markieren Sie die Fehlerhandleraktion mit dem Attribut [ApiExplorerSettings], und legen Sie die IgnoreApi-Eigenschaft auf true fest. Diese Attributkonfiguration schließt die Fehlerhandleraktion aus der OpenAPI-Spezifikation der App aus:

[ApiExplorerSettings(IgnoreApi = true)]

Lassen Sie den anonymen Zugriff auf die Methode zu, wenn nicht authentifizierten Benutzern der Fehler angezeigt werden soll.

Die Middleware zur Ausnahmebehandlung kann auch in der Entwicklungsumgebung verwendet werden, um ein konsistentes Nutzdatenformat für alle Umgebungen zu erzeugen:

  1. Registrieren Sie umgebungsspezifische Middleware zur Ausnahmebehandlung in Program.cs:

    if (app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error-development");
    }
    else
    {
        app.UseExceptionHandler("/error");
    }
    

    Im oben stehenden Code ist die Middleware mit Folgendem registriert:

    • Route /error-development in der Entwicklungsumgebung.
    • Eine /error-Route in Nicht-Entwicklungsumgebungen.

  2. Fügen Sie Controlleraktionen sowohl für die Entwicklungs- als auch für die Nicht-Entwicklungsrouten hinzu:

    [Route("/error-development")]
    public IActionResult HandleErrorDevelopment(
        [FromServices] IHostEnvironment hostEnvironment)
    {
        if (!hostEnvironment.IsDevelopment())
        {
            return NotFound();
        }
    
        var exceptionHandlerFeature =
            HttpContext.Features.Get<IExceptionHandlerFeature>()!;
    
        return Problem(
            detail: exceptionHandlerFeature.Error.StackTrace,
            title: exceptionHandlerFeature.Error.Message);
    }
    
    [Route("/error")]
    public IActionResult HandleError() =>
        Problem();
    

Verwenden von Ausnahmen zum Ändern der Antwort

Der Inhalt der Antwort kann von außerhalb des Controllers mithilfe einer benutzerdefinierten Ausnahme und eines Aktionsfilters geändert werden:

  1. Erstellen Sie einen bekannten Ausnahmetyp namens HttpResponseException:

    public class HttpResponseException : Exception
    {
        public HttpResponseException(int statusCode, object? value = null) =>
            (StatusCode, Value) = (statusCode, value);
    
        public int StatusCode { get; }
    
        public object? Value { get; }
    }
    
  2. Erstellen Sie einen Aktionsfilter namens HttpResponseExceptionFilter:

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

    Der vorherige Filter gibt eine Order des maximalen ganzzahligen Werts minus 10 an. Durch diese Order können andere Filter am Ende der Pipeline ausgeführt werden.

  3. Fügen Sie den Aktionsfilter in Program.cs der Filtersammlung hinzu:

    builder.Services.AddControllers(options =>
    {
        options.Filters.Add<HttpResponseExceptionFilter>();
    });
    

Antwort auf Überprüfungsfehler

Bei Web-API-Controllern antwortet MVC mit dem Antworttyp ValidationProblemDetails, wenn bei der Modellüberprüfung ein Fehler auftritt. MVC verwendet die Ergebnisse von InvalidModelStateResponseFactory, um die Fehlerantwort auf einen Überprüfungsfehler zu erstellen. Im folgenden Beispiel wird die Standardfactory durch eine Implementierung ersetzt, die auch das Formatieren von Antworten als XML in Program.cs unterstützt:

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
            new BadRequestObjectResult(context.ModelState)
            {
                ContentTypes =
                {
                    // using static System.Net.Mime.MediaTypeNames;
                    Application.Json,
                    Application.Xml
                }
            };
    })
    .AddXmlSerializerFormatters();

Fehlerantwort des Clients

Ein Fehlerergebnis ist als Ergebnis mit dem HTTP-Statuscode 400 oder höher definiert. Bei Web-API-Controllern transformiert MVC ein Fehlerergebnis so, dass ProblemDetails erzeugt werden.

Die Fehlerantwort kann auf eine der folgenden Arten konfiguriert werden:

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

Implementieren Sie ProblemDetailsFactory.

MVC verwendet die Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory, um alle Instanzen von ProblemDetails und ValidationProblemDetails zu generieren. Diese Factory wird für Folgendes verwendet:

Um die Antwort mit Problemdetails anzupassen, registrieren Sie eine benutzerdefinierte Implementierung von ProblemDetailsFactory in Program.cs:

builder.Services.AddControllers();
builder.Services.AddTransient<ProblemDetailsFactory, SampleProblemDetailsFactory>();

Verwenden Sie ApiBehaviorOptions.ClientErrorMapping

Verwenden Sie die ClientErrorMapping-Eigenschaft zum Konfigurieren des Inhalts der ProblemDetails-Antwort. Der folgende Code in Program.cs aktualisiert beispielsweise die Link-Eigenschaft für 404-Antworten:

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

Benutzerdefinierte Middleware zum Behandeln von Ausnahmen

Die Standardwerte in der Middleware zur Ausnahmebehandlung funktionieren für die meisten Apps gut. Für Apps, die eine spezielle Ausnahmebehandlung erfordern, sollten Sie erwägen, die Middleware zur Ausnahmebehandlung anzupassen.

Erstellen von ProblemDetails-Nutzdaten für Ausnahmen

ASP.NET Core erzeugt keine standardisierte Fehlernutzdaten, wenn eine unbehandelte Ausnahme auftritt. In Szenarien, in denen es wünschenswert ist, eine standardisierte ProblemDetails-Antwort an den Client zurückzugeben, kann die ProblemDetails-Middleware verwendet werden, um Ausnahmen und 404-Antworten den ProblemDetails-Nutzdaten zuzuordnen. Die Middleware zur Ausnahmebehandlung kann auch verwendet werden, um ProblemDetails-Nutzdaten für unbehandelte Ausnahmen zurückzugeben.

Zusätzliche Ressourcen

Dieser Artikel beschreibt die Verarbeitung und Anpassung der Fehlerbehandlung bei ASP.NET Core-Web-APIs.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

die Seite mit Ausnahmen für Entwickler

Die Seite mit Ausnahmen für Entwickler ist ein nützliches Tool, um detaillierte Stapelüberwachungen für Serverfehler zu erhalten. Es verwendet DeveloperExceptionPageMiddleware, um synchrone und asynchrone Ausnahmen aus der HTTP-Pipeline zu erfassen und um Fehlerantworten zu generieren. Betrachten Sie zur Darstellung die folgende Controlleraktion:

[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:

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

Die Seite mit Ausnahmen für Entwickler zeigt eine Nur-Text-Antwort an, wenn der Client keine HTML-formatierte Ausgabe anfordert. Die folgende Ausgabe wird angezeigt:

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. Beispiel:

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

Beachten Sie den folgenden Auszug aus der HTTP-Antwort:

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. Auf der folgenden Bildschirmaufnahme sind jeweils die in Klartext verfassten und die HTML-formatierte Antworten in Postman dargestellt:

Test the Developer Exception Page in Postman.

Warnung

Aktivieren Sie die Seite mit Ausnahmen für Entwickler nur dann, wenn die App in der Entwicklungsumgebung ausgeführt wird. Wenn die App in der Produktionsumgebung ausgeführt wird, sollten Sie keine detaillierten Ausnahmeinformationen öffentlich teilen. Weitere Informationen zum Konfigurieren der Umgebung finden Sie unter Verwenden mehrerer Umgebungen in ASP.NET Core.

Markieren Sie die Aktionsmethode für die Fehlerbehandlung nicht mit HTTP-Methodenattributen wie HttpGet. Durch explizite Verben könnte bei einigen Anforderungen verhindert werden, dass diese Aktionsmethode zum Einsatz kommt. Lassen Sie den anonymen Zugriff auf die Methode zu, wenn nicht authentifizierten Benutzern der Fehler angezeigt werden soll.

Ausnahmehandler

In nicht zur Entwicklung dienenden Umgebungen kann Middleware zur Ausnahmebehandlung verwendet werden, um eine Fehlernutzlast zu erzeugen:

  1. Rufen Sie UseExceptionHandler in Startup.Configure auf, um die Middleware zu verwenden:

    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();
        });
    }
    
  2. Konfigurieren Sie eine Controlleraktion, um auf die /error-Route zu antworten:

    [ApiController]
    public class ErrorController : ControllerBase
    {
        [Route("/error")]
        public IActionResult Error() => Problem();
    }
    

Die oben stehende Error-Aktion sendet eine mit RFC 7807 konforme Nutzlast an den Client.

Middleware zur Ausnahmebehandlung kann auch eine detailliertere Ausgabe der Inhaltsaushandlung in der lokalen Entwicklungsumgebung bieten. Führen Sie die folgenden Schritte aus, um für ein konsistentes Nutzlastformat in Entwicklungs- und Produktionsumgebungen zu sorgen:

  1. Registrieren Sie umgebungsspezifische Middleware zur Ausnahmebehandlung in Startup.Configure:

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

    Im oben stehenden Code ist die Middleware mit Folgendem registriert:

    • Route /error-local-development in der Entwicklungsumgebung.
    • Route /error in einer nicht zur Entwicklung dienenden Umgebung.

  2. Wenden Sie das Attributrouting auf Controlleraktionen an:

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

    Der vorherige Code ruft ControllerBase.Problem auf, um eine ProblemDetails-Antwort zu erstellen.

Verwenden von Ausnahmen zum Ändern der Antwort

Der Inhalt der Antwort kann außerhalb des Controllers geändert werden. In der ASP.NET 4.x-Web-API war die einzige Möglichkeit hierfür die Verwendung des Typs HttpResponseException. ASP.NET Core enthält keinen entsprechenden Typ. Die Unterstützung für HttpResponseException kann mit folgenden Schritten hinzugefügt werden:

  1. Erstellen Sie einen bekannten Ausnahmetyp namens HttpResponseException:

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

    public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
    {
        public int Order { get; } = 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;
            }
        }
    }
    

    Der vorherige Filter gibt eine Order des maximalen ganzzahligen Werts minus 10 an. Durch diese Order können andere Filter am Ende der Pipeline ausgeführt werden.

  3. Fügen Sie den Aktionsfilter in Startup.ConfigureServices der Filtersammlung hinzu:

    services.AddControllers(options =>
        options.Filters.Add(new HttpResponseExceptionFilter()));
    

Antwort auf Überprüfungsfehler

Bei Web-API-Controllern antwortet MVC mit dem Antworttyp ValidationProblemDetails, wenn bei der Modellüberprüfung ein Fehler auftritt. MVC verwendet die Ergebnisse von InvalidModelStateResponseFactory, um die Fehlerantwort auf einen Überprüfungsfehler zu erstellen. Das folgende Beispiel verwendet die Factory, um den Standardantworttyp von SerializableError in Startup.ConfigureServices zu ändern:

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

Fehlerantwort des Clients

Ein Fehlerergebnis ist als Ergebnis mit dem HTTP-Statuscode 400 oder höher definiert. Bei Web-API-Controllern transformiert MVC ein Fehlerergebnis in ein Ergebnis mit ProblemDetails.

Die Fehlerantwort kann auf eine der folgenden Arten konfiguriert werden:

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

Implementieren Sie ProblemDetailsFactory.

MVC verwendet die Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory, um alle Instanzen von ProblemDetails und ValidationProblemDetails zu generieren. Diese Factory wird für Folgendes verwendet:

Um die Antwort mit Problemdetails anzupassen, registrieren Sie eine benutzerdefinierte Implementierung von ProblemDetailsFactory in Startup.ConfigureServices:

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

Verwenden von ApiBehaviorOptions.ClientErrorMapping

Verwenden Sie die ClientErrorMapping-Eigenschaft zum Konfigurieren des Inhalts der ProblemDetails-Antwort. Der folgende Code in Startup.ConfigureServices aktualisiert beispielsweise die type-Eigenschaft für 404-Antworten:

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";
        options.DisableImplicitFromServicesParameters = true;
    });

Benutzerdefinierte Middleware zum Behandeln von Ausnahmen

Die Standardwerte in der Middleware zur Ausnahmebehandlung funktionieren für die meisten Apps gut. Für Apps, die eine spezielle Ausnahmebehandlung erfordern, sollten Sie erwägen, die Middleware zur Ausnahmebehandlung anzupassen.

Erzeugen von ProblemDetails-Nutzdaten für Ausnahmen

ASP.NET Core erzeugt keine standardisierte Fehlernutzdaten, wenn eine unbehandelte Ausnahme auftritt. In Szenarien, in denen es wünschenswert ist, eine standardisierte ProblemDetails-Antwort an den Client zurückzugeben, kann die ProblemDetails-Middleware verwendet werden, um Ausnahmen und 404-Antworten den ProblemDetails-Nutzdaten zuzuordnen. Die Middleware zur Ausnahmebehandlung kann auch verwendet werden, um ProblemDetails-Nutzdaten für unbehandelte Ausnahmen zurückzugeben.