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

99

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

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

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

GoatInTheMachine
источник
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";

            })
Mattbloke
источник
Речь идет о веб-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