Viacheslav
Viacheslav

Reputation: 120

How to make ASP.NET Core Web API action execute asynchronously?

I have an action in controller in my ASP.NET Core Web API that uploads and processes a file. It's okay when file is small, but what if the file will be large? I want this action to execute asynchronously, so I defined method as async, its return value is just Task to be awaitable, and inserted continuous operations in await Task.Run(() => ...) blocks, made the action also async and call this method with await keyword, but, in fact, this action doesn't return after calling this method. I've tried to upload large file, and have had to wait until file is completely uploaded and processed. So, what should I do to make this action executed really asynchronously?

Upvotes: 2

Views: 5705

Answers (2)

Rob
Rob

Reputation: 7217

I had this code for processing large files (this was for a large CSV file upload):

    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...
    }

There are web.config settings to increase the allowed time for file upload, this might help: How to increase the max upload file size in ASP.NET?

If your request exceeds the max timeout allowed data wont be returned to your caller as expected!

If you want to execute "fire-and-forget" code from C#, you can do this:

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();

Some async/await tips for Js: http://2ality.com/2016/10/async-function-tips.html

Upvotes: 0

nvoigt
nvoigt

Reputation: 77285

The calling side of your Web Api project has no idea of C# details, of what a Task is or await means. It might be C# or JS or even PHP or a person that typed your URL in a browser bar.

Your Web Api will by default (and should!) wait until the whole processing is done and then give a signal that is was successful. This is generally done through a status code as return value.

Now the client calling your API does not have to be stuck waiting for that. But that's client side programming. You cannot really do anything in your API to solve the client's problem with that.

Otherwise, you could make your API just put the file in a processing queue to be processed by a different piece of software later. But that would need another architecture, because now the caller needs to be notified of success of failure (or result in general) when it's not longer connected to your API, so you'd need callbacks.

Upvotes: 5

Related Questions