Fehlerbehandlung in ASP.NET Core-Web-APIs
In diesem Artikel wird beschrieben, wie Sie Fehler behandeln und die Fehlerbehandlung mit ASP.NET Core Web-APIs anpassen.
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 die Entwicklerausnahmeseite eine nicht behandelte Ausnahme erkennt, generiert sie eine Standard-Nur-Text-Antwort ähnlich dem folgenden Beispiel:
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 ANTWORT im HTML-Format anfordert, generiert die Entwickler-Ausnahmeseite 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;
}
...
Um eine ANTWORT im HTML-Format anzufordern, legen Sie den HTTP-Anforderungsheader Accept
auf fest text/html
.
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
Verwenden Sie in Nicht-Entwicklungsumgebungen die Middleware zur Ausnahmebehandlung , um eine Fehlernutzlast zu erzeugen:
Rufen Sie UseExceptionHandler in
Program.cs
auf, um die Middleware für die Ausnahmebehandlung hinzuzufügen:var app = builder.Build(); app.UseHttpsRedirection(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/error"); } app.UseAuthorization(); app.MapControllers(); app.Run();
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 fest true
. Diese Attributkonfiguration schließt die Fehlerhandleraktion aus der OpenAPI-Spezifikation der App aus:
[ApiExplorerSettings(IgnoreApi = true)]
Lassen Sie anonymen Zugriff auf die -Methode zu, wenn nicht authentifizierte Benutzer den Fehler sehen sollten.
Die Middleware zur Ausnahmebehandlung kann auch in der Entwicklungsumgebung verwendet werden, um ein konsistentes Nutzlastformat für alle Umgebungen zu erzeugen:
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 Route von
/error
in Nicht-Entwicklungsumgebungen.
- Route
Fügen Sie Controlleraktionen sowohl für die Entwicklungs- als auch für die Nichtentwicklungsroute 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:
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; } }
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 vorangehende Filter gibt einen
Order
des maximalen ganzzahligen Werts minus 10 an. DadurchOrder
können andere Filter am Ende der Pipeline ausgeführt werden.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. Für Web-API-Controller transformiert MVC ein Fehlerergebnis, um eine ProblemDetailszu erzeugen.
Die automatische Erstellung eines ProblemDetails
für Fehlerstatuscodes ist standardmäßig aktiviert, Fehlerantworten können jedoch auf eine der folgenden Arten konfiguriert werden:
- Verwenden des Diensts "Problemdetails"
- Implementieren der ProblemDetailsFactory
- Verwenden von ApiBehaviorOptions.ClientErrorMapping
Standardantwort für 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 zurückgibt BadRequest , 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 Problemdetailseantwort wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:
- Der
/api/values2/divide
Endpunkt wird mit einem Nenner 0 (null) aufgerufen. - Der
/api/values2/squareroot
Endpunkt wird mit einem Bogenmaß kleiner als 0 (null) aufgerufen.
Der Standardantworttext für Problemdetails weist die folgenden type
Werte , title
und status
auf:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "00-84c1fd4063c38d9f3900d06e56542d48-85d1d4-00"
}
Problemdetails-Dienst
ASP.NET Core unterstützt das Erstellen von Problemdetails für HTTP-APIs mithilfe von IProblemDetailsService. Weitere Informationen finden Sie unter 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 zurückgegeben wird BadRequest , 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 Problemdetailseantwort wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:
- Eine ungültige Eingabe wird bereitgestellt.
- Der URI verfügt über keinen übereinstimmenden Endpunkt.
- Eine nicht behandelte Ausnahme tritt 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 den obigen Code zurückgibt BadRequest
, wird ein HTTP 400-Antwortstatus ohne Antworttext zurückgegeben. SuppressMapClientErrors
verhindert, dass eine ProblemDetails
Antwort erstellt wird, auch wenn für einen API Controller-Endpunkt aufgerufen WriteAsync
wird. WriteAsync
wird später im Artikel erläutert.
Im nächsten Abschnitt wird gezeigt, wie Sie den Antworttext der Problemdetails mithilfe von CustomizeProblemDetailsanpassen, 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 und MathErrorFeature
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 Problemdetailseantwort wird mit dem vorherigen Code generiert, wenn eine der folgenden Bedingungen zutrifft:
- Der
/divide
Endpunkt wird mit einem Nenner 0 (null) aufgerufen. - Der
/squareroot
Endpunkt wird mit einem Bogenmaß kleiner als 0 (null) aufgerufen. - Der URI verfügt über keinen übereinstimmenden Endpunkt.
Der Antworttext der Problemdetails enthält Folgendes, wenn ein squareroot
Endpunkt mit einem Bogen von weniger als 0 (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:
- Clientfehlerantworten
- Antworten auf Validierungsfehlerfehler
- ControllerBase.Problem und ControllerBase.ValidationProblem
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
In diesem Artikel wird beschrieben, wie Sie Fehler behandeln und die Fehlerbehandlung mit ASP.NET Core Web-APIs anpassen.
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 die Entwicklerausnahmeseite eine nicht behandelte Ausnahme erkennt, generiert sie eine Standard-Nur-Text-Antwort ähnlich dem folgenden Beispiel:
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 ANTWORT im HTML-Format anfordert, generiert die Entwickler-Ausnahmeseite 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;
}
...
Um eine ANTWORT im HTML-Format anzufordern, legen Sie den HTTP-Anforderungsheader Accept
auf fest text/html
.
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
Verwenden Sie in Nicht-Entwicklungsumgebungen die Middleware zur Ausnahmebehandlung , um eine Fehlernutzlast zu erzeugen:
Rufen Sie UseExceptionHandler in
Program.cs
auf, um die Middleware für die Ausnahmebehandlung hinzuzufügen:var app = builder.Build(); app.UseHttpsRedirection(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/error"); } app.UseAuthorization(); app.MapControllers(); app.Run();
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 fest true
. Diese Attributkonfiguration schließt die Fehlerhandleraktion aus der OpenAPI-Spezifikation der App aus:
[ApiExplorerSettings(IgnoreApi = true)]
Lassen Sie anonymen Zugriff auf die -Methode zu, wenn nicht authentifizierte Benutzer den Fehler sehen sollten.
Die Middleware zur Ausnahmebehandlung kann auch in der Entwicklungsumgebung verwendet werden, um ein konsistentes Nutzlastformat für alle Umgebungen zu erzeugen:
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 Route von
/error
in Nicht-Entwicklungsumgebungen.
- Route
Fügen Sie Controlleraktionen sowohl für die Entwicklungs- als auch für die Nichtentwicklungsroute 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:
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; } }
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 vorangehende Filter gibt einen
Order
des maximalen ganzzahligen Werts minus 10 an. DadurchOrder
können andere Filter am Ende der Pipeline ausgeführt werden.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. Für Web-API-Controller transformiert MVC ein Fehlerergebnis, um eine ProblemDetailszu erzeugen.
Die Fehlerantwort kann auf eine der folgenden Arten konfiguriert werden:
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:
- Clientfehlerantworten
- Antworten auf Validierungsfehlerfehler
- ControllerBase.Problem und ControllerBase.ValidationProblem
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 die Middleware für die Ausnahmebehandlung anpassen.
Erstellen von ProblemDetails-Nutzdaten für Ausnahmen
ASP.NET Core erzeugt keine standardisierte Fehlernutzlast, wenn eine nicht behandelte 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 einer ProblemDetails-Nutzlast zuzuordnen. Die Middleware zur Ausnahmebehandlung kann auch verwendet werden, um eine ProblemDetails Nutzlast für nicht behandelte 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)
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 Entwicklerausnahmeseite zeigt eine Nur-Text-Antwort an, wenn der Client keine HTML-formatierte Ausgabe angibt. 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:
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 anonymen Zugriff auf die -Methode zu, wenn nicht authentifizierte Benutzer den Fehler sehen sollten.
Ausnahmehandler
In nicht zur Entwicklung dienenden Umgebungen kann Middleware zur Ausnahmebehandlung verwendet werden, um eine Fehlernutzlast zu erzeugen:
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(); }); }
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:
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.
- Route
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 obige 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:
Erstellen Sie einen bekannten Ausnahmetyp namens
HttpResponseException
:public class HttpResponseException : Exception { public int Status { get; set; } = 500; public object Value { get; set; } }
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 einen
Order
des maximalen ganzzahligen Werts minus 10 an. DadurchOrder
können andere Filter am Ende der Pipeline ausgeführt werden.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:
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:
- Clientfehlerantworten
- Fehlerantworten bei Überprüfungsfehlern
- ControllerBase.Problem und ControllerBase.ValidationProblem>
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 mit Ausnahmebehandlung funktionieren für die meisten Apps gut. Für Apps, die eine spezielle Ausnahmebehandlung erfordern, sollten Sie die Middleware für die Ausnahmebehandlung anpassen.
Erstellen einer ProblemDetails-Nutzlast für Ausnahmen
ASP.NET Core erzeugt keine standardisierte Fehlernutzlast, wenn eine nicht behandelte 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 einer ProblemDetails-Nutzlast zuzuordnen. Die Middleware zur Ausnahmebehandlung kann auch verwendet werden, um eine ProblemDetails Nutzlast für nicht behandelte Ausnahmen zurückzugeben.