Interfejs API sieci Web ASP.NET: poprawny sposób zwracania 401 / nieautoryzowanej odpowiedzi


99

Mam witrynę internetową MVC, która używa uwierzytelniania OAuth / token do uwierzytelniania żądań. Wszystkie odpowiednie kontrolery mają odpowiednie atrybuty, a uwierzytelnianie działa prawidłowo.

Problem polega na tym, że nie wszystkie żądania można autoryzować w zakresie atrybutu - niektóre sprawdzenia autoryzacji muszą być przeprowadzane w kodzie wywoływanym przez metody kontrolera - jaki jest prawidłowy sposób zwrócenia nieautoryzowanej odpowiedzi 401 w tym przypadku?

Próbowałem throw new HttpException(401, "Unauthorized access");, ale kiedy to zrobię, kod statusu odpowiedzi to 500 i otrzymuję również ślad stosu. Nawet w naszym logowaniu DelegatingHandler widzimy, że odpowiedź to 500, a nie 401.


1
Każdemu, kto podchwyci tę odpowiedź, proponuję zastanowić się nad odpowiednim momentem, aby rzucić a HttpResponseExceptionversus, kiedy zwrócić Unauthorized(). Używanie wyjątku dla „oczekiwanego” błędu jest trochę anty-wzorzec, więc jeśli są przypadki, w których spodziewasz się, że wywołanie popełni ten błąd, powrót Unauthorized()jest prawdopodobnie właściwym wywołaniem. Oszczędzaj HttpResponseExceptionna naprawdę nieoczekiwane.
— Rikki


@Rikki, 401 nie jest „oczekiwanym” błędem. - To wyjątkowa okoliczność, która powinna spowodować przerwanie przepływu pracy (może z wyjątkiem logowania, które powinieneś już robić w przypadku każdego wyjątku ...) - W każdym razie, jeśli chcesz zwrócić silnie wpisany wynik z kontrolera ( np. w celu ułatwienia testowania jednostkowego), wyjątek jest zdecydowanie najlepszą drogą.
— BrainSlugs83

Odpowiedzi:


145

Powinieneś wyrzucać HttpResponseExceptionze swojej metody API, a nie HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Lub, jeśli chcesz podać niestandardową wiadomość:

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

96

Po prostu zwróć następujące informacje:

return Unauthorized();

2
Myślę, że zaakceptowana odpowiedź konkretnie na pytanie PO. Moja odpowiedź odpowiada tytułowi pytania „ASP.NET Web API: poprawny sposób zwrócenia 401 / nieautoryzowanej odpowiedzi”
— JohnWrensby,

3
Czy ktoś wie, dlaczego nie ma przeciążonej wersji tego z komunikatem?
— Simon_Weaver,

5
@Simon_Weaver Nie mam pojęcia dlaczego, ale możesz użyć return Content<string>(HttpStatusCode.Unauthorized, "Message");do tego.
— Rikki

2
To powinna być prawidłowa odpowiedź. 1 to jest poprawne. 2) Jeśli to się zmieni w późniejszym frameworku, nie musisz zmieniać kodu. 3) Nie musisz podawać powodu 401. Powinien to zrobić klient, a nie serwer.
— Nick Turner

1
W której bibliotece to jest?
— Nae

19

Alternatywnie do innych odpowiedzi możesz również użyć tego kodu, jeśli chcesz zwrócić IActionResultw kontrolerze ASP.NET.

ASP.NET

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

Aktualizacja: ASP.NET Core

Powyższy kod nie działa w ASP.NET Core, możesz zamiast tego użyć jednego z nich:

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

Najwyraźniej fraza powodu jest dość opcjonalna ( czy w odpowiedzi HTTP można pominąć wyrażenie powodu? )


1
To już nie działa w ASP.NET Core, ControllerBaseklasa (używana przez ASP.NET Core WebAPI) nie ma już Contentprzeciążenia, które akceptuje kod stanu HTTP.
— Dai,

To jest źle. Odpowiedź zawartości ma status 200 OK. Serwer powinien wysłać 401, a klient powinien to zrobić. Nie możesz wysłać 200 jako 401. To nie ma sensu. Jeśli klient otrzyma 401, to nie jest Ups, to twoje złamanie prawa.
— Nick Turner

Ten kod wysyła kod statusu 401 ( HttpStatusCode.Unauthorized), a nie 200. Content(...)po prostu skrót do zwracania dowolnej treści z podanym kodem statusu HTTP. Jeśli chcesz wysłać 200, możesz użyćOk(...)
— Alex AIT

@NickTurner - to argument przemawiający za niewłaściwą nazwą metody webapi2 Content (), a nie dlatego, że jest to zła odpowiedź. Ponieważ nazwa metody (status, wiadomość) została zmieniona w NetCore, myślę, że twórcy zgadzają się, że została źle nazwana.
— Chris F Carroll

9

Otrzymujesz kod odpowiedzi 500, ponieważ rzucasz wyjątek (the HttpException), który wskazuje na jakiś rodzaj błędu serwera, to jest niewłaściwe podejście.

Wystarczy ustawić kod statusu odpowiedzi .eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

To trochę dziwne, że wyjątek przyjmuje kod stanu HTTP jako parametr, a dokumentacja Intellisense twierdzi, że jest to kod stanu wysłany do klienta - miałem nadzieję, że uniknę bezpośredniej mutacji odpowiedzi, ponieważ wydaje się to podatne na błędy, widząc, jak jej stan globalny
— GoatInTheMachine

1
Podstawowy kontroler interfejsu API sieci Web nie uwidacznia Responsewłaściwości.
— LukeH

3

Aby dodać do istniejącej odpowiedzi w ASP.NET Core> = 1,0, możesz

return Unauthorized();

return Unauthorized(object value);

Aby przekazać informacje klientowi, możesz zadzwonić w ten sposób:

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

Na kliencie oprócz odpowiedzi 401 będziesz miał również przekazane dane. Na przykład w przypadku większości klientów możeszawait response.json() to uzyskać.


3

W .Net Core możesz użyć

return new ForbidResult();

zamiast

return Unauthorized();

który ma tę zaletę, że przekierowuje na domyślną nieautoryzowaną stronę (Odmowa dostępu / Konta) zamiast podawania prostego 401

aby zmienić domyślną lokalizację, zmodyfikuj plik startup.cs

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

            })

Pytanie dotyczy internetowego interfejsu API. Więc to byłaby nieprawidłowa odpowiedź, jeśli się nie mylę? API nie powinno zwracać „działań”, tylko wyniki.
— Niels Lucas

1

możesz użyć następującego kodu w asp.net core 2.0:

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

1

Postępujesz również zgodnie z tym kodem:

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);

Nie musisz ponownie ustawiać StatusCode, jeśli przekażesz go konstruktorowi - użycie któregokolwiek z nich jest w porządku
— Jon Story
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.