ASP.NET Web API:返回401 /未经授权的响应的正确方法


99

我有一个MVC webapi网站,该网站使用OAuth /令牌身份验证对请求进行身份验证。所有相关的控制器都具有正确的属性,并且身份验证正常。

问题在于,并非所有请求都可以在属性范围内得到授权-必须在控制器方法调用的代码中执行某些授权检查-在这种情况下,返回401未经授权的响应的正确方法是什么?

我已经尝试过了throw new HttpException(401, "Unauthorized access");,但是当我这样做时,响应状态代码是500,我也得到了堆栈跟踪。即使在日志记录DelegatingHandler中,我们也可以看到响应为500,而不是401。


1
对于任何想直接回答这个问题的人,我建议考虑在适当的时间投掷HttpResponseExceptionvs以及何时返回Unauthorized()。将异常用于“预期的”错误有点反模式,因此,如果在某些情况下您希望调用会导致此错误,则返回Unauthorized()可能是正确的调用。节省HttpResponseException真正的意外。
Rikki

参见github.com/aspnet/Mvc/issues/5507进行一些讨论。
Rikki

@ Rikki,401不是“预期的”错误。-这是一种特殊情况,可能会导致您中止工作流程(可能是日志记录除外,除非有任何例外,否则您应该已经这样做了……)-无论如何,如果您想从控制器中返回强类型的结果(例如,为了简化单元测试),例外显然是最佳途径。
BrainSlugs83 '18 -10-9

Answers:


145

您应该HttpResponseException从API方法中抛出a ,而不是HttpException

throw new HttpResponseException(HttpStatusCode.Unauthorized);

或者,如果您想提供自定义消息:

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

96

只需返回以下内容:

return Unauthorized();

2
我认为被接受者将具体回答OP的问题。我的答案回答了问题的标题“ ASP.NET Web API:返回401 /未经授权的响应的正确方法”
JohnWrensby 2016年

3
有人知道为什么没有消息的重载版本吗?
Simon_Weaver

5
@Simon_Weaver不知道为什么,但是您可以使用a return Content<string>(HttpStatusCode.Unauthorized, "Message");来做到这一点。
Rikki

2
这应该是正确的答案。1这是正确的。2)如果这在以后的框架中有所更改,则不必更改代码。3)您不需要向401提供原因。应由客户端而非服务器处理。
尼克·特纳

1
这是哪个图书馆?

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状态代码的重载。

错了 内容响应为200 OK状态。服务器应发送401,客户端应进行相应处理。您不能将200作为401发送。这没有任何意义。如果客户得到401,则不是糟糕,这是您的违法行为。
Nick Turner

此代码发送401个状态码(HttpStatusCode.Unauthorized),而不是200 Content(...)简单地返回任何给定的内容与给定的HTTP状态代码的缩写。如果您想发送200,则可以使用Ok(...)
Alex AIT

@NickTurner-这是webapi2 Content()方法命名错误的一个参数,不是因为这是错误的答案。由于(status,message)方法在NetCore中已重命名,所以我想开发人员同意它的命名不正确。
克里斯·卡罗尔

9

您得到500响应代码,因为您抛出了HttpException表示某种服务器错误的异常(),这是错误的方法。

只需设置响应状态代码.eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

奇怪的是,该异常将HTTP状态代码作为参数,并且intellisense文档说这是发送给客户端的状态代码-我希望避免自己直接修改响应,因为这似乎容易出错,其全球状态
GoatInTheMachine 2015年

1
基本的Web 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";

            })

问题是关于Web 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 -无论是使用是好的
乔恩故事
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.