ASP.NET Core发生错误时如何记录请求URL 现有中间件仅无错时执行怎么办
问题解答
问题1:当前编写的中间件仅无错误时执行的原因
- 代码执行顺序错误。你将响应状态码判断、日志记录逻辑全部写在了
await next.Invoke()之前,此时后续的业务处理、路由匹配、接口逻辑都还未执行,context.Response.StatusCode还处于初始默认值200,你的错误状态码判断条件自然不会触发。如果后续中间件抛出未处理异常,执行流会直接从await next.Invoke()处向上抛出,你写在调用前的逻辑早已执行完毕,自然捕获不到异常场景。 - 中间件注册顺序错误。ASP.NET Core中间件遵循「先进后出」的执行顺序,如果你的这个中间件注册在了全局异常处理中间件(如
UseExceptionHandler、UseDeveloperExceptionPage)的后方,那么未处理异常会先被前方的异常处理中间件捕获并消化,不会流到你自定义的中间件中。
问题2:未处理异常时记录请求URL与Body的实现方法
方案1:修改自定义中间件实现(兼容性最好)
调整中间件的逻辑顺序,用try-catch包裹await next.Invoke()捕获未处理异常,同时开启请求体缓冲支持重复读取即可:
// 注册中间件时注意要放到所有业务中间件的最前面,排在异常处理中间件之前 app.Use(async (context, next) => { // 开启请求体缓冲,支持重复读取 context.Request.EnableBuffering(); try { // 先调用后续中间件处理业务 await next.Invoke(); // 后续中间件执行完成后判断状态码(处理非异常类的错误,比如404、403等) if (context.Response.StatusCode is < 200 or > 299) { Console.WriteLine($"请求方法:{context.Request.Method}"); Console.WriteLine($"请求路径:{context.Request.Path}{context.Request.QueryString}"); // 如果需要记录Body,取消注释以下代码即可 // context.Request.Body.Position = 0; // using var reader = new StreamReader(context.Request.Body, leaveOpen: true); // var bodyStr = await reader.ReadToEndAsync(); // Console.WriteLine($"请求体:{bodyStr}"); // context.Request.Body.Position = 0; } } catch (Exception ex) { // 捕获未处理异常,记录信息 Console.WriteLine($"异常类型:{ex.GetType().Name},异常信息:{ex.Message}"); Console.WriteLine($"请求方法:{context.Request.Method}"); Console.WriteLine($"请求路径:{context.Request.Path}{context.Request.QueryString}"); // 如果需要记录Body,取消注释以下代码即可 // context.Request.Body.Position = 0; // using var reader = new StreamReader(context.Request.Body, leaveOpen: true); // var bodyStr = await reader.ReadToEndAsync(); // Console.WriteLine($"请求体:{bodyStr}"); // context.Request.Body.Position = 0; // 重新抛出异常,让后续异常处理中间件可以正常处理响应 throw; } });
方案2:使用内置异常处理机制(更简便)
直接通过配置UseExceptionHandler的回调实现,不需要自定义完整中间件:
app.UseExceptionHandler(appBuilder => { appBuilder.Run(async context => { var exception = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error; if (exception != null) { // 此处记录逻辑和上述中间件逻辑完全一致 Console.WriteLine($"异常信息:{exception.Message}"); Console.WriteLine($"请求URL:{context.Request.GetEncodedUrl()}"); } // 可自定义返回给前端的错误响应 context.Response.StatusCode = 500; await context.Response.WriteAsJsonAsync(new { msg = "系统异常" }); }); });
注意:如果需要记录请求体,必须在所有读取请求体的中间件之前调用context.Request.EnableBuffering(),否则请求体读取一次后就无法再次读取。
内容的提问来源于stack exchange,提问作者Noah Bergh
相关产品推荐
相关产品推荐

