kennethc
kennethc

Reputation: 844

API progress bar with ASP.NET Core 3.1 and axios

I have long-running ASP.NET Core API that I would like to present a progress bar on UI.

From the server-side, I know how many jobs will be done from the very beginning. Say, if I have 10 jobs and each job takes a second, this will be 10 seconds long progress bar.

The best I could find was https://github.com/DmitrySikorsky/AspNetCoreUploadingProgress, But it relies on saving the progress on Startup. Progress, which is static int. Wouldn't it mean there can be only one upload session at a time in the entire web application?

I wonder if I can do this with axios call:

return axios.post(
    '/api/long-running-task',
    {},
    {
      onUploadProgress: function(progressEvent) {
        console.log("upload", progressEvent.loaded, progressEvent.total);
      },
      onDownloadProgress: function(progressEvent) {
        console.log("download", progressEvent.loaded, progressEvent.total);
      }
    }
  );

And if I do something properly from ASP.NET Core side, would I be able to communicate back to axios and trigger either onUploadProgress or onDownloadProgress?

What I tried:

[HttpPost]
[Route("long-running-task")]
public async Task<ActionResult> Run()
{
    Response.StatusCode = 200;
    Response.ContentType = "text/plain";
    Response.ContentLength = 10;

    var sw = new StreamWriter(Response.Body);

    for (var i = 0; i < 10; i++)
    {
        await Task.Delay(1000);
        await sw.WriteAsync("1");
        await sw.FlushAsync();
    }
    return null;
}

axios writes one upload log shortly after, and then one download log 10 seconds later. No interim progress is received.

Upvotes: 3

Views: 5269

Answers (2)

kennethc
kennethc

Reputation: 844

I found a way to make this work: specify ContentType to be text/event-stream.

I believe it changes server side caching behavior so it should work on any browsers as along as axios is supported. Confirmed working on Chrome 81, Edge 44 and IE 11.

[HttpPost]
[Route("long-running-task")]
public async Task<ActionResult> Run()
{
    Response.StatusCode = 200;
    Response.ContentType = "text/event-stream";
    Response.ContentLength = 10;

    var sw = new StreamWriter(Response.Body);

    for (var i = 0; i < 10; i++)
    {
        await Task.Delay(1000);
        await sw.WriteAsync("1");
        await sw.FlushAsync();
    }
    return Ok();
}

EDIT: at the end, return Ok() instead of null. If you don't have any jobs, returning null will throw an exception.

Upvotes: 5

Vova Bilyachat
Vova Bilyachat

Reputation: 19514

Upload and download status wont help you with your task since it will take progress of request size.

What you need to do is on asp.net core side:

  1. Create job/start api which returns you some kind of key
  2. job/progress/id api which will tell you how much you progress

Then inside of your axios call start job get id, then each N seconds call status to see how much your job progressed.

Other option look into signalR steps will be the same the only you wont need to call status api every N seconds you will be able to push back from server you job status.

Upvotes: 0

Related Questions