ASP.NET Core中如何在OnActionExecutionAsync内记录HTTP请求与响应
ASP.NET Core 在OnActionExecutionAsync阶段记录请求响应日志的实现方法
你需要自定义继承自ActionFilterAttribute的过滤器,重写OnActionExecutionAsync方法即可实现,由于ASP.NET Core默认请求、响应流仅支持一次读取,需要做特殊处理避免影响后续业务逻辑,具体实现步骤如下:
1. 自定义请求响应日志过滤器
首先实现过滤器的核心逻辑,包含请求内容读取、响应内容缓存读取、日志记录三个部分:
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; using System.IO; using System.Text; public class RequestResponseLoggingFilter : ActionFilterAttribute { private readonly ILogger<RequestResponseLoggingFilter> _logger; public RequestResponseLoggingFilter(ILogger<RequestResponseLoggingFilter> logger) { _logger = logger; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var httpContext = context.HttpContext; // ------------- 记录请求内容 ------------- // 启用请求体重复读取,避免后续业务逻辑无法获取请求参数 httpContext.Request.EnableBuffering(); var requestBody = await ReadRequestBodyAsync(httpContext.Request); _logger.LogInformation("请求日志:\n请求地址:{Url}\n请求方法:{Method}\n请求头:{Headers}\n请求体:{Body}", httpContext.Request.Path, httpContext.Request.Method, string.Join(" | ", httpContext.Request.Headers.Select(h => $"{h.Key}:{h.Value}")), requestBody); // 缓存原始响应流,替换为内存流暂存响应内容 var originalResponseBody = httpContext.Response.Body; using var responseMemoryStream = new MemoryStream(); httpContext.Response.Body = responseMemoryStream; // 执行Action后续业务逻辑 var resultContext = await next(); // ------------- 记录响应内容 ------------- responseMemoryStream.Seek(0, SeekOrigin.Begin); var responseBody = await new StreamReader(responseMemoryStream, Encoding.UTF8).ReadToEndAsync(); _logger.LogInformation("响应日志:\n响应状态码:{StatusCode}\n响应头:{Headers}\n响应体:{Body}", httpContext.Response.StatusCode, string.Join(" | ", httpContext.Response.Headers.Select(h => $"{h.Key}:{h.Value}")), responseBody); // 将暂存的响应内容写回原始流,确保客户端能正常收到响应 responseMemoryStream.Seek(0, SeekOrigin.Begin); await responseMemoryStream.CopyToAsync(originalResponseBody); httpContext.Response.Body = originalResponseBody; } /// <summary> /// 读取请求体内容,读取后重置流位置 /// </summary> private async Task<string> ReadRequestBodyAsync(HttpRequest request) { request.Body.Position = 0; using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true); var bodyContent = await reader.ReadToEndAsync(); request.Body.Position = 0; return bodyContent; } }
2. 注册过滤器
你可以根据需求选择全局生效或者局部生效:
- 全局注册(所有Controller的Action都生效):在
Program.cs中添加配置
builder.Services.AddControllers(options => { options.Filters.Add<RequestResponseLoggingFilter>(); });
- 局部生效(仅指定Controller/Action生效):给对应类或方法加特性即可
[TypeFilter(typeof(RequestResponseLoggingFilter))] public class TestController : ControllerBase { // 接口逻辑 }
注意事项:
- 如果是文件上传、二进制下载这类接口,建议添加过滤逻辑跳过日志记录,避免内存占用过高或者日志内容乱码
- 生产环境如果请求响应包含密码、身份证号等敏感信息,需要在日志记录前添加脱敏逻辑,避免信息泄露
内容的提问来源于stack exchange,提问作者Hayk Beroyan
相关产品推荐
相关产品推荐

