Cannot access a closed Stream (Audit.WebApi.Core)
See original GitHub issueWhen 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(ReadOnlyMemory
1 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:
- Created 2 years ago
- Comments:6 (3 by maintainers)
Top GitHub Comments
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
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