Sdílet prostřednictvím


Zpracování chyb ve webových rozhraních API ASP.NET Core

Tento článek popisuje, jak zpracovávat chyby a přizpůsobit zpracování chyb pomocí webových rozhraní API ASP.NET Core.

Stránka výjimky pro vývojáře

Na stránce výjimky pro vývojáře se zobrazují podrobné trasování zásobníku pro chyby serveru. Používá DeveloperExceptionPageMiddleware se k zaznamenání synchronních a asynchronních výjimek z kanálu HTTP a k vygenerování chybových odpovědí. Představte si například následující akci kontroleru, která vyvolá výjimku:

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

Když stránka výjimky vývojáře zjistí neošetřenou výjimku, vygeneruje výchozí odpověď ve formátu prostého textu podobnou následujícímu příkladu:

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()

...

Pokud klient požádá o odpověď ve formátu HTML, stránka výjimky vývojáře vygeneruje odpověď podobnou následujícímu příkladu:

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

...

Pokud chcete požádat o odpověď ve formátu HTML, nastavte hlavičku Accept požadavku HTTP na text/html.

Upozorňující

Nepovolujte stránku výjimek vývojáře, pokud aplikace není spuštěná ve vývojovém prostředí. Nesdílejte podrobné informace o výjimce veřejně, když aplikace běží v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Obslužná rutina výjimky

V prostředích, která nejsou vývojová, použijte middleware zpracování výjimek k vytvoření datové části chyby:

  1. UseExceptionHandler Voláním Program.cspřidejte middleware zpracování výjimek:

    var app = builder.Build();
    
    app.UseHttpsRedirection();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error");
    }
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    
  2. Nakonfigurujte akci kontroleru, která bude reagovat na trasu /error :

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

Předchozí HandleError akce odešle klientovi datovou část kompatibilní s dokumentem RFC 7807.

Upozorňující

Neoznačí metodu akce obslužné rutiny chyby s atributy metody HTTP, například HttpGet. Explicitní příkazy brání některým požadavkům v dosažení metody akce.

U webových rozhraní API, která používají Swagger / OpenAPI, označte akci obslužné rutiny chyby atributem [ApiExplorer Nastavení] a nastavte jeho IgnoreApi vlastnost na true. Tato konfigurace atributu vylučuje akci obslužné rutiny chyby ze specifikace OpenAPI aplikace:

[ApiExplorerSettings(IgnoreApi = true)]

Povolte anonymní přístup k metodě, pokud by se měla zobrazit chyba neověřených uživatelů.

Middleware zpracování výjimek je možné použít také ve vývojovém prostředí k vytvoření konzistentního formátu datové části ve všech prostředích:

  1. Zaregistrujte Program.csinstance middlewaru specifické pro prostředí:

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

    V předchozím kódu je middleware zaregistrovaný v:

    • /error-development Trasa ve vývojovém prostředí.
    • Trasa /error v prostředích, která nejsou vývojová.

  2. Přidejte akce kontroleru pro trasy vývoje i pro jiné než vývojové trasy:

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

Úprava odpovědi pomocí výjimek

Obsah odpovědi lze upravit mimo kontroler pomocí vlastní výjimky a filtru akcí:

  1. Vytvořte známý typ výjimky s názvem 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. Vytvořte filtr akcí s názvem 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;
            }
        }
    }
    

    Předchozí filtr určuje Order maximální celočíselnou hodnotu minus 10. To Order umožňuje spuštění dalších filtrů na konci kanálu.

  3. Do Program.cskolekce filtrů přidejte filtr akcí:

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

Odpověď na chybu ověření

U řadičů webového rozhraní API MVC odpoví typem ValidationProblemDetails odpovědi, když se ověření modelu nezdaří. MVC používá výsledky InvalidModelStateResponseFactory k vytvoření chybové odpovědi pro selhání ověření. Následující příklad nahrazuje výchozí továrnu implementací, která také podporuje formátování odpovědí jako XML, v Program.cs:

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

