Веб-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.