Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Ошибки в приложении можно условно разделить на два типа: исключения, которые возникают в процессе выполнения кода (например, деление на 0), и стандартные ошибки протокола HTTP (например, ошибка 404).
Обычные исключения могут быть полезны для разработчика в процессе создания приложения, но простые пользователи не должны будут их видеть.
Если мы создаем проект ASP.NET Core, например, по типу Empty (да и в других типах проектов), то в классе Startup мы можем найти в начале метода Configure()
следующие строки:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
Если приложение находится в состоянии разработки, то с помощью middleware app.UseDeveloperExceptionPage()
приложение перехватывает исключения и
выводит информацию о них разработчику.
Например, изменим класс Startup следующим образом:
public class Startup { public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { int x = 0; int y = 8 / x; await context.Response.WriteAsync($"Result = {y}"); }); } }
В middleware app.Run симулируется генерация исключения при делении ноль. И если мы запустим проект, то в браузере мы увидим информацию об исключении:
Этой информации достаточно, чтобы определить где именно в коде произошло исключение.
Теперь посмотрим, как все это будет выглядеть для простого пользователя. Для этого изменим метод Configure:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { env.EnvironmentName = "Production"; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { int x = 0; int y = 8 / x; await context.Response.WriteAsync($"Result = {y}"); }); }
Выражение env.EnvironmentName = "Production";
устанавливает режим развертывания вместо режима разработки. В этом случае выражение if (env.IsDevelopment())
будет возвращать false, и мы увидим в браузере что-то наподобие "HTTP ERROR 500"
Это не самая лучшая ситуация, и нередко все-таки возникает необходимость дать пользователям некоторую информацию о том, что же все-таки произошло. Либо потребуется как-то обработать данную ситуацию. Для этих целей можно использовать еще один встроенный middleware в виде метода UseExceptionHandler(). Он перенаправляет при возникновении исключения на некоторый адрес и позволяет обработать исключение. Например, изменим метод Configure следующим образом:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { env.EnvironmentName = "Production"; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/error"); } app.Map("/error", ap => ap.Run(async context => { await context.Response.WriteAsync("DivideByZeroException occured!"); })); app.Run(async (context) => { int x = 0; int y = 8 / x; await context.Response.WriteAsync($"Result = {y}"); }); }
Метод app.UseExceptionHandler("/error");
перенаправляет при возникновении ошибки на адрес "/error".
Для обработки пути по определенному адресу здесь использовался метод app.Map()
. В итоге при возникновении исключения будет срабатывать делегат
из метода app.Map.
Следует учитывать, что оба middleware - app.UseDeveloperExceptionPage()
и app.UseExceptionHandler()
следует помещать ближе к началу конвейера middleware.
В отличие от исключений стандартный функционал проекта ASP.NET Core почти никак не обрабатывает ошибки HTTP, например, в случае если ресурс не найден.
При обращении к несуществующему ресурсу мы увидим в браузере пустую страницу, и только через консоль веб-браузера мы сможем увидеть статусный код.
Но с помощью компонента StatusCodePagesMiddleware можно добавить в проект отправку информации о статусном коде.
Для этого добавим в метод Configure()
класса Startup вызов app.UseStatusCodePages()
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // обработка ошибок HTTP app.UseStatusCodePages(); app.Map("/hello", ap => ap.Run(async (context) => { await context.Response.WriteAsync($"Hello ASP.NET Core"); })); }
Здесь мы можем обращаться только по адресу "/hello". При обращении ко всем остальным адресам браузер отобразит базовую информацию об ошибке:
Данный метод позволяет настроить отправляемое пользователю сообщение. В частности, мы можем изменить вызов метода так:
app.UseStatusCodePages("text/plain", "Error. Status code : {0}");
В качестве первого параметра указывается MIME-тип ответа, а в качестве второго - собственно то сообщение, которое увидит пользователь. В сообщение мы можем передать код ошибки через плейсхолдер "{0}".
Вместо метода app.UseStatusCodePages()
мы также можем использовать еще пару других, которые также обрабатываю ошибки HTTP.
С помощью метода app.UseStatusCodePagesWithRedirects()
можно выполнить переадресацию на определенный метод, который непосредственно обработает статусный код:
app.UseStatusCodePagesWithRedirects("/error?code={0}");
Здесь будет идти перенаправление по адресу "/error?code={0}". В качестве параметра через плейсхолдер "{0}" будет передаваться статусный код ошибки.
Но теперь при обращении к несуществующему ресурсу клиент получит статусный код 302 / Found. То есть формально несуществующий ресурс будет существовать, просто статусный код 302 будет указывать, что ресурс перемещен на другое место - по пути "/error/404".
Подобное поведение может быть неудобно, особенно с точки зрения поисковой индексации, и в этом случае мы можем применить другой метод app.UseStatusCodePagesWithReExecute():
app.UseStatusCodePagesWithReExecute("/error", "?code={0}");
Первый параметр метода указывает на путь перенаправления, а второй задает параметры строки запроса, которые будут передаваться при перенаправлении. Вместо плейсхолдера {0} опять же будет передаваться статусный код ошибки. Формально мы получим тот же ответ, так как так же будет идти перенаправление на путь "/error?code=404". Но теперь браузер получит оригинальный статусный код 404.
Пример использования:
public void Configure(IApplicationBuilder app) { // обработка ошибок HTTP app.UseStatusCodePagesWithReExecute("/error", "?code={0}"); app.Map("/error", ap => ap.Run(async context => { await context.Response.WriteAsync($"Err: {context.Request.Query["code"]}"); })); app.Map("/hello", ap => ap.Run(async (context) => { await context.Response.WriteAsync($"Hello ASP.NET Core"); })); }
Еще один способ обработки кодов ошибок представляет собой определение и настройка в файле конфигурации web.config элемента httpErrors. Этот способ в принципе использовался и в других версиях ASP.NET. В ASP.NET Core он также доступен, однако имеет очень ограниченное действие. В частности, мы его можем использовать только при развертывании на IIS, а также не можем использовать ряд настроек.
Итак, добавим в корень проекта новый элемент Web Configurarion File, который естественно назовем web.config:
Изменим его следующим образом:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <httpErrors errorMode="Custom" existingResponse="Replace"> <remove statusCode="404"/> <remove statusCode="403"/> <error statusCode="404" path="404.html" responseMode="File"/> <error statusCode="403" path="403.html" responseMode="File"/> </httpErrors> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> </handlers> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> </system.webServer> </configuration>
Также для обработки ошибок добавим в корень проекта новый файл 404.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Ошибка 404</title> </head> <body> <h1>Ошибка 404</h1> <h2>Ресурс не найден!</h2> </body> </html>
По аналогии можно добавить файл 403.html для ошибки 403.
Итак, элемент httpErrors имеет ряд настроек. Для тестирования настроек локально, необходимо установить атрибут errorMode="Custom"
.
Если тестирование необязательно, и приложение уже развернуто для использования, то можно установить значение errorMode="DetailedLocalOnly"
.
Значение existingResponse="Replace"
позволит отобразить ошибку по оригинальному запрошенному пути без переадресации.
Внутри элемента httpErrors с помощью отдельных элементов error устанавливается обработка ошибок. Атрибут statusCode
задает статусный код, атрибут path
- адрес url, который будет вызываться, а атрибут responseMode
указывает, как будет обрабатываться ответ вызванному url.
Атрибут responseMode
имеет значение File
, что позволяет рассматривать адрес url из атрибута path как статическую страницу и использовать ее в качестве ответа
Настройки элемента httpErrors
могут наследоваться с других уровней, например, от файла конфигурации machine.config
. И чтобы удалить
все унаследованные настройки, применяется элемент <clear />
. Чтобы удалить настройки для отдельных ошибок, применяется элемент
<remove />
.
Для тестирования используем следующий класс Startup:
public class Startup { public void Configure(IApplicationBuilder app) { app.Map("/hello", ap => ap.Run(async (context) => { await context.Response.WriteAsync($"Hello ASP.NET Core"); })); } }
И после обращения к несуществующему ресурсу в приложении отобразится содержимое из файла 404.html.