ASP.NET Web API: maneira correta de retornar uma resposta 401 / não autorizada


98

Eu tenho um site MVC webapi que usa autenticação OAuth / token para autenticar solicitações. Todos os controladores relevantes têm os atributos corretos e a autenticação está funcionando bem.

O problema é que nem todas as solicitações podem ser autorizadas no escopo de um atributo - algumas verificações de autorização devem ser realizadas no código que é chamado pelos métodos do controlador - qual é a maneira correta de retornar uma resposta 401 não autorizada neste caso?

Eu tentei throw new HttpException(401, "Unauthorized access");, mas quando faço isso, o código de status de resposta é 500 e também consigo um rastreamento de pilha. Mesmo em nosso registro DelegatingHandler, podemos ver que a resposta é 500, não 401.


1
Para qualquer pessoa que esteja pegando essa resposta no final da linha, sugiro pensar sobre o momento apropriado para lançar um HttpResponseExceptionversus quando retornar um Unauthorized(). Usar a exceção para um erro 'esperado' é um pouco antipadrão, portanto, se houver casos em que você espera que a chamada cometa esse erro, retornar Unauthorized()é provavelmente a chamada certa. Economize HttpResponseExceptionpara o verdadeiramente inesperado.
— Rikki

Consulte github.com/aspnet/Mvc/issues/5507 para uma discussão.
— Rikki

@Rikki, 401 não é um erro "esperado". - É uma circunstância excepcional que deve fazer com que você aborte seu fluxo de trabalho (exceto talvez para registro, o que você já deveria estar fazendo para qualquer exceção ...) - De qualquer forma, se você quiser retornar um resultado forte digitado de seu controlador ( por exemplo, para facilitar o teste de unidade), uma exceção é claramente o melhor caminho.
— BrainSlugs83,

Respostas:


145

Você deve lançar um HttpResponseExceptionde seu método de API, não HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Ou, se você deseja fornecer uma mensagem personalizada:

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

95

Basta devolver o seguinte:

return Unauthorized();

2
Acho que o aceito responde especificamente à pergunta do OP. Minha resposta responde ao título da pergunta "ASP.NET Web API: maneira correta de retornar uma resposta 401 / não autorizada"
— JohnWrensby

3
Alguém sabe por que não existe uma versão sobrecarregada disso com uma mensagem?
— Simon_Weaver

5
@Simon_Weaver Não tenho ideia do porquê, mas você poderia usar um return Content<string>(HttpStatusCode.Unauthorized, "Message");para fazer isso.
— Rikki

2
Esta deve ser a resposta correta. 1 está correto. 2) Se isso mudar em uma estrutura posterior, você não precisará alterar o código. 3) Você não precisa fornecer um motivo para um 401. Isso deve ser tratado pelo cliente e não pelo servidor.
— Nick Turner

1
Em qual biblioteca isso está?
— Nae

19

Como alternativa às outras respostas, você também pode usar esse código se quiser retornar um IActionResultem um controlador ASP.NET.

ASP.NET

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

Atualização: ASP.NET Core

O código acima não funciona no ASP.NET Core, você pode usar um destes em seu lugar:

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

Aparentemente, a frase de razão é bastante opcional ( uma resposta HTTP pode omitir a frase de razão? )


1
Isso não funciona mais no ASP.NET Core, a ControllerBaseclasse (usada pelo ASP.NET Core WebAPI) não tem mais uma Contentsobrecarga que aceita um código de status HTTP.
— Dia

Isto está errado. Uma resposta de conteúdo é um status 200 Ok. O servidor deve enviar um 401 e o cliente deve lidar com isso de acordo. Você não pode enviar um 200 como um 401. Não faz sentido. Se o cliente receber um 401, não é um Ops, é uma violação da lei.
— Nick Turner

Este código está enviando um código de status 401 ( HttpStatusCode.Unauthorized), não 200. Content(...)simplesmente uma abreviação para retornar qualquer conteúdo com um determinado código de status HTTP. Se você quiser enviar 200, você pode usarOk(...)
— Alex AIT

@NickTurner - esse é um argumento para o método webapi2 Content () ser mal nomeado, não por ser a resposta errada. Como o método (status, mensagem) foi renomeado no NetCore, acho que os desenvolvedores concordam que foi mal nomeado.
— Chris F Carroll de

9

Você obtém um código de resposta 500 porque está lançando uma exceção (o HttpException) que indica algum tipo de erro do servidor, essa é a abordagem errada.

Basta definir o código de status de resposta.

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

É um pouco estranho então que a exceção tome o código de status HTTP como um parâmetro, e os documentos da Intellisense dizem que este é o código de status enviado ao cliente - eu esperava evitar a mutação da resposta eu mesmo diretamente, pois isso parece sujeito a erros, visto que seu estado global
— GoatInTheMachine

1
O controlador de API da Web base não expõe uma Responsepropriedade.
— LukeH

3

Para adicionar a uma resposta existente no ASP.NET Core> = 1.0, você pode

return Unauthorized();

return Unauthorized(object value);

Para passar informações ao cliente, você pode fazer uma chamada como esta:

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

No cliente além da resposta 401 você também terá os dados passados. Por exemplo, na maioria dos clientes, você pode await response.json()obtê-lo.


3

Em .Net Core você pode usar

return new ForbidResult();

ao invés de

return Unauthorized();

que tem a vantagem de redirecionar para a página não autorizada padrão (Conta / Acesso Negado) em vez de fornecer um 401 direto

para alterar o local padrão, modifique seu startup.cs

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

            })

A questão é sobre uma API da web. Portanto, esta seria uma resposta inválida se não estou errado? API não deve retornar 'ações', apenas resultados.
— Niels Lucas

1

você pode usar o código a seguir no asp.net core 2.0:

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

1

Você também segue este código:

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

Você não precisa definir o StatusCode novamente se passá-lo para o construtor - usar qualquer um deles está certo
— Jon Story
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.