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

如何拦截并记录Web API中所有进出HTTP请求?

记录Web API传入与传出HTTP请求的最佳实现方案

一、传入请求:沿用ActionFilter(已有方案补充)

你已经知道用ActionFilter处理传入请求,这里补充核心逻辑:实现IAsyncActionFilter,在OnActionExecutionAsync方法中读取请求的方法、路径、Headers、Body等信息记录日志;待Action执行完成后,再记录响应的状态码、响应内容等。

二、传出请求:用自定义DelegatingHandler拦截HttpClient请求

要拦截你的API向外发起的HTTP请求,最佳方案是自定义HttpClient的DelegatingHandler——这是HttpClient管道的原生扩展点,能在请求发送前、响应返回后插入日志逻辑,完全覆盖你用client.GetAsync这类方法发起的请求。

1. 实现自定义日志Handler

创建继承自DelegatingHandler的类,重写SendAsync方法完成日志记录:

public class RequestLoggingHandler : DelegatingHandler
{
    private readonly ILogger<RequestLoggingHandler> _logger;

    public RequestLoggingHandler(ILogger<RequestLoggingHandler> logger)
    {
        _logger = logger;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 记录传出请求详情
        string requestBody = string.Empty;
        if (request.Content != null)
        {
            // 处理流类型请求体,避免消耗流导致后续无法使用
            var contentStream = await request.Content.ReadAsStreamAsync(cancellationToken);
            var originalPosition = contentStream.Position;
            using var reader = new StreamReader(contentStream);
            requestBody = await reader.ReadToEndAsync(cancellationToken);
            contentStream.Position = originalPosition;
        }

        _logger.LogInformation(
            "传出请求: 方法={Method}, 地址={Uri}, Headers={Headers}, Body={Body}",
            request.Method.Method,
            request.RequestUri,
            string.Join("; ", request.Headers.Select(h => $"{h.Key}: {string.Join(",", h.Value)}")),
            requestBody
        );

        // 执行请求获取响应
        var response = await base.SendAsync(request, cancellationToken);

        // 记录响应详情
        string responseBody = string.Empty;
        if (response.Content != null)
        {
            var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);
            var originalPosition = contentStream.Position;
            using var reader = new StreamReader(contentStream);
            responseBody = await reader.ReadToEndAsync(cancellationToken);
            contentStream.Position = originalPosition;
        }

        _logger.LogInformation(
            "响应接收: 状态码={StatusCode}, 原因={Reason}, Headers={Headers}, Body={Body}",
            (int)response.StatusCode,
            response.ReasonPhrase,
            string.Join("; ", response.Headers.Select(h => $"{h.Key}: {string.Join(",", h.Value)}")),
            responseBody
        );

        return response;
    }
}

2. 注册Handler到HttpClient管道

根据你的需求选择注册方式:

  • 全局生效:所有通过IHttpClientFactory创建的HttpClient都会自动应用这个日志Handler:
// 在Program.cs中注册
builder.Services.AddTransient<RequestLoggingHandler>();
builder.Services.AddHttpClient().AddHttpMessageHandler<RequestLoggingHandler>();
  • 针对特定HttpClient生效:如果只需要拦截调用某第三方API的请求,可以给HttpClient命名后注册:
builder.Services.AddTransient<RequestLoggingHandler>();
builder.Services.AddHttpClient("ThirdPartyApiClient")
    .AddHttpMessageHandler<RequestLoggingHandler>();

// 使用时通过命名获取实例
var client = _httpClientFactory.CreateClient("ThirdPartyApiClient");
var response = await client.GetAsync("/api/my/endpoint/name");

3. 关键注意事项

  • 处理大文件或流类型的请求/响应时,必须先复制流再读取,读完后重置流位置,否则后续操作会因为流已耗尽而失败。
  • 务必过滤敏感信息:比如Authorization头、请求体里的密码等,避免泄露到日志中。可以在日志前对Headers和Body做清洗处理。

三、关联传入与传出请求日志(可选优化)

为了方便排查问题,建议给每个请求分配唯一的Trace ID,将传入请求和它触发的传出请求日志关联起来:

  1. 在ActionFilter中获取当前请求的HttpContext.TraceIdentifier,记录到传入请求日志中。
  2. 发起传出请求时,将这个Trace ID添加到请求头:
client.DefaultRequestHeaders.Add("X-Trace-Id", HttpContext.TraceIdentifier);
  1. 在RequestLoggingHandler中读取这个X-Trace-Id头,写入传出请求日志,这样就能通过Trace ID追踪完整的请求链路。

内容的提问来源于stack exchange,提问作者deepo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 18:01:20