I would like to consume an IAsyncEnumerable via a HttpClient from a Blazor-Page. The stream of the results works properly but in case of cancelling the request, the Task does not end on the server side.
Server-Side Code (Sample Code->results in an endless-loop):
async IAsyncEnumerable<WeatherForecast> Post([EnumeratorCancellation] CancellationToken cancellationToken, ILogger<Program> logger)
{
logger?.LogInformation($"Called Server");
var i = 0;
while(!cancellationToken.IsCancellationRequested)
{
logger?.LogInformation($"Send item {i}");
cancellationToken.ThrowIfCancellationRequested();
yield return new WeatherForecast
(
DateTime.Now.AddDays(i),
Random.Shared.Next(-20, 55),
WeatherForecastController.Summaries[Random.Shared.Next(WeatherForecastController.Summaries.Length)]
);
await Task.Delay(1000, cancellationToken);
i++;
}
}
Client-Side (Cancellation via Button):
this._cts = new();
using var client = factory.CreateClient("Default");
using var req = new HttpRequestMessage(HttpMethod.Post, uri);
req.SetBrowserResponseStreamingEnabled(true);
var response = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, this._cts.Token);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync(this._cts.Token);
var weatherForcasts = JsonSerializer.DeserializeAsyncEnumerable<WeatherForecast>(
responseStream,
new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
DefaultBufferSize = 128
}
, this._cts.Token);
await foreach (var weatherForecast in weatherForcasts)
{
if (this._cts.Token.IsCancellationRequested)
{
break;
}
forecasts.Add(weatherForecast);
await this.InvokeAsync(StateHasChanged);
await Task.Delay(1);
}
}
UPDATE: When i try to iterate through a loop without "yield return" inside only awaiting a Delay, the cancellation works properly. After canelling the client request, the cancelationtoken of the server-call will be canceled as well.