Веб-API ASP.NET: Правильний спосіб повернення 401 / несанкціонованої відповіді


98

У мене є веб-сайт MVC, який використовує автентифікацію OAuth / маркер для автентифікації запитів. Усі відповідні контролери мають правильні атрибути, і автентифікація працює нормально.

Проблема полягає в тому, що не всі запити можуть бути авторизовані в межах атрибута - деякі перевірки авторизації повинні виконуватися в коді, який викликається методами контролера - який правильний спосіб повернути несанкціоновану відповідь 401 у цьому випадку?

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


1
Кожному, хто підбирає цю відповідь внизу, я б запропонував подумати про відповідний час, коли кинути HttpResponseExceptionпроти, а коли повернути Unauthorized(). Використання винятку для "очікуваної" помилки є трохи анти-шаблоном, тому, якщо є випадки, коли ви очікуєте, що дзвінок зробить цю помилку, повернення Unauthorized()- це, мабуть, правильний виклик. Збережіть HttpResponseExceptionдля справді несподіваного.
Ріккі,

Див. Github.com/aspnet/Mvc/issues/5507 для обговорення.
Ріккі,

@Rikki, 401 - це не "очікувана" помилка. - Це виняткова обставина, яка може призвести до переривання робочого процесу (крім, можливо, для ведення журналу, що ви вже повинні робити для будь-якого винятку ...) - У будь-якому випадку, якщо ви хочете повернути сильний набраний результат від вашого контролера ( наприклад, для зручності модульного тестування), виняток, очевидно, є найкращим маршрутом.
BrainSlugs83,

Відповіді:


145

Ви повинні кидати a HttpResponseExceptionз вашого методу API, а не HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Або, якщо ви хочете надати власне повідомлення:

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

95

Просто поверніть наступне:

return Unauthorized();

2
Я думаю, що прийняте відповідає конкретно на питання ОП. Моя відповідь відповідає на назву запитання "ASP.NET Web API: Правильний спосіб повернення 401 / несанкціонованої відповіді"
JohnWrensby,

3
Хтось знає, чому немає перевантаженої версії цього повідомлення?
Simon_Weaver

5
@Simon_Weaver Не знаю, чому, але для цього ти можеш використовувати a return Content<string>(HttpStatusCode.Unauthorized, "Message");.
Ріккі,

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.
Dai

Це неправильно. Відповідь вмісту - це статус 200 Ok. Сервер повинен надіслати 401, і клієнт повинен обробляти відповідні дії. Ви не можете надіслати 200 як 401. Це не має сенсу. Якщо клієнт отримає 401, це не На жаль, це ваше порушення закону.
Нік Тернер

Цей код надсилає код стану 401 ( HttpStatusCode.Unauthorized), а не 200. Це Content(...)просто стенографія для повернення будь-якого вмісту із заданим кодом стану HTTP. Якщо ви хочете надіслати 200, можете скористатисяOk(...)
Alex AIT

@NickTurner - це аргумент того, що метод webapi2 Content () був погано названий, бо це неправильна відповідь. Оскільки метод (статус, повідомлення) перейменовано в NetCore, я думаю, розробники погоджуються, що він був погано названий.
Кріс Ф Керролл,

9

Ви отримуєте код відповіді 500, оскільки ви створюєте виняток ( HttpException), який вказує на якусь помилку сервера, це неправильний підхід.

Просто встановіть код стану відповіді, наприклад

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 не повинен повертати "дії", а лише результати.
Niels Lucas

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.