Веб-API ASP.NET: правильный способ вернуть ответ 401 / неавторизованный


99

У меня есть сайт MVC webapi, который использует аутентификацию OAuth / токена для аутентификации запросов. Все соответствующие контроллеры имеют правильные атрибуты, и аутентификация работает нормально.

Проблема в том, что не весь запрос может быть авторизован в области действия атрибута - некоторые проверки авторизации должны выполняться в коде, который вызывается методами контроллера - каков правильный способ вернуть несанкционированный ответ 401 в этом случае?

Я пробовал throw new HttpException(401, "Unauthorized access");, но когда я это делаю, код состояния ответа - 500, и я также получаю трассировку стека. Даже в нашем журнале DelegatingHandler мы видим, что ответ - 500, а не 401.


1
Каждому, кто подберет этот ответ, я предлагаю подумать о подходящем времени, чтобы бросить, а не о том, HttpResponseExceptionкогда возвращать Unauthorized(). Использование исключения для «ожидаемой» ошибки - это своего рода антишаблон, поэтому, если есть случаи, когда вы ожидаете, что вызов совершит эту ошибку, возврат Unauthorized(), вероятно, будет правильным вызовом. За исключением HttpResponseExceptionдействительно неожиданного.
— Rikki

См. Github.com/aspnet/Mvc/issues/5507 для обсуждения.
— Rikki

@Rikki, ошибка 401 - не «ожидаемая». - Это исключительное обстоятельство, которое должно заставить вас прервать рабочий процесс (за исключением, возможно, ведения журнала, который вы уже должны делать для любого исключения ...) - В любом случае, если вы хотите вернуть строгий типизированный результат от вашего контроллера ( например, для простоты модульного тестирования), исключение явно является лучшим путем.
— BrainSlugs83

Ответы:


145

Вы должны выбросить HttpResponseExceptionиз своего метода API, а не HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Или, если вы хотите предоставить собственное сообщение:

var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "Oops!!!" };
throw new HttpResponseException(msg);

97

Просто верните следующее:

return Unauthorized();

3
Я думаю, что принятые ответы на вопрос ОП конкретно. Мой ответ отвечает на заголовок вопроса «Веб-API ASP.NET: правильный способ вернуть ответ 401 / неавторизованный»
— JohnWrensby

3
Кто-нибудь знает, почему нет перегруженной версии этого с сообщением?
— Simon_Weaver 09

5
@Simon_Weaver Понятия не имею, почему, но вы можете использовать return Content<string>(HttpStatusCode.Unauthorized, "Message");для этого.
— Rikki

2
Это должен быть правильный ответ. 1 это правильно. 2) Если это изменится в более поздних версиях, вам не нужно менять код. 3) Вам не нужно указывать причину 401. Это должно обрабатываться клиентом, а не сервером.
— Ник Тернер

1
В какой это библиотеке?
— Nae

19

В качестве альтернативы другим ответам вы также можете использовать этот код, если хотите вернуть IActionResultв контроллере ASP.NET.

ASP.NET

 return Content(HttpStatusCode.Unauthorized, "My error message");

Обновление: ASP.NET Core

Приведенный выше код не работает в ASP.NET Core, вместо этого вы можете использовать один из них:

 return StatusCode((int)System.Net.HttpStatusCode.Unauthorized, "My error message");
 return StatusCode(401, "My error message");

Очевидно, фраза причины довольно необязательна ( может ли HTTP-ответ опустить фразу причины? )


1
Это больше не работает в ASP.NET Core, ControllerBaseкласс (используемый ASP.NET Core WebAPI) больше не имеет Contentперегрузки, которая принимает код состояния HTTP.
— Дай

Это не верно. Ответ Content имеет статус 200 Ok. Сервер должен отправить 401, и клиент должен обработать его соответствующим образом. Вы не можете отправить 200 как 401. Это не имеет смысла. Если клиент получает ошибку 401, это не ошибка, это нарушение закона.
— Ник Тернер

Этот код отправляет код состояния 401 ( HttpStatusCode.Unauthorized), а не 200. Content(...)просто сокращение для возврата любого заданного содержимого с заданным кодом состояния HTTP. Если вы хотите отправить 200, вы можете использоватьOk(...)
— Alex AIT

@NickTurner - аргумент в пользу того, что метод webapi2 Content () плохо назван не потому, что это неправильный ответ. Поскольку метод (status, message) переименован в NetCore, я думаю, разработчики согласны с тем, что он был назван неудачно.
— Крис Ф. Кэрролл,

9

Вы получаете код ответа 500, потому что вы генерируете исключение ( HttpException), которое указывает на какую-то ошибку сервера, это неправильный подход.

Просто установите код статуса ответа .eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

Немного странно, что исключение принимает код состояния HTTP в качестве параметра, а в документации intellisense говорится, что это код состояния, отправленный клиенту - я надеялся избежать непосредственного изменения ответа, поскольку это кажется подверженным ошибкам, поскольку его глобальное состояние
— GoatInTheMachine

1
Базовый контроллер веб-API не предоставляет Responseсвойство.
— LukeH

3

Чтобы добавить к существующему ответу в ASP.NET Core> = 1.0, вы можете

return Unauthorized();

return Unauthorized(object value);

Чтобы передать информацию клиенту, вы можете сделать такой вызов:

return Unauthorized(new { Ok = false, Code = Constants.INVALID_CREDENTIALS, ...});

На клиенте помимо ответа 401 у вас будут также переданные данные. Например, на большинстве клиентов вы можете await response.json()его получить.


3

В .Net Core вы можете использовать

return new ForbidResult();

вместо того

return Unauthorized();

который имеет преимущество перенаправления на неавторизованную страницу по умолчанию (Account / AccessDenied) вместо того, чтобы давать прямой 401

чтобы изменить расположение по умолчанию, измените свой файл startup.cs

services.AddAuthentication(options =>...)
            .AddOpenIdConnect(options =>...)
            .AddCookie(options =>
            {
                options.AccessDeniedPath = "/path/unauthorized";

            })

Речь идет о веб-API. Значит, это был бы неверный ответ, если я не ошибаюсь? API не должен возвращать «действия», только результаты.
— Нильс Лукас,

1

вы можете использовать следующий код в asp.net core 2.0:

public IActionResult index()
{
     return new ContentResult() { Content = "My error message", StatusCode = (int)HttpStatusCode.Unauthorized };
}

1

Вы также следуете этому коду:

var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
      Content = new StringContent("Users doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
      StatusCode = HttpStatusCode.NotFound
 }
 throw new HttpResponseException(response);

Вам не нужно снова устанавливать StatusCode, если вы передадите его конструктору - можно использовать любой из них
— Jon Story
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.