如何使 ASP.NET Core Web API 操作异步执行?

Posted

技术标签:

【中文标题】如何使 ASP.NET Core Web API 操作异步执行?【英文标题】:How to make ASP.NET Core Web API action execute asynchronously? 【发布时间】:2019-02-09 05:22:31 【问题描述】:

我的 ASP.NET Core Web API 的控制器中有一个动作,用于上传和处理文件。文件小没关系,但文件大了怎么办?我希望这个动作异步执行,所以我将方法定义为async,它的返回值只是Task可以等待,并在await Task.Run(() => ...)块中插入连续操作,使动作也async并调用这个方法带有await关键字,但实际上调用该方法后该动作并没有返回。我试图上传大文件,并且不得不等到文件完全上传和处理。那么,我应该怎么做才能让这个动作真正异步执行呢?

【问题讨论】:

一个方法如果遇到await 将不会立即返回 - await 是该方法执行的暂停点,直到awaited Task 完成。 你应该使用异步任务 请访问***.com/questions/41953102/… 那么,它是在不同的任务中执行的,但我还需要等待它吗?如何不等待就让这个任务成为后台? 正如我在问题描述中提到的,我已经分别将操作设为异步,我还将其返回值设为 Task<IActionResult> 【参考方案1】:

Web Api 项目的调用方不知道 C# 细节,不知道 Task 是什么或 await 是什么意思。它可能是 C# 或 JS 甚至是 php,或者是在浏览器栏中键入您的 URL 的人。

默认情况下(并且应该!)您的 Web Api 将等待整个处理完成,然后发出成功的信号。这通常通过状态码作为返回值来完成。

现在客户端调用您的 API 不必等待。但那是客户端编程。你不能在你的 API 中做任何事情来解决客户的问题。

否则,您可以让您的 API 将文件放入处理队列中,以便稍后由不同的软件处理。但这需要另一种架构,因为现在调用者需要在不再连接到您的 API 时收到失败成功(或一般结果)的通知,因此您需要回调。

【讨论】:

谢谢!您能否推荐一些资源来了解 ASP.NET Core 中的处理队列? 对于 ASP.NET Core 队列和处理没有什么特别的。您的 API 将请求写入队列(您认为合适的任何类型...文件、数据库、消息队列、biztalk),然后另一个进程在该队列上运行并逐个处理项目。最后,它需要一个通道来通知原始调用者。没有单一的技术,但如果你用谷歌搜索“服务总线”,你可能会发现一些有趣的话题。 谢谢!这真的很有帮助。【参考方案2】:

我有这段代码用于处理大文件(这是用于上传大型 CSV 文件):

    public async Task<IActionResult> UploadAsync(IFormFile file)
    
        // Ensure the file has contents before processing.
        if (file == null || file.Length == 0)
            throw new ApiException("Csv file should not be null", HttpStatusCode.BadRequest)
                .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.BelowMinimumLength, SOURCE); 

        // Ensure the file is not over the allowed limit.
        if (file.Length > (_settings.MaxCsvFileSize * 1024))
            throw new ApiException("Max file size exceeded, limit of " + _settings.MaxCsvFileSize + "mb", HttpStatusCode.BadRequest)
                .AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.ExceedsMaximumLength, SOURCE); 

        // Ensure the file type is csv and content type is correct for the file.
        if (Path.GetExtension(file.FileName) != ".csv" || 
            !Constants.CsvAcceptedContentType.Contains(file.ContentType.ToLower(CultureInfo.InvariantCulture)))
                throw new ApiException("Csv content only accepted").AddApiExceptionResponseDetails(ErrorTypeCode.ValidationError, ErrorCode.Invalid, SOURCE);

        // Read csv content.
        var content = await file.ReadCsvAsync<OrderCsvResponseDto>() as CsvProcessedResponseDto<OrderCsvResponseDto>;

        await ProcessBulkUpload(content);

        // Return information about the csv file.
        return Ok(content);
    

    internal async Task ProcessBulkUpload(CsvProcessedResponseDto<OrderCsvResponseDto> content)
    
         // do some processing...
    

有 web.config 设置可以增加文件上传的允许时间,这可能会有所帮助:How to increase the max upload file size in ASP.NET?

如果您的请求超过允许的最大超时时间,数据将不会按预期返回给您的调用者!

如果你想从 C# 中执行“即发即弃”代码,你可以这样做:

public static void FireAndForget(this Task task)

    Task.Run(async() => await task).ConfigureAwait(false);

javascript

xhr.onreadystatechange = function()  xhr.abort(); 

AngularJS:

var defer = $q.defer();
$http.get('/example',  timeout: defer.promise ).success(callback);
// [...]
defer.resolve();

一些针对 Js 的 async/await 技巧:http://2ality.com/2016/10/async-function-tips.html

【讨论】:

我有几乎相同的代码,但它仍然不是异步的。问题是我必须等待我的方法完成,我想将上传的文件发送到方法中,然后从操作返回OkActionResult。 如果您在浏览器中通过脚本执行调用,您是否看到它在几分钟后超时?我会尝试 web.config 设置,看看是否有帮助:) 不,浏览器中没有任何内容。你看,代码是正确的并且执行正确,我只是希望用户不要等待它完全执行。

以上是关于如何使 ASP.NET Core Web API 操作异步执行?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AddMvcCore() 实现“纯”ASP.NET Core Web API

带有 EF Core 更新实体的 ASP.Net 核心 Web Api 如何

如何在 ASP.NET Core Web API 中发布对象列表

Asp.Net Core Web API 应用程序:如何更改监听地址?

ASP.Net Core Web API 如何返回 File。

如何注销或过期 ASP.NET Core Web API 的 JWT 令牌?