You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:48:03