Hope
Hope

Reputation: 135

UseExceptionHandling failing after stream.CopyToAsync() call

I am having an issue with UseExceptionHandler in .Net Core 3.1 WebApi starts failing to trigger after copying the response body to a stream. I am using a middleware to intercept incoming requests and outgoing responses for a webapi project and logs records to a table. It works fine until the stream.CopyToAsync() method is called. Any exceptions after that does not trigger the ExceptionHandler, even though a local try/catch will still catch an exception and rethrowing the exception in the catch block still doesn't cause the ExceptionHandler to trigger.

Invoke Method

 public async Task Invoke(HttpContext context)
        {   
               Stream originalBody = context.Response.Body;
                try
                {
                
                    using var memStream = new MemoryStream();
                    context.Response.Body = memStream;
                    await _next(context).ConfigureAwait(false);
                    await LogResponse(context, originalBody, memStream).ConfigureAwait(false);


                }
                catch (Exception ex)
                {
                    throw ex; 
                }
                finally
                {
                    context.Response.Body = originalBody;
                    originalBody.Close();                    
                }
            }
        }

LogResponse Method

 private async Task LogResponse(HttpContext context, Stream originalBody, MemoryStream stream)
    {
        stream.Seek(0, SeekOrigin.Begin);
        string body = await new StreamReader(stream).ReadToEndAsync().ConfigureAwait(false);
        stream.Seek(0, SeekOrigin.Begin);
        
        //This is the line of code that breaks UseExceptionHandling pipeline
        await stream.CopyToAsync(originalBody).ConfigureAwait(false);

        var resp = new ApiLogItemRequest()
        {
            AppName = "AppName",
            CreatedBy = "AppName",
            DateTime = DateTime.Now,                
            RawBodyJson = body,
            TraceIdentifier = context.TraceIdentifier
        };

        await _logHelper.LogRawHttpBodyItem(resp);
    }

Any thoughts or hints would be much appreciated. Thanks!

Upvotes: 0

Views: 715

Answers (1)

Hope
Hope

Reputation: 135

I was able to figure out the issue. The originalBody variable was null, then an exception was being thrown by the ExceptionHandler creating a weird error loop in the middleware, causing it to fail to execute the handler's code. I'm assuming it has something to do with the context being written with a null response from the CopyToAsync() line of code, but I'm not 100% certain on the why.

I changed the code so that it insured only copying when originalBody is not null and it fixed the issue.

I recognized the ExceptionHandler hang up in the debug console. ExceptionMiddleware's Invoke only should have been called once and done, but it was followed by HandleException then Invoke again which leads me to think there was some exception in the ExceptionHandler causing it to skip out the code there.

(I didn't state originally, but the root issue was that the originalBody = context.Response.Body was being written before _next(context); In this case, there is no response body to copy. That logic should have occurred after _next(context)

System.Exception: Test exception
   at Project.Api.Middleware.ApiRequestResponseLog.Invoke(HttpContext context) in C:\GIT\Project\Project.Api\Middleware\ApiRequestResponseLog.cs:line 82
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
//These below shouldn't have occured
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

Upvotes: 1

Related Questions