Odpověď na chybu klienta

Výsledek chyby je definován jako výsledek se stavovým kódem HTTP 400 nebo vyšší. U řadičů webového rozhraní API MVC transformuje výsledek chyby tak, aby vytvořil ProblemDetails.

Automatické vytvoření stavových ProblemDetails kódů chyb je ve výchozím nastavení povolené, ale odpovědi na chyby je možné nakonfigurovat jedním z následujících způsobů:

  1. Použití služby podrobností o problému
  2. Implementace ProblemDetailsFactory
  3. Použití apiBehaviorOptions.ClientErrorMapping

Odpověď s výchozími podrobnostmi o problému

Program.cs Následující soubor vygenerovaly šablony webových aplikací pro kontrolery rozhraní API:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Vezměte v úvahu následující kontroler, který vrátí BadRequest , když je vstup neplatný:

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

Odpověď s podrobnostmi o problému se vygeneruje s předchozím kódem, pokud platí některá z následujících podmínek:

  • Koncový /api/values2/divide bod se volá s nulovým jmenovatelem.
  • Koncový /api/values2/squareroot bod se volá s paprsky menší než nula.

Výchozí text odpovědi s podrobnostmi o problému obsahuje následující typetitlehodnoty a status hodnoty:

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

Služba podrobností o problému

ASP.NET Core podporuje vytváření podrobností o problému pro rozhraní HTTP API pomocí rozhraní IProblemDetailsService. Další informace najdete ve službě Podrobnosti o problému.

Následující kód nakonfiguruje aplikaci tak, aby vygenerovala odpověď na podrobnosti o problému pro všechny odpovědi na chyby klienta HTTP a serveru, které ještě nemají základní obsah:

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

Vezměte v úvahu kontroler rozhraní API z předchozí části, který se vrátí BadRequest , když je vstup neplatný:

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

Odpověď s podrobnostmi o problému se vygeneruje s předchozím kódem, pokud platí některá z následujících podmínek:

  • Je zadán neplatný vstup.
  • Identifikátor URI nemá žádný odpovídající koncový bod.
  • Dojde k neošetřené výjimce.

Automatické vytvoření ProblemDetails pro stavové kódy kódů chyb je zakázáno, pokud je vlastnost SuppressMapClientErrors nastavená na true:

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

Při použití předchozího kódu se při návratu kontroleru rozhraní API vrátí BadRequeststav odpovědi HTTP 400 bez textu odpovědi. SuppressMapClientErrorsProblemDetails zabraňuje vytvoření odpovědi i při volání WriteAsync koncového bodu kontroleru rozhraní API. WriteAsync je vysvětleno dále v tomto článku.

V další části se dozvíte, jak přizpůsobit text odpovědi s podrobnostmi o problému pomocí příkazu CustomizeProblemDetails, aby se vrátila užitečnější odpověď. Další možnosti přizpůsobení najdete v tématu Přizpůsobení podrobností o problému.

Přizpůsobení podrobností o problému pomocí CustomizeProblemDetails

Následující kód používá ProblemDetailsOptions k nastavení CustomizeProblemDetails:

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

Aktualizovaný kontroler rozhraní API:

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

}

Následující kód obsahuje MathErrorFeature kód, MathErrorTypekterý se používá s předchozím vzorem:

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

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

Odpověď s podrobnostmi o problému se vygeneruje s předchozím kódem, pokud platí některá z následujících podmínek:

  • Koncový /divide bod se volá s nulovým jmenovatelem.
  • Koncový /squareroot bod se volá s paprsky menší než nula.
  • Identifikátor URI nemá žádný odpovídající koncový bod.

Text odpovědi s podrobnostmi o problému obsahuje následující informace, pokud je volán některý squareroot koncový bod s radikandou menší než nula:

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

