在ASP.NET Core 2.0 Web API中返回有意义的HTTP响应
我觉得返回有意义的HTTP响应是个好主意,但我试图想出正确的方法来处理这个问题。在ASP.NET Core 2.0 Web API中返回有意义的HTTP响应
在我的ASP.NET Core Web API应用程序中,端点(即API操作方法)仅接收请求,调用我的业务层以获取响应并返回响应。
在业务层,我检查请求是否被授权。如果它没有被授权,我会抛出一个例外,它包含了未授权的请求类型,但在这些情况下,我的API端点只返回HTTP 500
。我宁愿退还HTTP 401
。
问题是如何将我的较低级别例外转换为HTTP
状态代码。
两个问题:
- 是否值得试图捕捉例外发生在应用中的较低水平的类型,并尝试将其翻译为
HTTP
反应,或者我应该只是让我的API返回HTTP 500
? - 如果值得,我该如何处理?
假设您的控制器扩展了Microsoft.AspNet.MVC.Controller,您将继承一些方法来执行您需要的操作,例如OK,Forbidden,BadRequest,ObjectResult等。因此,在上述情况下你可以不喜欢
public async IActionResult DoMyThing()
{
try
{
return ObjectResult(await DoMyInternalCall());
}
catch (Exception e)
{
//FigureOut the Exception type indications a security violation
return Forbidden()
}
....
我个人倾向于建立一个返回状态,而不是抛出一个异常,这使得这一切有点清洁尤其是现在,我们有元组的API。所以像
public async IActionResult DoMyThing()
{
var (Status status, string myThing) = await DoMyInternalCall();
switch(status)
{
case Status.OK: return ObjectResult(myThing);
break;
case Status.AccessDenied: return Forbidden();
break;
case Status.NotFound: return Notfound();
...
虽然这只是一种滋味 - 我不判断。关键是,Microsoft.AspNet.MVC.Controller内置了方法,可以让您返回有效的HTTP状态代码和数据。
这就是我一直在寻找的东西。谢谢! – Sam
当然有很多HTTP响应代码,只是因为请求无法正确处理并不一定意味着它是500内部服务器错误。如果有人要求更新一个不存在的对象会怎么样?这不会落在500的范围之内(不确定哪一个可以正常工作 - 我猜可能是4xx,但推荐使用这个常用的HTTP响应列表)。我通常认为,500人是为了“呃,发生在我们身上的事情,我们不知道...”类型的事情。对于大多数其他情况,还有更合适的回应。
就这样说,我觉得例外是最好的概括,因为他们进一步走向顶层。实体框架引发了一些深奥的异常,数据访问层将其包装在一个稍微更通用的DataAccessException中(当然,原始内部为原始),存储库可能更加通用地处理它,并且在它到达控制器时对于响应处理,您应该只有少数应该需要处理的“实际”异常。返回相当通用的东西,在服务器上记录嵌套的异常,并从那里去?
我的2分钱小费
一个Web API应该努力永远不会返回500
状态代码(内部服务器错误)。如果确实如此,那么你写的代码有问题。
话虽如此,你不应该抛出一个异常,意图发回一个状态代码是一个相当差的方式来处理请求 - 即通过做一个毯子捕获所有和掩盖它与一些不错的状态代码给客户。
您应该对请求运行所有验证和逻辑,并将您选择的任何
4xx
状态代码发回。
从本质上讲,一个Web API的状态码应该是
2XX -- Success //(ex: OK, created, no content, etc)
3XX -- Redirection //(ex: renamed an API's path/url to a new one)
4XX -- Client Error
ex:
405 //Method Not Allowed (ex: client sent a DELETE request to your API)
409 //Conflict
415 //Unsupported Media Type (ex: client requests for XML -- yuck! no!)
416 //Range Not Suitable (ex: client asked for a million records)
422 //Unpronounceable Entity (ex: client sent something invalid in the body)
5XX -- Server Error //(ex: a well written Web API will NEVER error!)
但是,如果你真的有一个例外,那么500
错误代码是正确的 - 这意味着你写不好的代码(请参考我的第一点)。
我不喜欢这种抛出异常的想法。 500是一个标志,说明你自己的代码有一些错误。
因此,假设您的业务层正在检查用户是否被授权。我会做的是创建一个方法来检查授权,让该方法返回一个简单的布尔响应,例如。然后你的控制器检查标志,如果它是假的,返回401,完成工作。这是在业务层和api层之间进行通信的更好方式。
很明显,我无法知道您的业务层是如何构建的,但保持简单,保持清晰,不要试图捕获任何异常,干净地处理所有内容并返回适当的HTTP代码。
业务层不应该关心api,不应该处理HTTP代码,基本上这意味着您不会在任何地方泄漏抽象,并且将事物保留在它们所属的层中。
因此,在5xx与4xx之间进行讨论与错误消息的回应是错误的。 –
@Sam - 我同意阿列克谢,这个问题在这里脱离主题。然而,如果你重新提出问题来问“怎么做”而不是“我应该”这样做,这将是一个合理的问题。 – NightOwl888