Убедитесь, что конкретное исключение всегда приводит к заданному коду HTTP-ответа - PullRequest
0 голосов
/ 28 июня 2018

Требование

У меня есть приложение ASP.Net MVC, которое работает с рядом различных библиотек. Как и в большинстве библиотек, различные вызовы функций могут вызывать одно из множества различных исключений.

В настоящее время, когда выдается какое-либо исключение, приложение MVC обрабатывает их и возвращает «внутреннюю ошибку сервера» (код 500) обратно клиенту (например, веб-браузер).

Это нормально для большинства случаев, однако, есть один конкретный тип исключения (в данном случае UnauthorizedAccessException), который я хотел бы привести к статусу «Несанкционированный» (код 401) вместо ответа обычная ошибка 500.

Текущая попытка

Я провел немало исследований, и похоже, что лучший способ «поймать» все исключения и обработать их с помощью метода Application_Error. Поэтому я попробовал следующую реализацию Application_Error в классе MvcApplication:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();

    if(ex is UnauthorizedAccessException)
    {
        Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
    }
}

Задача

Проблема здесь в том, что, хотя я могу отладить и увидеть, что Response.StatusCode устанавливается на 401, клиент / браузер все еще получает ошибку 500.

Я ожидаю, что есть очень веская причина, почему это происходит, но я исчерпал свой мозг, размышляя над поисковыми терминами, которые дадут мне нужный мне ответ.

Вопрос

Короче говоря, что мне нужно сделать, чтобы получить поведение, которое я ищу?


Для получения дополнительной информации, в конечном счете, я хочу, чтобы UnauthorizedAccessException работал так же, как MVC обрабатывает неаутентифицированные запросы (которые перенаправляют на страницу входа). Однако мне также нужно, чтобы он работал для запросов AJAX, так как мой javascript может проверять на наличие ошибки 401 и выполнять определенную логику (в этом случае перенаправление ответа на страницу входа в систему не работает)

1 Ответ

0 голосов
/ 28 июня 2018

Умный способ сделать это - создать базовый контроллер, который ваши контроллеры наследуют через контроллер по умолчанию. Там вы наследуете класс Controller по умолчанию и переопределяете метод OnException.

public abstract class BaseController : Controller
{
    protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {
        var responseCode = Response.StatusCode;
        var exception = filterContext.Exception;
        switch (exception.GetType().ToString())
        {
            case "UnauthorizedAccessException":
                responseCode = 401;
                filterContext.ExceptionHandled = true;
                break;
        }

        Response.StatusCode = responseCode;

        base.OnException(filterContext);
    }
}

Хитрость, которая заставляет его работать, это filterContext.ExceptionHandled = true;, если вы не установите это значение в true, сервер вернет 500.

Ваши контроллеры унаследуют BaseController;

public class UserController : BaseController
{
    public ActionResult Index(){
        return View();
    }
}

То, что вам нужно добавить к этому коду, - это ваше перенаправление на страницу входа в систему, к вашему методу OnException (при необходимости). Я бы сделал это для вас, но у меня нет достаточно времени, чтобы написать и протестировать его для вас ... в настоящее время ожидаю завершения автоматических тестов ..: -)

Edit:

Я не осознавал, что ваша точка зрения тоже может выдавать ошибки, которые, очевидно, не будут обрабатываться контроллером.

В этом случае мы можем вернуться к исходному методу Application_Error в Global.asax.

Нам нужны две строки кода ..

Response.StatusCode = 401;
Response.End();

Первая строка устанавливает код состояния на 401, На этом этапе вторая строка завершает выполнение и вызывает событие EndRequest, поэтому StatusCode не будет изменен на 500.

Если вы хотите прикрепить сообщение с вашим ответом:

Response.Write("Oops, you're not authorized...");

Было бы неплохо вызвать Response.Clear(); перед началом изменения объекта ответа в вашем обработчике ошибок.

...