简体   繁体   中英

Is it possible to stream partial results through ServiceStack on a long running request?

I have a long running database copy request on my ServiceStack API. Upon completion it returns a log of the database copy procedure.

I'd like the response to return each line as it is added to the List object containing the db copy log.

The response will be received by an angular4 app so I guess if this is possible I'll also need to configure something in angular4 to alter the state (ngrx) upon each response update.

To be honest I'm not sure where to begin.

Anyone got any ideas?

To stream to the response in ServiceStack you just need to write to the IResponse.OutputStream directly which you can do in your Service:

public async Task Any(MyRequest request)
{
    foreach (var chunk in Chunks)
        await base.Response.OutputStream.WriteAsync(chunk, 0, chunk.Length);
}

Or by returning a result that implements IStreamWriterAsync or IStreamWriter , eg:

public class StreamChunksResponse : IStreamWriterAsync
{
    public IEnumerable<byte[]>> Chunks { get; set; }

    public async Task WriteToAsync(
        Stream responseStream, 
        CancellationToken token = default(CancellationToken))
    {
        foreach (var chunk in Chunks)
            await responseStream.WriteAsync(chunk, 0, chunk.Length);
    }
}

Which will write to the Response OutputStream for Hosts which don't buffer responses, for IIS/ASP.NET you'll need to ensure Response Buffering is disabled .

The issue then becomes how to handle partial responses on the client which is going to require manually coding against the browser's XMLHttpRequest API by handling responses with a readyState == 3 , eg:

<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', '/stream');
xhr.seenBytes = 0;

xhr.onreadystatechange = function() {
  console.log("state change.. state: "+ xhr.readyState);

  if(xhr.readyState == 3) {
    var newData = xhr.response.substr(xhr.seenBytes);
    console.log("newData: <<" +newData+ ">>");
    document.body.innerHTML += "New data: <<" +newData+ ">><br />";

    xhr.seenBytes = xhr.responseText.length;
    console.log("seenBytes: " +xhr.seenBytes);
  }
};

xhr.addEventListener("error", function(e) {
  console.log("error: " +e);
});

console.log(xhr);
xhr.send();
</script>

This can get unwieldy pretty quickly especially if you're trying to stream a serialization format like JSON where you need to ensure the client can construct valid JSON framgents.

If you just wanted to notify the clients of updates whilst the client is downloading the entire response I recommend using something like Server Events where you progressively send the client notification updates whilst your streaming the Response to the client.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM