Обработка ошибок в минимальных приложениях API

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 8 этой статьи.

С вкладом ДэвидА Акера

В этой статье описывается обработка ошибок в минимальных приложениях API. Сведения об обработке ошибок в API на основе контроллера см. в разделе "Обработка ошибок в ASP.NET Core и обработка ошибок в веб-API на основе контроллера ASP.NET core".

Исключения

В приложении API "Минимальный" существует два встроенных централизованных механизма для обработки необработанных исключений:

В этом разделе приведен пример приложения, демонстрирующий способы обработки исключений в минимальном API. Он создает исключение при запросе конечной точки /exception :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Страница со сведениями об исключении для разработчика

Страница исключений для разработчика содержит подробные сведения о необработанных исключениях запросов. Он использует DeveloperExceptionPageMiddleware для записи синхронных и асинхронных исключений из конвейера HTTP и для создания ответов об ошибках. Страница со сведениями об исключении для разработчика запускается в начале конвейера ПО промежуточного слоя, чтобы можно было перехватывать необработанные исключения, вызываемые в последующем ПО промежуточного слоя.

Приложения ASP.NET Core по умолчанию включают страницу со сведениями об исключении при выполнении всех следующих условий:

Приложения, созданные с помощью WebHost.CreateDefaultBuilderпредыдущих шаблонов, могут включить страницу исключения разработчика, вызвав вызов app.UseDeveloperExceptionPage.

Предупреждение

Не включите страницу исключений разработчика, если приложение не запущено в среде разработки. Не делитесь подробными сведениями об исключениях публично при запуске приложения в рабочей среде. Дополнительные сведения о настройке сред см. в статье Использование нескольких сред в ASP.NET Core.

Страница исключений для разработчика может содержать следующие сведения об исключении и запросе:

  • Трассировка стека
  • параметры строки запроса (при наличии);
  • Файлы cookie, если таковые есть
  • Заголовки
  • Метаданные конечной точки, если таковые есть

Страница со сведениями об исключении для разработчика не гарантирует предоставление каких-либо сведений. Используйте Ведение журнала для получения полных сведений об ошибке.

На следующем рисунке показана пример страницы исключений разработчика с анимацией для отображения вкладок и отображаемых сведений:

Страница исключений разработчика, анимированная для отображения каждой выбранной вкладки.

В ответ на запрос с заголовком страница исключений разработчика возвращает обычный Accept: text/plain текст вместо HTML. Например:

Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
   at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f

Чтобы просмотреть страницу исключений разработчика, выполните следующие действия.

  • Запустите пример приложения в среде разработки.
  • Перейдите к конечной точке /exception .

Обработчик исключений

В средах, отличных от разработки, используйте ПО промежуточного слоя обработчика исключений для создания полезных данных об ошибке. Чтобы настроить Exception Handler Middleware, вызовите UseExceptionHandler.

Например, следующий код изменяет приложение для реагирования на полезные данные, совместимые с RFC 7807, на клиент. Дополнительные сведения см . в разделе "Сведения о проблеме" далее в этой статье.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Ответы на ошибки клиента и сервера

Рассмотрим следующее минимальное приложение API.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

/users Конечная точка создает 200 OK представление json о том, User когда id больше 0, в противном случае 400 BAD REQUEST код состояния без текста ответа. Дополнительные сведения о создании ответа см. в статье "Создание ответов" в приложениях API "Минимальный".

Его Status Code Pages middleware можно настроить для создания общего содержимого текста, если пусто для всех ответов HTTP-клиента (499-400) или сервера.500 -599 ПО промежуточного слоя настраивается путем вызова метода расширения UseStatusCodePages .

Например, в следующем примере приложение изменяет ответ на полезные данные, совместимые с RFC 7807, для всех ответов клиента и сервера, включая ошибки маршрутизации (например, 404 NOT FOUND). Дополнительные сведения см. в разделе "Сведения о проблеме".

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    => await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Сведения о проблеме

Сведения о проблеме — это не единственный формат ответа, описывающий ошибку API HTTP, однако они часто используются для сообщения об ошибках для API HTTP.

Служба сведений о проблеме IProblemDetailsService реализует интерфейс, который поддерживает создание сведений о проблеме в ASP.NET Core. Метод AddProblemDetails(IServiceCollection) расширения для IServiceCollection регистрации реализации по умолчанию IProblemDetailsService .

В приложениях ASP.NET Core следующий по промежуточному слоя создает ответы HTTP для получения сведений о проблемах при AddProblemDetails вызове, за исключением случаев, когда Accept заголовок HTTP запроса не включает один из типов контента, поддерживаемых зарегистрированным IProblemDetailsWriter (по умолчанию: application/json):

  • ExceptionHandlerMiddleware: создает ответ сведений о проблеме, когда настраиваемый обработчик не определен.
  • StatusCodePagesMiddleware: создает ответ сведений о проблеме по умолчанию.
  • DeveloperExceptionPageMiddleware: создает ответ сведений о проблеме в разработке, если Accept заголовок HTTP запроса не включает text/html.

Минимальные приложения API можно настроить для создания ответа сведений о проблеме для всех ответов об ошибках HTTP-клиента и сервера, которые еще не содержат содержимого AddProblemDetails текста с помощью метода расширения.

Следующий код настраивает приложение для создания сведений о проблеме:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

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

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Дополнительные сведения об использовании AddProblemDetailsсм. в разделе "Сведения о проблеме"

