Zpracování chyb ve ASP.NET Core rozhraní API
Tento článek popisuje, jak zpracovávat a přizpůsobovat zpracování chyb pomocí ASP.NET Core rozhraní API.
Zobrazení nebo stažení ukázkového kódu (Stažení )
Stránka výjimky pro vývojáře
Stránka výjimky pro vývojáře je užitečný nástroj, který poskytuje podrobné trasování zásobníku pro chyby serveru. Používá k zachycení synchronních a asynchronních výjimek z kanálu HTTP a DeveloperExceptionPageMiddleware ke generování chybových odpovědí. Pro ilustraci zvažte následující akci kontroleru:
[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();
}
Spuštěním následujícího curl příkazu otestujte předchozí akci:
curl -i https://localhost:5001/weatherforecast/chicago
Ve ASP.NET Core 3.0 a novějších verzích se na stránce výjimky vývojáře zobrazí odpověď ve formátu prostého textu, pokud klient nepožádá o výstup ve formátu HTML. Objeví se následující výstup:
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
Pokud chcete místo toho zobrazit odpověď ve formátu HTML, nastavte hlavičku požadavku Accept HTTP na text/html typ média. Například:
curl -i -H "Accept: text/html" https://localhost:5001/weatherforecast/chicago
Vezměte v úvahu následující úryvek z odpovědi HTTP:
V ASP.NET Core verze 2.2 a starší se na stránce Developer Exception Page zobrazí odpověď ve formátu HTML. Představte si například následující úryvek z odpovědi HTTP:
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;
}
Odpověď ve formátu HTML se stává užitečnou při testování prostřednictvím nástrojů, jako je Postman. Následující snímek obrazovky ukazuje odpovědi ve formátu prostého textu i HTML v aplikaci Postman:

Upozornění
Stránku výjimky pro vývojáře povolte jenom v případě, že je aplikace spuštěná ve vývojovém prostředí. Pokud je aplikace spuštěná v produkčním prostředí, nesdílejte podrobné informace o výjimce veřejně. Další informace o konfiguraci prostředí najdete v tématu Používání více prostředí v ASP.NET Core .
Neo označení metody akce obslužné rutiny chyb atributy metody HTTP, například HttpGet . Explicitní příkazy brání některým požadavkům v dosažení metody akce. Pokud by se měla chyba zobrazit neověřeným uživatelům, povolte anonymní přístup k metodě.
Obslužná rutina výjimky
V jiných než vývojových prostředích lze middleware pro zpracování výjimek použít k vytvoření datové části chyby:
V
Startup.Configuresouboru UseExceptionHandler vyvolat k použití middlewaru: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(); }Nakonfigurujte akci kontroleru tak, aby reagovala na
/errortrasu:[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); } }
Předchozí akce odešle klientovi datovou část odpovídající specifikaci Error RFC 7807.
Middleware pro zpracování výjimek může také poskytovat podrobnější výstup vyjednaný obsahem v místním vývojovém prostředí. Následující postup použijte k vytvoření konzistentního formátu datové části v různých vývojových a produkčních prostředích:
V
Startup.Configurezaregistrujte instance middlewaru pro zpracování výjimek specifické pro prostředí: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"); } }V předchozím kódu je middleware zaregistrován pomocí:
- Trasa ve
/error-local-developmentvývojovém prostředí. - Trasa v
/errorprostředích, která nejsou Vývoj.
- Trasa ve
Použití směrování atributů na akce kontroleru:
[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); } }Předchozí kód volá ControllerBase.Problem k vytvoření ProblemDetails odpovědi.
Použití výjimek k úpravě odpovědi
Obsah odpovědi je možné upravit mimo kontroler. V ASP.NET rozhraní API verze 4.x bylo jedním ze způsobů použití HttpResponseException typu . ASP.NET Core neobsahuje ekvivalentní typ. Podporu HttpResponseException pro můžete přidat pomocí následujících kroků:
Vytvořte známý typ výjimky s názvem
HttpResponseException:public class HttpResponseException : Exception { public int Status { get; set; } = 500; public object Value { get; set; } }Vytvořte filtr akcí s názvem
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; } } }Předchozí filtr určuje maximální celočíselnou
Orderhodnotu minus 10. To umožňuje spuštění dalších filtrů na konci kanálu.V
Startup.ConfigureServicessouboru přidejte filtr akcí do kolekce filtrů: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);
Odpověď na chybu ověření
U kontrolerů webového rozhraní API odpoví MVC ValidationProblemDetails typem odpovědi, když se ověření modelu nezdaří. MVC používá výsledky k InvalidModelStateResponseFactory vytvoření chybové odpovědi pro selhání ověření. Následující příklad používá objekt pro vytváření ke změně výchozího typu odpovědi na SerializableError v 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;
};
});
Odpověď na chybu klienta
Výsledek chyby je definován jako výsledek se stavový kód HTTP 400 nebo vyšší. V případě kontrolerů webového rozhraní API MVC transformuje výsledek chyby na výsledek pomocí ProblemDetails .
Důležité
ASP.NET Core 2.1 vygeneruje odpověď s podrobnostmi o problému, která téměř vyhovuje specifikaci RFC 7807. Pokud je 100% dodržování předpisů důležité, upgradujte projekt na ASP.NET Core 2.2 nebo novější.
Odpověď na chybu je možné nakonfigurovat jedním z následujících způsobů:
Implementovat ProblemDetailsFactory
MVC používá k vytvoření všech instancí a Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory ProblemDetails ValidationProblemDetails . Patří sem odpovědi na chyby klienta, chybové odpovědi při ověřování a metody ControllerBase.Problem ControllerBase.ValidationProblem a .
Pokud chcete přizpůsobit odpověď na podrobnosti problému, zaregistrujte vlastní implementaci v ProblemDetailsFactory Startup.ConfigureServices souboru :
public void ConfigureServices(IServiceCollection serviceCollection)
{
services.AddControllers();
services.AddTransient<ProblemDetailsFactory, CustomProblemDetailsFactory>();
}
Odpověď na chybu je možné nakonfigurovat tak, jak je uvedeno v části Use ApiBehaviorOptions.ClientErrorMapping.
Použití ApiBehaviorOptions.ClientErrorMapping
Ke ClientErrorMapping konfiguraci obsahu odpovědi použijte vlastnost ProblemDetails . Například následující kód v souboru Startup.ConfigureServices aktualizuje vlastnost pro odpovědi type 404:
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";
});
Vlastní middleware pro zpracování výjimek
Výchozí hodnoty middlewaru pro zpracování výjimek fungují pro většinu aplikací dobře. U aplikací, které vyžadují specializované zpracování výjimek, zvažte přizpůsobení middlewaru pro zpracování výjimek.
Vytvoření datové části ProblemDetails pro výjimky
ASP.NET Core nevytváří standardizovanou datovou část chyby, když na serveru dojde k neošetřené výjimce. Ve scénářích, kde je žádoucí vrátit klientovi standardizovanou odpověď ProblemDetails, lze middleware ProblemDetails použít k mapování výjimek a 404 na datovou část ProblemDetails. Middleware pro zpracování výjimek lze použít také k vrácení datové ProblemDetails části pro neošetřené výjimky.