【发布时间】:2017-05-18 18:52:42
【问题描述】:
ASP.NET Core API 控制器通常返回显式类型(如果您创建新项目,默认情况下会返回),类似于:
[Route("api/[controller]")]
public class ThingsController : Controller
{
// GET api/things
[HttpGet]
public async Task<IEnumerable<Thing>> GetAsync()
{
//...
}
// GET api/things/5
[HttpGet("{id}")]
public async Task<Thing> GetAsync(int id)
{
Thing thingFromDB = await GetThingFromDBAsync();
if(thingFromDB == null)
return null; // This returns HTTP 204
// Process thingFromDB, blah blah blah
return thing;
}
// POST api/things
[HttpPost]
public void Post([FromBody]Thing thing)
{
//..
}
//... and so on...
}
问题在于 return null; - 它返回一个 HTTP 204:成功,没有内容。
这被很多客户端Javascript组件认为是成功的,所以有这样的代码:
const response = await fetch('.../api/things/5', {method: 'GET' ...});
if(response.ok)
return await response.json(); // Error, no content!
在线搜索(例如this question 和this answer)指向有用的控制器扩展方法return NotFound();,但所有这些都返回IActionResult,这与我的Task<Thing> 返回类型不兼容。该设计模式如下所示:
// GET api/things/5
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync(int id)
{
var thingFromDB = await GetThingFromDBAsync();
if (thingFromDB == null)
return NotFound();
// Process thingFromDB, blah blah blah
return Ok(thing);
}
这行得通,但要使用它,必须将 GetAsync 的返回类型更改为 Task<IActionResult> - 显式类型丢失,并且控制器上的所有返回类型都必须更改(即在all) 或者会有一些操作处理显式类型而其他操作的混合。此外,单元测试现在需要对序列化做出假设,并明确反序列化 IActionResult 的内容,在它们具有具体类型之前。
有很多方法可以解决这个问题,但它似乎是一个很容易设计出来的令人困惑的混搭,所以真正的问题是:ASP.NET Core 设计者想要的正确方法是什么?
似乎可能的选项是:
- 根据预期的类型,有一个奇怪的(测试混乱)显式类型和
IActionResult的组合。 - 忘记显式类型吧,Core MVC 并不真正支持它们,总是使用
IActionResult(在这种情况下,它们为什么会出现?) - 编写
HttpResponseException的实现并像ArgumentOutOfRangeException一样使用它(有关实现,请参见this answer)。但是,这确实需要对程序流使用异常,这通常是个坏主意,而且deprecated by the MVC Core team。 - 编写一个
HttpNoContentOutputFormatter的实现,为 GET 请求返回404。 - Core MVC 的工作方式还缺少什么?
- 或者对于失败的 GET 请求,
204正确而404错误是否有原因?
这些都涉及妥协和重构,会丢失一些东西或增加一些看起来不必要的复杂性,与 MVC Core 的设计不一致。哪种妥协是正确的,为什么?
【问题讨论】:
-
@Hackerman 嗨,你读过这个问题吗?我特别了解
StatusCode(500),它仅适用于返回IActionResult的操作,然后我将详细介绍。 -
@Hackerman 不,它确实不是。 only 适用于
IActionResult。我问的是显式类型 的操作。我继续在第一个要点中询问IActionResult的使用,但我不是在问如何调用StatusCode(404)- 我已经知道并在问题中引用它。 -
对于您的方案,解决方案可能类似于
return new HttpResponseMessage(HttpStatusCode.NotFound);...也根据此:docs.microsoft.com/en-us/aspnet/core/mvc/models/formattingFor non-trivial actions with multiple return types or options (for example, different HTTP status codes based on the result of operations performed), prefer IActionResult as the return type. -
@Hackerman 你投票结束了我的问题,因为这是我发现、阅读并经历过的一个问题,在我问这个问题之前和我在问题不是我正在寻找的答案。显然,我采取了防御措施——我想回答 我的 问题,而不是被指向一个圆圈。您的最终评论实际上很有用,并开始解决我实际要问的问题 - 您应该将其充实为完整的答案。
-
好的,我得到了更多关于这个主题的信息......为了完成类似的事情(我仍然认为最好的方法应该是使用
IActionResult),你可以按照这个例子@987654353 @ 如果thing是null,您可以返回HttpResponseException...
标签: c# asp.net-core asp.net-core-mvc http-status-code-404 asp.net-core-webapi