Zobrazení nebo stažení ukázkového kódu

Implementovat ProblemDetailsFactory

MVC používá Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory k vytvoření všech instancí ProblemDetails a ValidationProblemDetails. Tato továrna se používá pro:

Pokud chcete přizpůsobit odpověď na podrobnosti problému, zaregistrujte vlastní implementaci ProblemDetailsFactory v Program.cs:

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

Použití ApiBehaviorOptions.ClientErrorMapping

ClientErrorMapping Pomocí vlastnosti můžete nakonfigurovat obsah ProblemDetails odpovědi. Například následující kód v Program.cs aktualizaci Link vlastnosti pro odpovědi 404:

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

Další materiály

Tento článek popisuje, jak zpracovávat chyby a přizpůsobit zpracování chyb pomocí webových rozhraní API ASP.NET Core.

Stránka výjimky pro vývojáře

Na stránce výjimky pro vývojáře se zobrazují podrobné trasování zásobníku pro chyby serveru. Používá DeveloperExceptionPageMiddleware se k zaznamenání synchronních a asynchronních výjimek z kanálu HTTP a k vygenerování chybových odpovědí. Představte si například následující akci kontroleru, která vyvolá výjimku:

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

Když stránka výjimky vývojáře zjistí neošetřenou výjimku, vygeneruje výchozí odpověď ve formátu prostého textu podobnou následujícímu příkladu:

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()

...

Pokud klient požádá o odpověď ve formátu HTML, stránka výjimky vývojáře vygeneruje odpověď podobnou následujícímu příkladu:

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

...

Pokud chcete požádat o odpověď ve formátu HTML, nastavte hlavičku Accept požadavku HTTP na text/html.

Upozorňující

Nepovolujte stránku výjimek vývojáře, pokud aplikace není spuštěná ve vývojovém prostředí. Nesdílejte podrobné informace o výjimce veřejně, když aplikace běží v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Obslužná rutina výjimky

V prostředích, která nejsou vývojová, použijte middleware zpracování výjimek k vytvoření datové části chyby:

  1. UseExceptionHandler Voláním Program.cspřidejte middleware zpracování výjimek:

    var app = builder.Build();
    
    app.UseHttpsRedirection();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error");
    }
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    
  2. Nakonfigurujte akci kontroleru, která bude reagovat na trasu /error :

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

Předchozí HandleError akce odešle klientovi datovou část kompatibilní s dokumentem RFC 7807.

Upozorňující

Neoznačí metodu akce obslužné rutiny chyby s atributy metody HTTP, například HttpGet. Explicitní příkazy brání některým požadavkům v dosažení metody akce.

U webových rozhraní API, která používají Swagger / OpenAPI, označte akci obslužné rutiny chyby atributem [ApiExplorer Nastavení] a nastavte jeho IgnoreApi vlastnost na true. Tato konfigurace atributu vylučuje akci obslužné rutiny chyby ze specifikace OpenAPI aplikace:

[ApiExplorerSettings(IgnoreApi = true)]

Povolte anonymní přístup k metodě, pokud by se měla zobrazit chyba neověřených uživatelů.

Middleware zpracování výjimek je možné použít také ve vývojovém prostředí k vytvoření konzistentního formátu datové části ve všech prostředích:

  1. Zaregistrujte Program.csinstance middlewaru specifické pro prostředí:

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

    V předchozím kódu je middleware zaregistrovaný v:

    • /error-development Trasa ve vývojovém prostředí.
    • Trasa /error v prostředích, která nejsou vývojová.

  2. Přidejte akce kontroleru pro trasy vývoje i pro jiné než vývojové trasy:

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

Úprava odpovědi pomocí výjimek

