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

.NET 6 REST服务中HTTP消息处理器注册无效,求正确实现方法

.NET 6中请求日志处理器未生效问题的修复方案

问题分析

你当前的实现存在两处核心问题:

  1. 组件用途混淆:如果需求是记录当前REST服务收到的请求和响应,DelegatingHandler是用于HttpClient发送请求时的拦截器,并非服务端接收请求的拦截组件,属于用错工具。
  2. 代码实现与注册错误:即便要给HttpClient做请求日志,原代码也存在异步处理不当、响应内容读取错误、生命周期配置错误的问题。

场景一:记录REST服务自身接收的请求与响应(服务端日志)

这种场景下应该使用中间件而非DelegatingHandler,实现步骤如下:

1. 编写日志中间件

public class BongLogMiddleware
{
    private readonly RequestDelegate _next;

    public BongLogMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 读取并记录请求内容
        var requestPath = context.Request.Path;
        context.Request.EnableBuffering();
        var requestBody = await new StreamReader(context.Request.Body, leaveOpen: true).ReadToEndAsync();
        context.Request.Body.Seek(0, SeekOrigin.Begin);

        // 拦截响应流,记录响应内容
        var originalResponseBody = context.Response.Body;
        using var responseBuffer = new MemoryStream();
        context.Response.Body = responseBuffer;

        // 继续执行后续请求流程
        await _next(context);

        // 读取响应内容并写回原始流
        responseBuffer.Seek(0, SeekOrigin.Begin);
        var responseBody = await new StreamReader(responseBuffer).ReadToEndAsync();
        responseBuffer.Seek(0, SeekOrigin.Begin);
        await responseBuffer.CopyToAsync(originalResponseBody);
        context.Response.Body = originalResponseBody;

        // 写入日志文件
        var logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 请求路径: {requestPath}{Environment.NewLine}" +
                       $"请求内容: {requestBody}{Environment.NewLine}" +
                       $"响应内容: {responseBody}{Environment.NewLine}{Environment.NewLine}";
        File.AppendAllText(@"D:\temp\restbonglog.txt", logEntry);
    }
}

2. 注册中间件

在Program.cs中,将中间件注册到请求管道(注意顺序,需放在路由中间件之前):

var app = builder.Build();

// 注册日志中间件
app.UseMiddleware<BongLogMiddleware>();

// 其他标准中间件
app.UseRouting();
app.UseAuthorization();

app.MapControllers();

app.Run();

场景二:记录服务端作为HttpClient调用外部API的请求与响应(客户端日志)

如果需求是给服务端内部调用外部API的HttpClient做日志,修正后的实现如下:

1. 修正BongLogClient代码

public class BongLogClient : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 发送请求获取响应
        var response = await base.SendAsync(request, cancellationToken);
        
        // 读取响应内容(需重新设置Content,因为流只能读取一次)
        var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
        response.Content = new StringContent(responseContent, response.Content.Headers.ContentType);

        // 写入日志
        var logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 请求路径: {request.RequestUri.LocalPath}{Environment.NewLine}" +
                       $"响应内容: {responseContent}{Environment.NewLine}{Environment.NewLine}";
        File.AppendAllText(@"D:\temp\restbonglog.txt", logEntry);

        return response;
    }
}

2. 正确注册Handler与HttpClient

在Program.cs中,将BongLogClient注册为Transient生命周期(HttpClientFactory要求Handler使用此生命周期),再绑定到HttpClient:

// 注册日志Handler
builder.Services.AddTransient<BongLogClient>();

// 方式一:给所有HttpClient添加此Handler(全局生效)
builder.Services.ConfigureAll<HttpClientFactoryOptions>(options =>
{
    options.HttpMessageHandlerBuilderActions.Add(builder =>
    {
        builder.AdditionalHandlers.Add(builder.Services.GetRequiredService<BongLogClient>());
    });
});

// 方式二:给指定命名的HttpClient添加此Handler(推荐,按需生效)
builder.Services.AddHttpClient("ExternalApiClient")
    .AddHttpMessageHandler<BongLogClient>();

关键修正点

  • 原代码用ContinueWith处理异步逻辑易引发异常,改为标准await写法
  • body.ToString()仅返回HttpContent类型名,需用ReadAsStringAsync()读取实际响应内容
  • DelegatingHandler必须注册为Transient,避免并发请求时的生命周期冲突
  • 读取响应内容后需重新设置Content,确保后续流程能正常读取响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:24:05