Srinjoy Santra
Srinjoy Santra

Reputation: 131

The stream was already consumed. It cannot be read again. When trying to clone HttpResponseMessage with the message delegating handler

I tried cloning the HttpResponseMessage object and printing the response in a fire-and-forget async call. For this, I received The stream already consumed error.

Any leads are appreciated.

Logs

 An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: The stream was already consumed. It cannot be read again.
         at System.Net.Http.HttpConnectionResponseContent.ConsumeStream()
         at System.Net.Http.HttpConnectionResponseContent.SerializeToStreamAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)

Sample Code


using System.Text;
using BookMyShow.Logging;
using Microsoft.Extensions.Http;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient();
builder.Services.ConfigureAll<HttpClientFactoryOptions>(options =>
{
    options.HttpMessageHandlerBuilderActions.Add(messageHandlerBuilder =>
    {
        messageHandlerBuilder.AdditionalHandlers.Add(messageHandlerBuilder.Services.GetRequiredService<HttpRetentionHandler>());
    });
});
builder.Services.AddTransient<HttpRetentionHandler>();
var app = builder.Build();

app.MapPost("/post", async (IHttpClientFactory httpClientFactory) =>
{
    var httpClient = httpClientFactory.CreateClient();
    string jsonPayload = @"{""title"": ""New Post"", ""body"": ""This is the body of the new post"", ""userId"": 1}";
    var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
    using var request = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        RequestUri = new Uri("https://jsonplaceholder.typicode.com/posts"),
        Content = content
    };
    var response = await httpClient.SendAsync(request);
        
    if (response.StatusCode != HttpStatusCode.OK)
    {
        Console.WriteLine($"Integration Service failed with error and Status Code: {response.StatusCode}");
    }
    string body = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
    return Results.Ok(body);
});

app.Run();

public class HttpRetentionHandler : DelegatingHandler
{
    public HttpRetentionHandler()
    {
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {

        var logReference = new Dictionary<string, object>();
        try
        {
            HttpResponseMessage responseMessage = await base.SendAsync(request, cancellationToken);
            HttpResponseMessage clonedResponse = await CloneAsync(responseMessage);

            var responseText = new StringBuilder(clonedResponse.ToString());
            responseText.Append('\n');
            logReference["response"] = responseText.ToString();
            Task.Run(async () => {
                await PrintApiResponseAsync("print response", logReference, clonedResponse.Content, responseText);
            }, cancellationToken);
            return responseMessage;

        }
        catch (Exception)
        {
            throw;
        }
    }
        


    async Task PrintApiResponseAsync(string name, Dictionary<string, object> logReference, HttpContent responseContent, StringBuilder responseText)
    {

        if (responseContent != null)
        {
            string content = await responseContent.ReadAsStringAsync();
            responseText.Append(content);
            logReference["response"] = responseText.ToString();
        }
        string text = JsonSerializer.Serialize(logReference, new JsonSerializerOptions { WriteIndented = true });
        Console.WriteLine(text);
    }
        
    private async Task<HttpResponseMessage> CloneAsync(HttpResponseMessage response)
    {
        var clone = new HttpResponseMessage(response.StatusCode);
            
        if (response.Content != null)
        {
            using var ms = new MemoryStream();
            await response.Content.CopyToAsync(ms);
                
            ms.Position = 0;
            clone.Content = new StreamContent(ms);

            response.Content.Headers.ToList().ForEach(header =>
                clone.Content.Headers.TryAddWithoutValidation(header.Key, header.Value));
        }
        response.Headers.ToList().ForEach(header =>
            clone.Headers.TryAddWithoutValidation(header.Key, header.Value));
        return clone;
    }
}

Upvotes: 1

Views: 56

Answers (0)

Related Questions