KevinDockx / AccessingApisWithHttpClient

Fully functioning finished sample and starter files for my Accessing APIs with HttpClient in .NET course, currently targeting .NET 8.
MIT License
10 stars 8 forks source link

Closed Stream exception following send and read compression example #1

Open shallowtek opened 2 months ago

shallowtek commented 2 months ago

Hi Kevin,

Following your compression example ensuring the correct nesting of the using statements, when I try to return the Posters Object I get a Closed Stream Exception.

I am wondering if some using statements need to be changed for this sample to show how an object can be returned.

"status": 500, "detail": "Cannot access a closed Stream.", "exception": { "details": "System.ObjectDisposedException: Cannot access a closed Stream.\n at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)\n at System.IO.Compression.DeflateStream.PurgeBuffers(Boolean disposing)\n at System.IO.Compression.DeflateStream.Dispose(Boolean disposing)\n at System.IO.Stream.Close()\n at System.IO.Compression.GZipStream.Dispose(Boolean disposing)\n at System.IO.Stream.Close()\n

shallowtek commented 2 months ago

Some additional info and what I did to resolve.

I needed to remove using statement on gzipStream and instead dispose on a finally block of a try/catch/finally withing the streamContent using statement from your example.

If you have the time maybe you can review if this will still ensuring same performance and best practices.

` private async Task SendAndReceivePosterWithGZipCompressionAsync() { var httpClient = _httpClientFactory.CreateClient("MoviesAPIClient");

    // generate a movie poster of 5MB
    var random = new Random();
    var generatedBytes = new byte[5242880];
    random.NextBytes(generatedBytes);

    var posterForCreation = new PosterForCreation()
    {
        Name = "A new poster for The Big Lebowski",
        Bytes = generatedBytes
    };

    using (var memoryContentStream = new MemoryStream())
    {
        await JsonSerializer.SerializeAsync(
            memoryContentStream,
            posterForCreation);

        memoryContentStream.Seek(0, SeekOrigin.Begin);

        using (var request = new HttpRequestMessage(
            HttpMethod.Post,
            "api/movies/d8663e5e-7494-4f81-8739-6e0de1bea7ee/posters"))
        {
            request.Headers.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.AcceptEncoding.Add(
              new StringWithQualityHeaderValue("gzip"));

            using (var compressedMemoryContentStream = new MemoryStream())
            {
                var gzipStream = new GZipStream(
                        compressedMemoryContentStream,
                        CompressionMode.Compress);

                    memoryContentStream.CopyTo(gzipStream);
                    gzipStream.Flush();
                    compressedMemoryContentStream.Position = 0;

                    using (var streamContent = new StreamContent(compressedMemoryContentStream))
                    {
                        streamContent.Headers.ContentType =
                            new MediaTypeHeaderValue("application/json");
                        streamContent.Headers.ContentEncoding.Add("gzip");

                        request.Content = streamContent;
                        try
                        {
                            var response = await httpClient.SendAsync(request,
                            HttpCompletionOption.ResponseHeadersRead);
                        response.EnsureSuccessStatusCode();

                        var stream = await response.Content.ReadAsStreamAsync();
                        var poster = await JsonSerializer.DeserializeAsync<Poster>(
                            stream,
                            _jsonSerializerOptionsWrapper.Options);

                        // This throws closed stream exception when trying to return
                        // I am using the try catch finally to return and then dispose of gzipStream
                        return poster;

                        }
                        catch
                        {
                            // catch certain exceptions
                        }
                        finally
                        {
                            gzipStream.Dispose();
                        }
                    }

            }
        }
    }
}`
shallowtek commented 2 months ago

I just spotted I can use the leavOpen flag in gzipStream contrustctor to keep this stream open. Possibly this is best option?

shallowtek commented 2 months ago

I just spotted I can use the leavOpen flag in gzipStream contrustctor to keep this stream open. Possibly this is best option?

This actually doesn't work. Still get the stream closed error when trying to return poster object. Hmmm.

shallowtek commented 2 months ago

For now I will keep the finally block to close the gzipStream. without it I get the exception when I try return poster response in the try block.