Obsah odpovědi lze upravit mimo kontroler pomocí vlastní výjimky a filtru akcí:

  1. Vytvořte známý typ výjimky s názvem 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. Vytvořte filtr akcí s názvem 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;
            }
        }
    }
    

    Předchozí filtr určuje Order maximální celočíselnou hodnotu minus 10. To Order umožňuje spuštění dalších filtrů na konci kanálu.

  3. Do Program.cskolekce filtrů přidejte filtr akcí:

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

Odpověď na chybu ověření

U řadičů webového rozhraní API MVC odpoví typem ValidationProblemDetails odpovědi, když se ověření modelu nezdaří. MVC používá výsledky InvalidModelStateResponseFactory k vytvoření chybové odpovědi pro selhání ověření. Následující příklad nahrazuje výchozí továrnu implementací, která také podporuje formátování odpovědí jako XML, v Program.cs:

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

Odpověď na chybu klienta

Výsledek chyby je definován jako výsledek se stavovým kódem HTTP 400 nebo vyšší. U řadičů webového rozhraní API MVC transformuje výsledek chyby tak, aby vytvořil ProblemDetails.

Odpověď na chybu je možné nakonfigurovat jedním z následujících způsobů:

  1. Implementace ProblemDetailsFactory
  2. Použití apiBehaviorOptions.ClientErrorMapping

Implementovat ProblemDetailsFactory

MVC používá Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory k vytvoření všech instancí ProblemDetails a ValidationProblemDetails. Tato továrna se používá pro:

Pokud chcete přizpůsobit odpověď na podrobnosti problému, zaregistrujte vlastní implementaci ProblemDetailsFactory v Program.cs:

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

Použití ApiBehaviorOptions.ClientErrorMapping

ClientErrorMapping Pomocí vlastnosti můžete nakonfigurovat obsah ProblemDetails odpovědi. Například následující kód v Program.cs aktualizaci Link vlastnosti pro odpovědi 404:

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

Vlastní middleware pro zpracování výjimek

Výchozí hodnoty v 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 zpracování výjimek.

Vytvoření datové části ProblemDetails pro výjimky

ASP.NET Core nevytváří standardizovanou datovou část chyby, když dojde k neošetřené výjimce. Ve scénářích, ve kterých je žádoucí vrátit standardizovanou odezvu ProblemDetails klientovi, lze middleware ProblemDetails použít k mapování výjimek a 404 odpovědí na datovou část ProblemDetails . Middleware pro zpracování výjimek se dá použít také k vrácení ProblemDetails datové části pro neošetřené výjimky.

Další materiály

Tento článek popisuje, jak zpracovávat a přizpůsobovat zpracování chyb pomocí webových rozhraní API ASP.NET Core.

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 pro získání podrobných trasování zásobníku pro chyby serveru. Používá DeveloperExceptionPageMiddleware se k zaznamenání synchronních a asynchronních výjimek z kanálu HTTP a k vygenerová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

Na stránce výjimky vývojáře se 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 Accept požadavku HTTP na text/html typ média. Příklad:

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

Podívejte se na následující výňatek 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 stane užitečnou při testování pomocí nástrojů, jako je Postman. Následující snímek obrazovky ukazuje prostý text i odpovědi ve formátu HTML v Nástroji Postman:

Test the Developer Exception Page in Postman.

Upozorňující

Povolte stránku výjimky vývojáře jenom v případech, kdy je aplikace spuštěná ve vývojovém prostředí. Nesdílejte podrobné informace o výjimce veřejně, když aplikace běží v produkčním prostředí. Další informace o konfiguraci prostředí najdete v tématu Použití více prostředí v ASP.NET Core.

Neoznačí metodu akce obslužné rutiny chyby s atributy metody HTTP, například HttpGet. Explicitní příkazy brání některým požadavkům v dosažení metody akce. Povolte anonymní přístup k metodě, pokud by se měla zobrazit chyba neověřených uživatelů.

Obslužná rutina výjimky