Резервная часть IProblemDetailsService

В следующем коде возвращает ошибку, httpContext.Response.WriteAsync("Fallback: An error occurred.") если IProblemDetailsService реализация не может создать ProblemDetails:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Предыдущий код:

  • Записывает сообщение об ошибке с резервным кодом, если не problemDetailsService удается написать сообщение об ошибке ProblemDetails. Например, конечная точка, в которой заголовок запроса Accept указывает тип носителя, который DefaulProblemDetailsWriter не поддерживается.
  • Использует ПО промежуточного слоя обработчика исключений.

Следующий пример аналогичен предыдущему, за исключением того, что вызывается .Status Code Pages middleware

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseStatusCodePages(statusCodeHandlerApp =>
{
    statusCodeHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/users/{id:int}", (int id) =>
{
    return id <= 0 ? Results.BadRequest() : Results.Ok(new User(id));
});

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

В этой статье описывается обработка ошибок в минимальных приложениях API.

Исключения

В приложении API "Минимальный" существует два встроенных централизованных механизма для обработки необработанных исключений:

В этом разделе описывается следующее приложение API с минимальным количеством способов обработки исключений. Он создает исключение при запросе конечной точки /exception :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Страница со сведениями об исключении для разработчика

На странице исключений разработчика отображаются подробные трассировки стека для ошибок сервера. Он использует DeveloperExceptionPageMiddleware для записи синхронных и асинхронных исключений из конвейера HTTP и для создания ответов об ошибках.

Приложения ASP.NET Core по умолчанию включают страницу со сведениями об исключении при выполнении всех следующих условий:

Дополнительные сведения о настройке ПО промежуточного слоя см. в разделе "По промежуточного слоя" в минимальных приложениях API.

Developer Exception Page При обнаружении необработанного исключения при обнаружении необработанного исключения создается ответ обычного текста по умолчанию, аналогичный следующему примеру:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 18:00:59 GMT
Server: Kestrel
Transfer-Encoding: chunked

    System.InvalidOperationException: Sample Exception
    at Program.<>c.<<Main>$>b__0_1() in ....:line 17
    at lambda_method2(Closure, Object, HttpContext)
    at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5239
Accept-Encoding: gzip, deflate, br

Предупреждение

Не включите страницу исключений разработчика, если приложение не запущено в среде разработки. Не делитесь подробными сведениями об исключениях публично при запуске приложения в рабочей среде. Дополнительные сведения о настройке сред см. в статье Использование нескольких сред в ASP.NET Core.

Обработчик исключений

В средах, отличных от разработки, используйте ПО промежуточного слоя обработчика исключений для создания полезных данных об ошибке. Чтобы настроить Exception Handler Middleware, вызовите UseExceptionHandler.

Например, следующий код изменяет приложение для реагирования на полезные данные, совместимые с RFC 7807, на клиент. Дополнительные сведения см. в разделе "Сведения о проблеме".

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Ответы на ошибки клиента и сервера

Рассмотрим следующее минимальное приложение API.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Run();

public record User(int Id);

/users Конечная точка создает 200 OK представление json о том, User когда id больше 0, в противном случае 400 BAD REQUEST код состояния без текста ответа. Дополнительные сведения о создании ответа см. в статье "Создание ответов" в приложениях API "Минимальный".

Его Status Code Pages middleware можно настроить для создания общего содержимого текста, если пусто для всех ответов HTTP-клиента (499-400) или сервера.500 -599 ПО промежуточного слоя настраивается путем вызова метода расширения UseStatusCodePages .

Например, в следующем примере приложение изменяет ответ на полезные данные, совместимые с RFC 7807, для всех ответов клиента и сервера, включая ошибки маршрутизации (например, 404 NOT FOUND). Дополнительные сведения см. в разделе "Сведения о проблеме".

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    =>  await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Run();

public record User(int Id);

Сведения о проблеме

Сведения о проблеме — это не единственный формат ответа, описывающий ошибку API HTTP, однако они часто используются для сообщения об ошибках для API HTTP.

Служба сведений о проблеме IProblemDetailsService реализует интерфейс, который поддерживает создание сведений о проблеме в ASP.NET Core. Метод AddProblemDetails(IServiceCollection) расширения для IServiceCollection регистрации реализации по умолчанию IProblemDetailsService .

В приложениях ASP.NET Core следующий по промежуточному слоя создает ответы HTTP для получения сведений о проблемах при AddProblemDetails вызове, за исключением случаев, когда Accept заголовок HTTP запроса не включает один из типов контента, поддерживаемых зарегистрированным IProblemDetailsWriter (по умолчанию: application/json):

  • ExceptionHandlerMiddleware: создает ответ сведений о проблеме, когда настраиваемый обработчик не определен.
  • StatusCodePagesMiddleware: создает ответ сведений о проблеме по умолчанию.
  • DeveloperExceptionPageMiddleware: создает ответ сведений о проблеме в разработке, если Accept заголовок HTTP запроса не включает text/html.

Минимальные приложения API можно настроить для создания ответа сведений о проблеме для всех ответов на ошибки HTTP-клиента и сервера, которые еще не содержат содержимое текста с помощью AddProblemDetails метода расширения.

Следующий код настраивает приложение для создания сведений о проблеме:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

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

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Дополнительные сведения об использовании AddProblemDetailsсм. в разделе "Сведения о проблеме"