I want to stream an arbitrary amount of lines of plain text from an ASP.NET server to a Blazor WebAssembly client (.NET 6.0).
For testing I implemented the following dummy API:
[HttpGet("lines")]
public async IAsyncEnumerable<string> GetLines() {
for (var i = 0; i < 10; ++i) {
yield return "test\n";
await Task.Delay(1000);
}
}
On the client I tried the following approach (following these ideas):
public async IAsyncEnumerable<string?> GetLines() {
var response = await HttpClient.GetAsync($"{apiRoot}/lines", HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode) {
var responseStream = await response.Content.ReadAsStreamAsync();
var lines = JsonSerializer.DeserializeAsyncEnumerable<string>(responseStream);
await foreach (var line in lines) {
yield return line;
}
}
else {
Log.Error($"Server response code: {response.StatusCode}");
yield return null;
}
}
Unfortunately, instead of returning immediately, response.Content.ReadAsStreamAsync()
buffers the entire stream (i.e. 10 lines of "test\n"), taking 10 s in this case, before the buffered content gets deserialized as an IAsyncEnumerable<string>
.
The same behavior can be observed using HttpClient.GetStreamAsync
:
public async IAsyncEnumerable<string?> GetLines() {
var responseStream = await HttpClient.GetStreamAsync($"{apiRoot}/lines"); // buffers for 10 s
var linesAsync = JsonSerializer.DeserializeAsyncEnumerable<string>(responseStream);
await foreach (var line in lines) {
yield return line;
}
}
How can I change this so that every line sent from the server is processed immediately on the client, without any buffering? Is this an issue on the client or the server side? Is there a way to disable this buffering behavior?
Edit: After some more experimentation, I found that calling the API directly (e.g. via the browser) does indeed show the expected streaming behavior, i.e. the individual lines pop up one after the other with a 1.0 s delay. So it seems to be a client-side issue, indeed.