异步任务<IActionResult> 与任务<T>

Posted

技术标签:

【中文标题】异步任务<IActionResult> 与任务<T>【英文标题】:async Task<IActionResult> vs Task<T> 【发布时间】:2018-07-03 01:29:52 【问题描述】:

我有一个只有一个动作的控制器。在此操作方法中,我调用了一个 async 方法,仅此而已。这是我正在使用的代码:

[HttpGet]
public Task<MyObject> Get()

    return _task.GetMyObject()

这会正确地序列化为我期望的 JSON。现在我的经理坚持要将签名更改为以下内容:

[HttpGet]
public async Task<IActionResult> Get()

    var data = await_task.GetMyObject();
    return Ok(data);

我相信在控制器中没有理由将代码发送到await,并且可以只返回Task,因为之后没有任何事情取决于结果。除了为await 完成的额外代码生成(创建状态机等)之外,从WebApi 的角度来看这些方法是否有任何影响?澄清一下,我想知道返回 IActionResult 是否比只返回 Task&lt;MyObject&gt; 更好,即使结果看起来是一样的。

【问题讨论】:

What is the purpose of "return await" in C#?的可能重复 @SeM 不,它不是重复的,只是await 关键字的一小部分并不意味着它与另一个带有await 关键字的问题重复。 @SeM 这不是骗子。我看过那个帖子。我想了解 IActionResult 响应 @JohanP:以前有人问过,见here @tseng 差不多了,IActionResult 是其中的一部分,另一个是我通过等待异步调用然后返回 Ok() 是否会失去/获得任何东西,或者我是否会失去/获得任何东西只是返回我的任务,我失去/得到的那些东西是什么 【参考方案1】:

动作可以返回任何东西,大多数情况下它们会返回一个产生响应的IActionResult(或Task&lt;IActionResult&gt; 用于异步方法)的实例。 action 方法负责选择它返回什么样的响应以及响应的 action 结果。

如果一个动作返回一个IActionResult 实现者并且控制器从控制器继承,开发者有许多辅助方法对应于许多选择。返回非 IActionResult 类型的对象的操作的结果将使用适当的 IOutputFormatter 实现进行序列化。

对于具有多种返回类型或选项的重要操作(例如,基于执行操作结果的不同 HTTP 状态代码),首选 IActionResult 作为返回类型。

【讨论】:

【参考方案2】:

任务

专业版

单元测试不需要任何转换,

 Product product = await controller.Get();

最大的优势是,您的单元测试真正独立于底层 HTTP 堆栈。

Swagger 不需要任何额外的属性来生成响应模式,因为 swagger 可以轻松检测结果类型。

另一个很大的优势是,当逻辑保持不变时,您可以在其他控制器中重用您的控制器。

在返回之前避免await 也会略微提高性能,因为这部分代码不需要任务状态机。我认为未来的 C# 版本将省略单等待作为编译器优化。

骗局

返回错误状态码需要抛出异常..

    throw new HttpStatusException(404, "File not found");
    throw new HttpStatusException(409, "Unauthorized");

任务

专业版

可以返回HTTP状态码如

 return NotFound(); // (Status Code = 404)
 return Unauthorized(); // (Status Code = 409)

骗局

单元测试需要额外的转换..

 Product productResult = ((await controller.Get()) as OkResult).Result as Product;

由于这种转换,很难在其他控制器中重用您的控制器,从而导致逻辑重复。

Swagger 生成器需要额外的属性来生成响应架构

 [ProducesResponseType(typeof(Product), 200)]

仅当您处理不属于单元测试的逻辑且不属于您的业务逻辑(例如 OAuth 与第三方服务的集成,您希望更多地关注基于 IActionResult 的情况下)时,才建议使用此方法结果如ChallengeRedirect等。

【讨论】:

不仅仅是返回状态码,Task&lt;T&gt; 也可以(通过设置http 响应上的属性。它允许您选择返回的方式类型将被序列化,即FileResult 以返回/蒸汽文件或JsonResult,即使请求的媒体类型是 xml 并且请求了 xml 序列化器,甚至返回完整或部分呈现的 html 文件,也将始终返回 json (使用ViewResultPartialViewResult @Tseng 问题是关于Task&lt;T&gt;Task&lt;IActionResult&gt;,而不是关于FileResultViewResult 等,如果我期待在Api 中使用Xml 或Json 的模型,我为什么要@ 987654340@ 或 ViewResult ? 翻白眼 FileResult IActionResult 的一个实现。当您有操作并启用 xml 格式化程序时,返回模型或 IActionResultEVERY 控制器操作将根据浏览器请求返回 Xml 或 Json,尽管您可以通过显式覆盖它返回 JsonResult (或使用基本相同的 Json 辅助方法)。只是说IActionResult 的唯一优点是返回状态码只是完全错误 @Tseng 你真的认为我不知道FileResultIActionResult 的实现吗?我的观点很简单,如果我期待一个具有一组字段的产品,我将如何处理FileResult?你说得对,IActionResult最好,继续用,没人逼你不用。 您对 IActionResult 的评论仅 pro您可以返回诸如 之类的 HTTP 状态代码确实暗示 【参考方案3】:

ASP.NET Core 团队在统一 MVC 和 WEB API(Controller 和 ApiController)的同时,将 IActionResult 抽象为强大的异常处理机制。

为控制流抛出异常是动作方法的反模式。

[HttpGet]
public async Task<MyObject> Get()

    var data = await _task.GetMyObject()
    if(data == null)
    
        return NotFound(); //Or, return Request.CreateResponse(HttpStatusCode.NotFound)
        // Versus, throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
    

    return Ok(data);

注意: NotFound()Ok() 等是 WEBAPI2 时代的 IHttpActionResult 东西,对于 asp.net core 来说并不新鲜。

【讨论】:

不,我不是这个意思。我的意思是我的描述中写的内容。返回任务,不等待它然后返回结果。 @JohanP,返回任务而不是等待它,这是矛盾的。你能澄清一下吗? 在action方法中你可以只返回一个任务,框架会等待它【参考方案4】:

ASP.NET MVC 是一个conventions over configuration 框架。这意味着您的代码的任何未来维护者,包括您未来的自己,都希望以某种方式编写代码,以减少您必须检查以进行更改或添加的类文件的数量。

虽然您的两个技术上不同的选项的结果可能相同,但传统的方法是异步/等待您的结果。该约定以外的任何内容都可能给未来的维护者造成混淆。此外,由于您未遵循约定,未来的 MVC 版本可能会以未知方式破坏您的代码。

软件开发团队的良好领导包括灌输通过简化未来潜在的代码维护来减少组织整体人力需求的愿望。您的经理可能正在尝试推广这一概念。

【讨论】:

以上是关于异步任务<IActionResult> 与任务<T>的主要内容,如果未能解决你的问题,请参考以下文章

IActionResult 或 async Task<IActionResult> 有啥好处?

C# 异步方法:如何返回 HttpStatusCode? [复制]

如何从通过 API 返回的 Task<IActionResult> 获取值以进行单元测试

使所有控制器方法异步

线程可以做啥,而基于任务的异步模式(TAP)和任务并行(TPL)与任务(或任务<T>)不能做啥?

如何在服务中调用异步任务<ActionResult> 方法?