是否可以在长 运行 请求中通过 ServiceStack 流式传输部分结果?
Is it possible to stream partial results through ServiceStack on a long running request?
我的 ServiceStack API 上有一个很长的 运行 数据库复制请求。完成后,它 return 是数据库复制过程的日志。
我想要对 return 每行的响应,因为它被添加到包含数据库复制日志的列表对象中。
响应将由 angular4 应用程序接收,所以我想如果可能的话,我还需要在 angular4 中配置一些东西以在每次响应更新时更改状态 (ngrx)。
老实说,我不知道从哪里开始。
有人有什么想法吗?
要流式传输到 ServiceStack 中的响应,您只需直接写入 IResponse.OutputStream
,您可以在您的服务中执行此操作:
public async Task Any(MyRequest request)
{
foreach (var chunk in Chunks)
await base.Response.OutputStream.WriteAsync(chunk, 0, chunk.Length);
}
或者通过返回实现 IStreamWriterAsync
或 IStreamWriter
的结果,例如:
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);
}
}
这将为不缓冲响应的主机写入响应输出流,对于 IIS/ASP.NET,您需要确保 Response Buffering is disabled.
然后问题变成了如何处理客户端的部分响应,这将需要通过使用 readyState == 3
处理响应来针对浏览器的 XMLHttpRequest
API 手动编码,例如:
<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>
这很快就会变得笨拙,尤其是当您尝试流式传输像 JSON 这样的序列化格式时,您需要确保客户端可以构建有效的 JSON 片段。
如果您只是想在客户端下载整个响应时通知客户端更新,我建议使用类似 Server Events 的方式,在将响应流式传输到客户端的同时逐步发送客户端通知更新。
我的 ServiceStack API 上有一个很长的 运行 数据库复制请求。完成后,它 return 是数据库复制过程的日志。
我想要对 return 每行的响应,因为它被添加到包含数据库复制日志的列表对象中。
响应将由 angular4 应用程序接收,所以我想如果可能的话,我还需要在 angular4 中配置一些东西以在每次响应更新时更改状态 (ngrx)。
老实说,我不知道从哪里开始。
有人有什么想法吗?
要流式传输到 ServiceStack 中的响应,您只需直接写入 IResponse.OutputStream
,您可以在您的服务中执行此操作:
public async Task Any(MyRequest request)
{
foreach (var chunk in Chunks)
await base.Response.OutputStream.WriteAsync(chunk, 0, chunk.Length);
}
或者通过返回实现 IStreamWriterAsync
或 IStreamWriter
的结果,例如:
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);
}
}
这将为不缓冲响应的主机写入响应输出流,对于 IIS/ASP.NET,您需要确保 Response Buffering is disabled.
然后问题变成了如何处理客户端的部分响应,这将需要通过使用 readyState == 3
处理响应来针对浏览器的 XMLHttpRequest
API 手动编码,例如:
<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>
这很快就会变得笨拙,尤其是当您尝试流式传输像 JSON 这样的序列化格式时,您需要确保客户端可以构建有效的 JSON 片段。
如果您只是想在客户端下载整个响应时通知客户端更新,我建议使用类似 Server Events 的方式,在将响应流式传输到客户端的同时逐步发送客户端通知更新。