Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

JZO001/WebApiStreaming

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebAPI Streaming

How to get data as a stream from a WebAPI (.NET)

A remote web API can be consumed as a stream, which can take longer, depends an other server process, and the necessary time to generate the requested content.

It is a real scenario, if we get event, data pieces from a services, etc.

In this project I will demonstrate, how we can use a streaming API, how we can iterate through on the incoming dataset with the IAsyncEnumerable interface.

The server side, using a controller

This controller generates a set of weather data, than send the dataset back to the client as a stream. Every data item serialized into Json format, and finally it sends '[DONE]' string to indicate, the streaming is over.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", 
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public async Task GetAsync()
    {
        Response.StatusCode = 200;
        Response.ContentType = "text/html";

        List<WeatherForecast> list = Enumerable.Range(1, 5)
            .Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToList();

        StreamWriter sw;
        await using ((sw = new StreamWriter(Response.Body))
            .ConfigureAwait(false))
        {
            foreach (WeatherForecast item in list)
            {
                // Thread.Sleep simulates a long running process, 
                // which generates some kind of output
                Thread.Sleep(1000);

                await sw.WriteLineAsync(item.ToString()).ConfigureAwait(false);
                await sw.FlushAsync().ConfigureAwait(false);
            }
            await sw.WriteLineAsync("[DONE]").ConfigureAwait(false);
        };

    }

}

The client side #1, implementing the caller API

On the client side, we need a caller code, which can read and handle the incoming data. Check out the 'yield' instruction when passing every incoming data item towards the API caller. If the code detects the '[DONE]' maker, the reading ends.

public class Api
{

    public async IAsyncEnumerable<string> StreamedAsync([System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7176/weatherforecast"))
        {
            using (HttpClient httpClient = new HttpClient())
            {
                //httpClient.Timeout = Timeout.InfiniteTimeSpan;
                using (HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        using (Stream contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
                        {
                            using (StreamReader reader = new StreamReader(contentStream))
                            {
                                string? line = null;

                                while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null &&
                                    !cancellationToken.IsCancellationRequested)
                                {
                                    if ("[DONE]".Equals(line.Trim())) break;

                                    if (!string.IsNullOrWhiteSpace(line))
                                    {
                                        yield return line;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        string? jsonResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                        yield return jsonResult;
                    }
                }
            }
        }
    }

}

The client side #2, using the client side API

Finally, a code can use the Api class to acquire the data, using IAsyncEnumerable on a proper way.

This code iterates on the incoming data asynchronously and display the given data as a text, one by one, after a data item arrived. This approch does not wait to arrive all of the data, it immediatelly displays what we got from the server.

static async Task Main(string[] args)
{
    Console.WriteLine("Press a key to start");
    Console.ReadKey();

    Api api = new Api();
    await foreach (string line in api.StreamedAsync(CancellationToken.None))
    {
        Console.WriteLine(line);
    }

    Console.WriteLine("[DONE] press a key to exit");
    Console.ReadKey();
}

About

How to get data as a stream from a WebAPI (.NET)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.