question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cannot access a closed Stream (Audit.WebApi.Core)

See original GitHub issue

When using post man to make a get request somewhere in my code I have an unhandled exception, but the result on postman is always 1 and status code is 500 as expected. But the result should show the entire stack trace instead of 1

To Reproduce I have a static class which I created for my purpose and this is being called in the Startup.cs

public static class ApiRequestAuditing
    {
        public static void UseApiRequestAuditing(this IApplicationBuilder app, IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
        {
            Configuration.IncludeTypeNamespaces = true;

            Configuration.Setup().UseMongoDB(c =>
            {
                c.ConnectionString(configuration["MongoConfig:ConnectionString"]);
                c.Database(configuration["MongoConfig:DatabaseName"]);
                c.Collection("auditrequest");
                c.SerializeAsBson();
            });

            Configuration.AddOnCreatedAction(s =>
            {
                var action = s.Event.GetWebApiAuditAction();
                s.Event.CustomFields.Add("ExpireAt", new BsonDateTime(DateTime.UtcNow)); // The TTL field which will be used to purge the audit records
            });

            Configuration.AddOnSavingAction(s =>
            {
                var action = s.Event.GetWebApiAuditAction();
                var request = httpContextAccessor.HttpContext.Request;
                var exHandlerFeature = httpContextAccessor.HttpContext.Features.Get<ICustomExceptionHandlerFeature>();
                var exception = exHandlerFeature?.Error;
                var routeData = httpContextAccessor.HttpContext.GetRouteData().Values;
                routeData.TryGetValue("controller", out object controllerName);
                routeData.TryGetValue("action", out object actionName);

                // If we got an IPV6 address, then we need to ask the network for the IPV4 address 
                // This usually only happens when the browser is on the same machine as the server.
                if (IPAddress.TryParse(action.IpAddress, out IPAddress ip) && ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    action.IpAddress = Dns.GetHostEntry(ip.ToString()).AddressList.First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).ToString();
                }

                action.ActionName = actionName?.ToString();
                action.ControllerName = controllerName?.ToString();
                action.Exception = exception?.ToString(); // Only unhandled exception will be log here.

                if (request.Path.StartsWithSegments("/api/XXX") && request.Method == HttpMethod.Get.ToString() && httpContextAccessor.HttpContext.Response.StatusCode == StatusCodes.Status200OK)
                {
                    // GET requests will have a response body of NULL if the status is 200OK but will still be audited
                    action.ResponseBody = null;
                }
            });

            app.UseAuditMiddleware(o =>
            {
                o.FilterByRequest(requestPredicate);
                o.WithEventType("{verb}:{url}");
                o.IncludeHeaders();
                o.IncludeResponseHeaders();
                o.IncludeRequestBody();
                o.IncludeResponseBody();
            });
        }

        internal readonly static Func<HttpRequest, bool> requestPredicate = req =>
        {
            if (req.Path.StartsWithSegments("/api")) //log api requests only
            {
                return true;
            }
            return false;
        };
    }

In my StartUp.cs, this is how my code looks like

public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider, IHttpContextAccessor httpContextAccessor)
        {
            if (_env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                ////Audit.Core.Configuration.AuditDisabled = true;
            }

            app.Use(async (context, next) =>
            {
                context.Request.EnableBuffering(); ////TO:DO Change to .EnableRewind() when moving to NET_CORE_6;
                await next();
            });

            //Should be call before UseMvc(), otherwise the middleware will not be able to process MVC actions.
            app.UseApiRequestAuditing(_configuration, httpContextAccessor);

..........

The exception I am having with the setup above is as follows whenever I have an exception

at XXX.Api.Middlewares.TraceIdLoggingEnricherMiddleware.InvokeAsync(HttpContext context) in C:\XXXX\XXXX\API\XXXX.Api\Middlewares\TraceIdLoggingEnricherMiddleware.cs:line 32 at Audit.WebApi.AuditMiddleware.InvokeNextAsync(HttpContext context, Boolean includeResponseBody, Boolean includeResponseHeaders) at Audit.WebApi.AuditMiddleware.InvokeNextAsync(HttpContext context, Boolean includeResponseBody, Boolean includeResponseHeaders) at Audit.WebApi.AuditMiddleware.InvokeAsync(HttpContext context) at XXX.Api.Startup.<>c.<<Configure>b__4_0>d.MoveNext() in C:\XXXX\XXX\API\XXX.Api\Startup.cs:line 123 — End of stack trace from previous location where exception was thrown — at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication1 application) [10:07:42 ERR] Connection id "0HM8CCTJ1LSHC", Request id "0HM8CCTJ1LSHC:00000001": An unhandled exception was thrown by the application. System.ObjectDisposedException: Cannot access a closed Stream. at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) at System.IO.MemoryStream.WriteAsync(ReadOnlyMemory1 buffer, CancellationToken cancellationToken) — End of stack trace from previous location where exception was thrown — at System.IO.Pipelines.StreamPipeWriter.FlushAsyncInternal(CancellationToken cancellationToken) at System.IO.Pipelines.StreamPipeWriter.CompleteAsync(Exception exception) at Microsoft.AspNetCore.Http.StreamResponseBodyFeature.CompleteAsync() at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FireOnCompletedAwaited(Task currentTask, Stack`1 onCompleted)

Expected behavior This should not happen in the exception System.ObjectDisposedException: Cannot access a closed Stream. This is why the result on postman is returning 1. The stream has already close. Expectation is to show the exception.

Libraries:

  • Audit.WebApi.Core: 17.0.3
  • Audit.Net.MongoDB 17.0.3

Target .NET framework:

  • .NET Core 3.1

Additional context I noticed if I removed the IncludeResponseBody in the UseAuditMiddleWare, the exception is displayed correctly on postman.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Khooshamcommented, May 4, 2021

Looks like I found out the root cause. In my unhandled exception middleware, in the catch block I was throwing the exception back again if I was on development and produce a friendly message if I was on prod. Throwing the exception was causing the stream to close early. I got it fixed.

Thanks a lot for your time

0reactions
thepirat000commented, May 3, 2021

Does it fails if you disable the TraceIdLoggingEnricherMiddleware ? can you share the code of that middleware?

Note the fastest way to get support is to create a minimal compilable project that reproduces the error

Read more comments on GitHub >

github_iconTop Results From Across the Web

Keep Getting Error: Cannot access a closed Stream on Asp ...
seems you are disposing the StreamWriter sw, which in turn will dispose the memory stream passed to it. try using this
Read more >
Cannot access a closed stream error
"DocWriter" is closing your stream. When other "usings" use preceeding "usings", they "may" close the underlying stream before you are ...
Read more >
C# – getting “Cannot access a closed Stream” here
The problem is that the stream is closed (via Dispose() ) at the end of the using block. You retain a reference to...
Read more >
Web service error codes (Microsoft Dataverse) - Power Apps
This topic lists the error codes you might encounter when you debug your code.
Read more >
ASP.NET Core Hero Boilerplate - Quick Start Guide
In this post, we will talk about getting started with ASP.NET Core Hero Boilerplate Solution Template for .NET 5. Note that this Solution...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found