Могут ли раздел 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?


person votive    schedule 21.05.2014    source источник
comment
Здесь найдено простое решение: stackoverflow.com/questions/18858925/   -  person Leblanc Meneses    schedule 13.12.2014
comment
Я настоятельно рекомендую всем проверить ответ @LeblancMeneses, указанный выше.   -  person Luke    schedule 04.04.2017


Ответы (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) вызывающей стороне. Возможно, вы не захотите этого делать, если ваши сообщения об исключениях неудобны для пользователя или если клиент не знает, как интерпретировать эти сообщения.

person votive    schedule 02.06.2014
comment
Это не сработало для меня. Все еще не уверен, почему. Обработка исключений никогда не запускалась. Со вторым разберусь, я сейчас тороплюсь. Но это дало мне действительно хорошие идеи. Спасибо. - person celerno; 20.02.2015
comment
Я нашел этот ответ, который работал у меня без необходимости создавать дополнительные классы или дублировать страницы ошибок. stackoverflow.com/questions/18858925/ - person zemien; 23.03.2016
comment
@zemien Это работает, если вы хотите ВСЕГДА заменить сообщение об ошибке ASP.NET. Причина для existsResponse=Auto в этом ответе заключается в том, чтобы позволить CustomErrors работать, когда это применимо, и вернуться к конфигурации httpErrors только тогда, когда ASP.NET не возвращает собственное содержимое ошибки. - person Chris; 31.03.2016