API Web ASP.NET: modo corretto per restituire una risposta 401 / non autorizzata


99

Ho un sito webapi MVC che utilizza l'autenticazione OAuth / token per autenticare le richieste. Tutti i controller rilevanti hanno gli attributi giusti e l'autenticazione funziona correttamente.

Il problema è che non tutte le richieste possono essere autorizzate nell'ambito di un attributo - alcuni controlli di autorizzazione devono essere eseguiti nel codice chiamato dai metodi del controller - qual è il modo corretto per restituire una risposta 401 non autorizzata in questo caso?

Ho provato throw new HttpException(401, "Unauthorized access");, ma quando lo faccio il codice di stato della risposta è 500 e ottengo anche una traccia dello stack. Anche nella nostra registrazione DelegatingHandler possiamo vedere che la risposta è 500, non 401.


1
A chiunque raccolga questa risposta lungo la linea, suggerirei di pensare al momento appropriato per lanciare un HttpResponseExceptioncontro quando restituire un Unauthorized(). Usare l'eccezione per un errore "atteso" è un po 'un anti-pattern, quindi se ci sono casi in cui ti aspetti che la chiamata commetta questo errore, la restituzione Unauthorized()è probabilmente la chiamata giusta. Tranne HttpResponseExceptionper ciò che è veramente inaspettato.
— Rikki

Vedi github.com/aspnet/Mvc/issues/5507 per alcune discussioni.
— Rikki

@ Rikki, 401 non è un errore "previsto". - È una circostanza eccezionale che dovrebbe indurti ad interrompere il tuo flusso di lavoro (tranne forse per la registrazione, che dovresti già fare per qualsiasi eccezione ...) - Ad ogni modo, se vuoi restituire un risultato tipizzato forte dal tuo controller ( es. per facilità di test unitario), un'eccezione è chiaramente il percorso migliore.
— BrainSlugs83

Risposte:


145

Dovresti lanciare un HttpResponseExceptiondal tuo metodo API, non HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Oppure, se desideri fornire un messaggio personalizzato:

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

95

Basta restituire quanto segue:

return Unauthorized();

2
Penso che l'accettato risponda in modo specifico alla domanda del PO. La mia risposta risponde al titolo della domanda "API Web ASP.NET: modo corretto per restituire una risposta 401 / non autorizzata"
— JohnWrensby

3
Qualcuno sa perché non esiste una versione sovraccarica di questo con un messaggio?
— Simon_Weaver

5
@Simon_Weaver Non ho idea del perché, ma potresti usare a return Content<string>(HttpStatusCode.Unauthorized, "Message");per farlo.
— Rikki

2
Questa dovrebbe essere la risposta corretta. 1 è corretto. 2) Se questo cambia in un framework successivo, non è necessario modificare il codice. 3) Non è necessario fornire un motivo a un 401. Questo dovrebbe essere gestito dal client e non dal server.
— Nick Turner

1
In quale libreria si trova?
— Nae

19

In alternativa alle altre risposte, puoi anche utilizzare questo codice se vuoi restituire un IActionResultall'interno di un controller ASP.NET.

ASP.NET

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

Aggiornamento: ASP.NET Core

Il codice sopra non funziona in ASP.NET Core, puoi invece usare uno di questi:

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

Apparentemente la frase del motivo è piuttosto facoltativa ( una risposta HTTP può omettere la frase-motivo? )


1
Questo non funziona più in ASP.NET Core, la ControllerBaseclasse (usata da ASP.NET Core WebAPI) non ha più un Contentoverload che accetta un codice di stato HTTP.
— Dai

Questo è sbagliato. Una risposta di contenuto è uno stato 200 OK. Il server dovrebbe inviare un 401 e il client dovrebbe gestirlo di conseguenza. Non puoi inviare un 200 come 401. Non ha senso. Se il cliente ottiene un 401, non è un Oops, è un tuo infrangere la legge.
— Nick Turner

Questo codice invia un codice di stato 401 ( HttpStatusCode.Unauthorized), non 200. Content(...)semplicemente una scorciatoia per restituire un determinato contenuto con un determinato codice di stato HTTP. Se vuoi inviare 200 puoi usareOk(...)
— Alex AIT

@NickTurner - questo è un argomento per cui il metodo webapi2 Content () è mal nominato non perché questa è la risposta sbagliata. Poiché il metodo (status, message) viene rinominato in NetCore, immagino che gli sviluppatori concordino sul fatto che sia stato chiamato male.
— Chris F Carroll

9

Ottieni un codice di risposta 500 perché stai lanciando un'eccezione (il HttpException) che indica un qualche tipo di errore del server, questo è l'approccio sbagliato.

Basta impostare il codice di stato della risposta .eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

È un po 'strano quindi che l'eccezione prenda il codice di stato HTTP come parametro, e i documenti intellisense dicono che questo è il codice di stato inviato al client - speravo di evitare di modificare la risposta da solo direttamente poiché sembra soggetto a errori, visto che il suo stato globale
— GoatInTheMachine

1
Il controller API Web di base non espone una Responseproprietà.
— LukeH

3

Per aggiungere a una risposta esistente in ASP.NET Core> = 1.0 puoi

return Unauthorized();

return Unauthorized(object value);

Per passare informazioni al cliente puoi fare una chiamata come questa:

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

Sul client oltre alla risposta 401 avrai anche i dati passati. Ad esempio, sulla maggior parte dei clienti puoi await response.json()ottenerlo.


3

In .Net Core È possibile utilizzare

return new ForbidResult();

invece di

return Unauthorized();

che ha il vantaggio di reindirizzare alla pagina non autorizzata predefinita (Account / AccessDenied) piuttosto che dare un semplice 401

per cambiare la posizione predefinita, modificare il file startup.cs

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

            })

La domanda riguarda un'API web. Quindi questa sarebbe una risposta non valida se non sbaglio? L'API non deve restituire "azioni", ma solo risultati.
— Niels Lucas

1

puoi usare il codice seguente in asp.net core 2.0:

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

1

Segui anche questo codice:

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

Non è necessario impostare di nuovo lo StatusCode se lo si passa al costruttore - l'utilizzo di uno dei due va bene
— Jon Story
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.