Может ли существовать web.config раздел httpErrors и область WebAPI?

У меня есть проект ASP.net MVC 5, который содержит WebAPI в определенной области API. Я включил обработку ошибок IIS7 в свой web.config следующим образом:

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="400" subStatusCode="-1" />
        <remove statusCode="404" subStatusCode="-1" />
        <remove statusCode="500" subStatusCode="-1" />
        <error statusCode="400" path="400.html" responseMode="File" />
        <error statusCode="404" path="404.html" responseMode="File" />
        <error statusCode="500" path="500.html" responseMode="File" />
    </httpErrors>
</system.webServer>

Отображает дружественные сообщения пользователю веб-сайта MVC при 404/500 и т.д. Моя проблема возникает, когда конкретные (законные) коды статуса возвращаются из WebAPI (например, 400, когда вызывается `/api/token '). В этих случаях JSON-контент ответа перехватывается IIS, и мое дружеское сообщение HTML возвращается как ответ вместо исходного JSON из WebAPI. Можно ли исключить область API из обработки ошибок IIS? Если этого не может быть сделано, то каково правильное решение, позволяющее сосуществовать с веб-сайтами ASP.net MVC и ответами WebAPI JSON?

Ответ 1

После долгих чтений и экспериментов я нашел, что эта комбинация настроек работает эффективно:

Дружественные страницы ошибок

Один aspx и одна html-страница для каждого кода состояния ответа (здесь моя):

  • 404.aspx
  • 404.html
  • 500.aspx
  • 500.html

Единственное различие между двумя страницами состоит в том, что страницы aspx содержат следующие строки:

<% Response.StatusCode = 500 %>
<% Response.TrySkipIisCustomErrors = true %>

Первая строка отправляет правильный код статуса HTTP обратно клиенту, а вторая строка пытается убедить IIS, что ему не нужно обрабатывать сам ответ.

Настройка Web.config

Пользовательские ошибки должны быть on или remoteonly и указывать на файлы aspx:

<customErrors mode="On" defaultRedirect="500.aspx" redirectMode="ResponseRewrite">
  <error statusCode="404" redirect="404.aspx" />
</customErrors>

Собственные ошибки IIS также должны быть включены и указывать на html файлы в разделе system.webServer:

<httpErrors errorMode="Custom" existingResponse="Auto">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="404.html" responseMode="File" />
  <error statusCode="500" path="500.html" responseMode="File" />
</httpErrors>

existingResponse="Auto" сообщает IIS только возвращать страницы с дружественной ошибкой, если установлен флаг SetStatus. Эффективно это позволяет ASP.net отправлять обратно пользовательский ответ, собственную страницу ошибок из раздела customErrors или разрешить IIS возвращать настроенную дружественную страницу ошибок.

Настройка FilterConfig.cs

Проект ASP.net MVC/WebAPI по умолчанию настроен с фильтром HandleErrorAttribute, который обрабатывает исключения, вызванные действиями, и возвращает правильную настроенную страницу пользовательских ошибок. Я расширил этот класс для обработки исключений из действий WebAPI, исходя из этого класса:

filters.Add(new HandleExceptionAttribute());

HandleExceptionAttribute

public class HandleExceptionAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            filterContext.HttpContext.Response.StatusDescription = filterContext.Exception.Message;
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
        else
        {
            base.OnException(filterContext);
        }
    }
}

Этот класс обрабатывает исключения из действий WebAPI и возвращает сообщение об исключении в виде ответа JSON (с правильным статусом HTTP) вызывающему. Возможно, вы не захотите сделать это, если ваши сообщения об исключениях не являются дружественными пользователю или если клиент не знает, как интерпретировать эти сообщения.