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

.NET 8独立进程Azure Functions中间件记录HTTP响应问题

Azure Functions独立进程模型中间件记录HTTP请求响应问题解决方案

问题根源

  1. 请求体读取问题:原始代码读取请求体后未重置流位置,导致函数本身无法读取请求数据
  2. 响应体读取问题:直接读取响应流时,底层流已被释放;替换MemoryStream后未将内容写回原始流,导致调用方收不到响应

修正后的中间件代码

public class RequestResponseLoggingMiddleware : IFunctionsWorkerMiddleware
{
    private readonly ILogger<RequestResponseLoggingMiddleware> _logger;

    public RequestResponseLoggingMiddleware(ILogger<RequestResponseLoggingMiddleware> logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        // 处理请求体:复制流避免原始流被耗尽
        var request = await context.GetHttpRequestDataAsync();
        if (request != null && request.Body.CanRead)
        {
            using var requestMemoryStream = new MemoryStream();
            await request.Body.CopyToAsync(requestMemoryStream);
            requestMemoryStream.Seek(0, SeekOrigin.Begin);
            
            // 读取并记录请求体
            var requestBody = await new StreamReader(requestMemoryStream).ReadToEndAsync();
            _logger.LogInformation("RequestBody: {RequestBody}", requestBody);
            
            // 重置流位置,确保函数能正常读取请求数据
            requestMemoryStream.Seek(0, SeekOrigin.Begin);
            request.Body = requestMemoryStream;
        }

        // 处理响应体:替换流后需写回原始流
        var httpContext = context.GetHttpContext();
        if (httpContext != null)
        {
            var originalResponseBody = httpContext.Response.Body;
            using var responseMemoryStream = new MemoryStream();
            httpContext.Response.Body = responseMemoryStream;

            try
            {
                await next(context);

                // 读取并记录响应体
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                var responseBody = await new StreamReader(responseMemoryStream).ReadToEndAsync();
                _logger.LogInformation("ResponseBody: {ResponseBody}", responseBody);

                // 将内存流内容复制回原始响应流,确保调用方收到响应
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                await responseMemoryStream.CopyToAsync(originalResponseBody);
            }
            finally
            {
                // 恢复原始响应流,避免资源泄漏
                httpContext.Response.Body = originalResponseBody;
            }
        }
        else
        {
            // 非HTTP触发场景,直接执行后续逻辑
            await next(context);
        }
    }
}

关键要点说明

  • 请求流处理:通过MemoryStream复制原始请求流,读取后重置位置再赋值回原始流,保证函数自身能正常读取请求数据
  • 响应流处理:保存原始响应流,用MemoryStream临时替换,执行完业务逻辑后将内存流内容复制回原始流,确保调用方收到完整响应
  • 异常安全:使用finally块强制恢复原始响应流,避免异常场景下出现资源泄漏或响应丢失

启用内置HttpLogging中间件(可选)

如果需要使用官方内置的HTTP日志功能,确保安装Microsoft.AspNetCore.HttpLogging NuGet包后,在Program.cs中配置:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder =>
    {
        // 配置HttpLogging服务
        builder.Services.AddHttpLogging(options =>
        {
            options.LoggingFields = HttpLoggingFields.All;
        });

        // 添加内置HttpLogging中间件到Worker管道
        builder.UseHttpLogging();
    })
    .Build();

host.Run();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:22:34