Обработка ошибок

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 06.11.2019

Ошибки в приложении можно условно разделить на два типа: исключения, которые возникают в процессе выполнения кода (например, деление на 0), и стандартные ошибки протокола HTTP (например, ошибка 404).

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

UseDeveloperExceptionPage

Если мы создаем проект 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 симулируется генерация исключения при делении ноль. И если мы запустим проект, то в браузере мы увидим информацию об исключении:

Обработка исключений в ASP.NET Core

Этой информации достаточно, чтобы определить где именно в коде произошло исключение.

Теперь посмотрим, как все это будет выглядеть для простого пользователя. Для этого изменим метод 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"

HTTP ERROR 500 в ASP.NET Core

UseExceptionHandler

Это не самая лучшая ситуация, и нередко все-таки возникает необходимость дать пользователям некоторую информацию о том, что же все-таки произошло. Либо потребуется как-то обработать данную ситуацию. Для этих целей можно использовать еще один встроенный 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.

Error Handling in ASP.NET Core

Следует учитывать, что оба middleware - app.UseDeveloperExceptionPage() и app.UseExceptionHandler() следует помещать ближе к началу конвейера middleware.

Обработка ошибок HTTP

В отличие от исключений стандартный функционал проекта 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". При обращении ко всем остальным адресам браузер отобразит базовую информацию об ошибке:

UseStatusCodePages в ASP.NET Core

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

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

Еще один способ обработки кодов ошибок представляет собой определение и настройка в файле конфигурации web.config элемента httpErrors. Этот способ в принципе использовался и в других версиях ASP.NET. В ASP.NET Core он также доступен, однако имеет очень ограниченное действие. В частности, мы его можем использовать только при развертывании на IIS, а также не можем использовать ряд настроек.

Итак, добавим в корень проекта новый элемент Web Configurarion File, который естественно назовем web.config:

Обработка ошибок в 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.

Настройка обработки ошибок в web.config в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850