V prostředích, která nejsou vývojová, se dá middleware zpracování výjimek použít k vytvoření datové části chyby:

  1. V Startup.Configurepříkazu vyvolání UseExceptionHandler pro 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();
        });
    }
    
  2. Nakonfigurujte akci kontroleru, která bude reagovat na trasu /error :

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

Předchozí Error akce odešle klientovi datovou část kompatibilní s dokumentem RFC 7807.

Middleware zpracování výjimek může také poskytovat podrobnější výstup vyjednaný obsah v místním vývojovém prostředí. Pomocí následujících kroků vytvořte konzistentní formát datové části napříč vývojovými a produkčními prostředími:

  1. Zaregistrujte Startup.Configureinstance middlewaru specifické pro prostředí:

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

    V předchozím kódu je middleware zaregistrovaný v:

    • /error-local-development Trasa ve vývojovém prostředí.
    • Trasa /error v prostředích, která nejsou vývojová.

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

    Předchozí kód volá ControllerBase.Problem k vytvoření ProblemDetails odpovědi.

Úprava odpovědi pomocí výjimek

Obsah odpovědi lze upravit mimo kontroler. V ASP.NET webovém rozhraní API 4.x to bylo jedním ze způsobů, jak to udělat, byl HttpResponseException typ. ASP.NET Core neobsahuje ekvivalentní typ. Podporu můžete HttpResponseException přidat pomocí následujících kroků:

  1. 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; }
    }
    
  2. 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 Order maximální celočíselnou hodnotu minus 10. To Order umožňuje spuštění dalších filtrů na konci kanálu.

  3. Do Startup.ConfigureServiceskolekce filtrů přidejte filtr akcí:

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

Odpověď na chybu ověření

U řadičů webového rozhraní API MVC odpoví typem ValidationProblemDetails odpovědi, když se ověření modelu nezdaří. MVC používá výsledky InvalidModelStateResponseFactory k vytvoření chybové odpovědi pro selhání ověření. Následující příklad používá továrnu ke změně výchozího typu odpovědi na SerializableError hodnotu in Startup.ConfigureServices:

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

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

            return result;
        };
    });

Odpověď na chybu klienta

Výsledek chyby je definován jako výsledek se stavovým kódem HTTP 400 nebo vyšší. U řadičů webového rozhraní API MVC transformuje výsledek chyby na výsledek pomocí ProblemDetails.

Odpověď na chybu je možné nakonfigurovat jedním z následujících způsobů:

  1. Implementace ProblemDetailsFactory
  2. Použití apiBehaviorOptions.ClientErrorMapping

Implementovat ProblemDetailsFactory

MVC používá Microsoft.AspNetCore.Mvc.Infrastructure.ProblemDetailsFactory k vytvoření všech instancí ProblemDetails a ValidationProblemDetails. Tato továrna se používá pro:

Pokud chcete přizpůsobit odpověď na podrobnosti problému, zaregistrujte vlastní implementaci ProblemDetailsFactory v Startup.ConfigureServices:

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

Použití apiBehaviorOptions.ClientErrorMapping

ClientErrorMapping Pomocí vlastnosti můžete nakonfigurovat obsah ProblemDetails odpovědi. Například následující kód v Startup.ConfigureServices aktualizaci type vlastnosti pro odpovědi 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";
        options.DisableImplicitFromServicesParameters = true;
    });

Vlastní middleware pro zpracování výjimek

Výchozí hodnoty v 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 zpracování výjimek.

Vytvoření datové části ProblemDetails pro výjimky

ASP.NET Core nevytváří standardizovanou datovou část chyby, když dojde k neošetřené výjimce. Ve scénářích, ve kterých je žádoucí vrátit standardizovanou odezvu ProblemDetails klientovi, lze middleware ProblemDetails použít k mapování výjimek a 404 odpovědí na datovou část ProblemDetails . Middleware pro zpracování výjimek se dá použít také k vrácení ProblemDetails datové části pro neošetřené výjimky.