Код ответа 401 для запросов json с ASP.NET MVC

Как отключить стандартную обработку кода ответа 401 в ASP.NET (перенаправление на страницу входа) для запросов AJAX / JSON?

Для веб-страниц это нормально, но для AJAX мне нужно получить правильный код ошибки 401 вместо красивого 302/200 для страницы входа.

Обновление: есть несколько решений от Фила Хаака, премьер-министра ASP.NET MVC - http://haacked.com/archive/2011/10/04/prevent-forms-authentication-login-page-redirect-when-you-donrsquot-want.aspx


person derigel    schedule 23.09.2008    source источник


Ответы (7)


Среда выполнения ASP.NET разработана таким образом, что она всегда будет перенаправлять пользователя, если HttpResponse.StatusCode имеет значение 401, но только если найден раздел <authentication /> файла Web.config.

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

person Troels Thomsen    schedule 24.09.2008
comment
Лучший способ реализовать собственное перенаправление - создать подкласс AuthorizeAttribute, а при отсутствии авторизации установить результат в RedirectResult вместо HttpUnauthorizedResult. - person Joseph Daigle; 07.01.2010
comment
Да, но в этом случае OP хотел бы отправить HTTP-код 401, но не перенаправлять (для правильной работы с JSON). - person Troels Thomsen; 08.01.2010
comment
У меня работает - хотя я настроил настройку как ‹authentication mode = None /› вместо того, чтобы полностью удалить атрибут. - person Timothy Lee Russell; 15.06.2013
comment
Гм, за исключением того, что я использую проверку подлинности с помощью форм в контроллерах MVC, так что на самом деле это не работает для меня. Требуется решение для аутентификации 404 && Forms, а не либо ||. - person Timothy Lee Russell; 18.06.2013
comment
Что делать, если вы хотите использовать аутентификацию с помощью форм и настроить перенаправление 401? - person DevDave; 29.08.2013
comment
@DevDave - Смотри мой ответ. - person Timothy Lee Russell; 30.08.2013

В классическом ASP.NET вы получаете код ответа HTTP 401 при вызове WebMethod с помощью Ajax. Я надеюсь, что они изменят его в будущих версиях ASP.NET MVC. Прямо сейчас я использую этот хак:

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
    {
        Context.Response.Clear();
        Context.Response.StatusCode = 401;
    }
}
person Catalin DICU    schedule 02.07.2009
comment
Это действительно хорошее временное решение, если вам нужно что-то быстро работающее. В противном случае я бы попытался реализовать ответ @troethom. - person Joseph Daigle; 07.01.2010

Мне нужна была проверка подлинности с помощью форм и возврат 401 для запросов Ajax, которые не прошли проверку подлинности.

В конце концов, я создал настраиваемый атрибут AuthorizeAttribute и украсил методы контроллера. (Это в .Net 4.5)

//web.config

<authentication mode="Forms">
</authentication>

// контроллер

[Authorize(Roles = "Administrator,User"), Response302to401]
[AcceptVerbs("Get")]
public async Task<JsonResult> GetDocuments()
{
    string requestUri = User.Identity.Name.ToLower() + "/document";
    RequestKeyHttpClient<IEnumerable<DocumentModel>, string> client =
        new RequestKeyHttpClient<IEnumerable<DocumentModel>, string>(requestUri);

    var documents = await client.GetManyAsync<IEnumerable<DocumentModel>>();

    return Json(documents, JsonRequestBehavior.AllowGet);
}

// authorizeAttribute

public class Response302to401 : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.Result = new JsonResult
                {
                    Data = new { Message = "Your session has died a terrible and gruesome death" },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
                filterContext.HttpContext.Response.StatusCode = 401;
                filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate";
                filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
            }
        }
        //base.HandleUnauthorizedRequest(filterContext);
    }
}
person Timothy Lee Russell    schedule 18.06.2013
comment
что, если я хочу использовать стандартные формы для авторизации перенаправления, когда пользователь не вошел в систему, а также для вызова пользовательских перенаправлений 401, когда у пользователя нет соответствующего разрешения, какие-либо идеи? - person DevDave; 30.08.2013
comment
Вы получите обычное перенаправление форм для методов, у которых нет этого атрибута AuthorizeAttribute. Вы также можете просто расширить этот атрибут, предоставив условие else для if (! FilterContext.IsAuthenticated), в котором вы должны проверить разрешения, а затем самостоятельно настроить перенаправление. - person Timothy Lee Russell; 30.08.2013

Вы также можете использовать Global.asax, чтобы прервать этот процесс примерно так:

    protected void Application_PreSendRequestHeaders(object sender, EventArgs e) {
        if (Response.StatusCode == 401) {
            Response.Clear();
            Response.Redirect(Response.ApplyAppPathModifier("~/Login.aspx"));
            return;
        }
    }
person Jared    schedule 22.11.2008
comment
Я сделал это, за исключением того, что проверил 302 и X-Requested-With в ответе от @troethom выше. - person Marc; 09.09.2010

Я не вижу, что нам нужно изменить режим аутентификации или тег аутентификации, как говорится в текущем ответе.

Следуя идее @TimothyLeeRussell (кстати, спасибо), я создал настраиваемый атрибут Authorize (проблема с одним из @TimothyLeeRussell заключается в том, что генерируется исключение, потому что он пытается изменить filterContext.Result an, который генерирует HttpException, и удалив эту часть, помимо filterContext.HttpContext.Response.StatusCode = 401, код ответа всегда был 200 OK). Итак, я наконец решил проблему, закрыв ответ после изменений.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class BetterAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            //Set the response status code to 500
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate";
            filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

            filterContext.HttpContext.Response.End();
        }
        else
            base.HandleUnauthorizedRequest(filterContext);
    }
}
person Sebastián Rojas    schedule 19.12.2014
comment
свойство SuppressFormsAuthenticationRedirect было именно тем, что я искал. - person zeocrash; 14.07.2015

Вы можете вызвать этот метод внутри своего действия,

 HttpContext.Response.End();

Пример

public async Task<JsonResult> Return401()
{
    HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
    HttpContext.Response.End();
    return Json("Unauthorized", JsonRequestBehavior.AllowGet);
}

Из MSDN: метод End вызывает веб-сервер, чтобы остановить обработку сценария и вернуть текущий результат. Остальное содержимое файла не обрабатывается.

person valter.santos.matos    schedule 31.03.2015

Вы можете создать собственный FilterAttribute, реализующий IAuthorizationFilter интерфейс.

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

Более того, вы можете просто переопределить OnAuthorization класса AuthorizeAttribute, чтобы не изобретать велосипед. Добавьте логику, о которой я упоминал выше, и перехватите, если filterContext.Cancel истинно (filterContext.Result будет установлен в экземпляр класса HttpUnauthorizedResult.

Подробнее о «Фильтры в ASP.NET MVC CodePlex Preview 4» в блоге Фила Хакса. Это также относится к последнему превью.

person Troels Thomsen    schedule 24.09.2008
comment
Но я все еще не могу получить код состояния ответа 401 для запроса AJAX. Даже если я настроил ответ в фильтре авторизации. Поскольку инфраструктура ASP.NET, которая улавливает ответ 401 и перенаправляет на страницу входа, находится поверх ASP.NET MVC. Сообщать, что пользователь не вошел в журнал по пустому результату JSON, не очень правильно ... - person derigel; 24.09.2008
comment
До сих пор я не совсем понимал, что вы имели в виду. Я добавляю новый ответ, который решит вашу проблему. - person Troels Thomsen; 25.09.2008