Поделиться через


Обработка ошибок в API ASP.NET Core

Замечание

Это не последняя версия этой статьи. См. версию этой статьи для .NET 10, чтобы узнать о текущем выпуске.

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

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

  • Минимальные API
  • Контроллеры

В этой статье описывается обработка ошибок в ASP.NET Core API. Выбрана документация по минимальным API. Чтобы просмотреть документацию по API на основе контроллера, перейдите на вкладку Controllers. Инструкции по обработке ошибок Blazor см. в разделе об ошибках Handle в приложениях ASP.NET Core Blazor.

Страница исключений разработчика

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

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

  • Запуск в среде.
  • Приложение было создано с использованием текущих шаблонов.

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

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

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

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

  • Трассировка стека
  • Параметры строки запроса, если таковые есть
  • Файлы cookie, если таковые есть
  • Headers
  • Метаданные конечной точки, если таковые есть

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

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

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

В ответ на запрос с заголовком страница исключений разработчика возвращает обычный текст вместо 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
  • Минимальные API
  • Контроллеры

Чтобы просмотреть страницу исключений разработчика в минимальном API:

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

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

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

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

В средах, отличных от разработки, используйте Middleware обработчика исключений для формирования пакета данных об ошибке.

  • Минимальные API
  • Контроллеры

Чтобы настроить , вызовите . Например, следующий код изменяет приложение для того, чтобы оно отвечало клиенту данными, совместимыми с 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
  • Контроллеры

Рассмотрим следующее минимальное приложение 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);

Конечная точка создает представление о том, когда больше , в противном случае код состояния без текста ответа. Дополнительные сведения о создании ответа см. в разделе "Создание ответов" в приложениях на основе минимального API.

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

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

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. Метод расширения для регистрации реализации по умолчанию .

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

  • создаёт ответ с деталями проблемы, когда пользовательский обработчик не определён.
  • создает ответ с подробностями о проблеме по умолчанию.
  • создает ответ с деталями проблемы во время разработки, если заголовок HTTP запроса не содержит ...
  • Минимальные API
  • Контроллеры

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

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

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

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

Резервный IProblemDetailsService

В следующем коде возвращает ошибку, если реализация не может создать :

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

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

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

Замечание

Поддерживает следующие типы носителей в заголовке запроса:

  • application/json
  • application/problem+json
  • Подстановочные знаки, такие как и

Типы носителей, отличные от JSON, например или , не поддерживаются и запускают резервное поведение.

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

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 на основе контроллера на минимальные API:

  1. Замена фильтров действий фильтрами конечных точек или ПО промежуточного слоя
  2. Замена проверки модели ручной проверкой или пользовательской привязкой
  3. Замена фильтров исключений на промежуточное программное обеспечение для обработки исключений
  4. Настройка сведений о проблеме с использованием для согласованных ответов об ошибках

Когда следует использовать обработку ошибок на основе контроллера

При необходимости рассмотрите api на основе контроллера:

  • Сложные сценарии проверки моделей
  • Централизованная обработка исключений между несколькими контроллерами
  • Точное управление форматированием ответа на ошибки
  • Интеграция с функциями MVC, такими как фильтры и соглашения

Подробные сведения об обработке ошибок на основе контроллера, включая ошибки проверки, настройку сведений о проблеме и фильтры исключений, см. в разделах вкладки "Контроллеры ".

Дополнительные ресурсы