【问题标题】:Exception when intercepting responses, when the ExceptionHandlerMiddleware runs拦截响应时出现异常,当 ExceptionHandlerMiddleware 运行时
【发布时间】:2020-12-14 10:10:34
【问题描述】:

我终其一生都无法弄清楚如何解决这个问题。请帮忙。 为了简化和复制,我在 VS2019 中复制了 .NetCore3.1 MVC 模板,并添加了相关部分。

我有一个拦截器中间件,它记录所有请求和响应,并使用 Home/Error 视图显示发生了错误。

当调用成功时,拦截器会很好地记录请求和响应。但是,如果管道中发生异常,响应拦截器将失败并出现错误 Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware,并显示 HTTP 500 错误而不是 Home/Error 视图。

An exception was thrown attempting to execute the error handler.

System.ObjectDisposedException: Cannot access a closed Stream.
Object name: 'destination'.
   at System.IO.StreamHelpers.ValidateCopyToArgs(Stream source, Stream destination, Int32 bufferSize)
   at System.IO.MemoryStream.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
   at System.IO.Stream.CopyToAsync(Stream destination)
   at Test.Middleware.Interceptor.Invoke(HttpContext context) in C:\Users\micher03\source\repos\test\Middleware\Interceptor.cs:line 30
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)

Startup.cs

namespace Test
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IHostEnvironment hostingEnvironment)
        {
            Configuration = configuration;

            var builder = new ConfigurationBuilder()
                 .SetBasePath(hostingEnvironment.ContentRootPath)
                 .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                 .AddEnvironmentVariables();
            Configuration = builder.Build();

            Log.Information("Application Starting");
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {

            app.UseExceptionHandler("/Home/Error");

            app.UseMiddleware<Interceptor>();

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Error.cshtml

@model ErrorViewModel
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

拦截器

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Serilog;

namespace Test.Middleware
{
    public class Interceptor
    {
        private readonly RequestDelegate _nextRequest;

        public Interceptor(RequestDelegate nextRequest)
        {
            _nextRequest = nextRequest;
        }

        public async Task Invoke(HttpContext context)
        {
            await LogRequest(context.Request);

            var originalBodyStream = context.Response.Body;

            using (var responseBody = new MemoryStream())
            {
                context.Response.Body = responseBody;
                await _nextRequest(context);
                await LogResponse(context.Response);
                await responseBody.CopyToAsync(originalBodyStream);
            }
        }

        private async Task<string> LogRequest(HttpRequest request)
        {
            HttpRequestRewindExtensions.EnableBuffering(request);
            var buffer = new byte[Convert.ToInt32(request.ContentLength)];
            await request.Body.ReadAsync(buffer, 0, buffer.Length);
            var bodyAsText = Encoding.UTF8.GetString(buffer);
            request.Body.Seek(0, SeekOrigin.Begin);

            Log.Information("Request {Path} {Body}", request.Path, bodyAsText);
            return "OK";
        }

        private async Task<string> LogResponse(HttpResponse response)
        {
            response.Body.Seek(0, SeekOrigin.Begin);
            string bodyAsText = await new StreamReader(response.Body).ReadToEndAsync();
            response.Body.Seek(0, SeekOrigin.Begin);

            Log.Information("Response {@StatusCode} {@Body}", response.StatusCode, bodyAsText);
            return "OK";
        }
    }
}

【问题讨论】:

标签: c# exception .net-core stream interceptor


【解决方案1】:

响应内存流到达DeveloperExceptionPageMiddlewareExceptionHandlerMiddleware时关闭

如果抛出异常,您需要在 Interceptor 中设置回原始流

public async Task Invoke(HttpContext context)
{
    await LogRequest(context.Request);

    var originalBodyStream = context.Response.Body;

    using (var responseBody = new MemoryStream())
    {
        context.Response.Body = responseBody;
        try {
            await _nextRequest(context);
        } 
        catch
        {
            if(!context.Response.HasStarted)
            {
                context.Response.Body = originalResponseStream;
            }
            throw;
        }
        
        await LogResponse(context.Response);
        await responseBody.CopyToAsync(originalBodyStream);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-21
    • 1970-01-01
    相关资源
    最近更新 更多