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

如何配置SoapCore记录输出WUA交互的XML请求响应报文

问题背景

使用Upsync工具为Windows Update Agent(WUA)手动提供更新服务时,需要记录并查看服务与WUA交互过程中的XML请求与响应内容,响应XML为重点关注对象。
当前服务基于.NET 6的WebHostBuilder构建,采用SoapCore作为SOAP服务中间件,已添加控制台日志、Debug日志、EventSource日志输出,现有配置代码如下:

var host = new WebHostBuilder()
.UseUrls($"http://{bindEndpoint}:{bindPort}")
.UseStartup<UpdateServerStartup>()
.UseKestrel()
.ConfigureKestrel((context, opts) => { })
.ConfigureLogging((hostingContext, logging) =>
{
    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole();
    logging.AddDebug();
    logging.AddEventSourceLogger();
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
    var configDictionary = new Dictionary<string, string>()
    {
        { "metadata-path", metadataPath },
        { "content-path", contentPath },
        { "service-config-json", serviceConfigurationJson },
        { "content-http-root", $"http://{bindEndpoint}:{bindPort}/microsoftupdate/content" },
    };

    config.AddInMemoryCollection(configDictionary);
})
.Build();
现存问题
  • 服务启动运行、触发WUA更新检测后,控制台仅输出基础运行日志、请求起止日志、SoapCore.SoapEndpointMiddleware记录的操作接收提示
  • 可观测到WUA依次发起GetConfig、GetConfig2、GetCookie、SyncUpdates等SOAP操作请求,每个请求均返回200状态的XML响应
  • 日志中未输出具体的XML报文内容,即使添加logging.SetMinimumLevel(LogLevel.Trace);将日志最低级别设置为Trace,依然无法打印出响应的XML内容
实现方案

方案1:开启SoapCore内置报文日志(配置简单,适配高版本SoapCore)

SoapCore内置了SOAP报文序列化日志开关,无需额外编写拦截逻辑,只需两步配置:

  1. 在ConfigureServices中注册SoapCore时,开启报文日志选项:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSoapCore(options =>
    {
        // 开启SOAP报文日志输出
        options.LogSoapMessage = true;
        // 同时输出SOAP头内容
        options.LogSoapHeaders = true;
    });
    // 其余原有服务注册逻辑保持不变
}
  1. 在原有日志配置中添加SoapCore命名空间的日志级别过滤,确保Trace级别日志能被输出:
.ConfigureLogging((hostingContext, logging) =>
{
    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole();
    logging.AddDebug();
    logging.AddEventSourceLogger();
    // 新增:指定SoapCore分类日志最低输出级别为Trace
    logging.AddFilter("SoapCore", LogLevel.Trace);
    logging.SetMinimumLevel(LogLevel.Trace);
})

配置生效后,控制台会输出SoapCore分类下的两类Trace日志:反序列化前接收到的原始SOAP请求XML、序列化完成后待返回的SOAP响应XML。

方案2:自定义中间件拦截原始流(兼容性最好,报文100%准确)

如果使用的SoapCore版本不支持上述内置配置开关,直接在请求管道添加自定义中间件,拦截原始HTTP请求、响应流读取报文,该方案拿到的报文内容和WUA实际收到的内容完全一致,不存在日志与实际返回不符的问题,重点关注响应内容的场景优先选用。
实现步骤:

  1. 编写自定义SOAP报文日志中间件:
public class SoapMessageLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<SoapMessageLoggingMiddleware> _logger;

    public SoapMessageLoggingMiddleware(RequestDelegate next, ILogger<SoapMessageLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 仅拦截SOAP服务路径请求,过滤静态资源等无关请求
        if (!context.Request.Path.StartsWithSegments("/microsoftupdate", StringComparison.OrdinalIgnoreCase))
        {
            await _next(context);
            return;
        }

        // 读取原始请求XML
        context.Request.EnableBuffering();
        using var requestReader = new StreamReader(context.Request.Body, leaveOpen: true);
        var requestXml = await requestReader.ReadToEndAsync();
        _logger.LogInformation("SOAP请求路径:{RequestPath}\n请求报文:\n{RequestContent}",
            context.Request.Path, requestXml);
        // 重置请求流位置,供后续SoapCore中间件正常读取
        context.Request.Body.Position = 0;

        // 替换响应流为内存流,用于拦截响应内容
        var originalResponseStream = context.Response.Body;
        using var cachedResponseStream = new MemoryStream();
        context.Response.Body = cachedResponseStream;

        // 执行后续请求管道逻辑
        await _next(context);

        // 读取原始响应XML
        cachedResponseStream.Position = 0;
        using var responseReader = new StreamReader(cachedResponseStream);
        var responseXml = await responseReader.ReadToEndAsync();
        _logger.LogInformation("SOAP响应状态码:{StatusCode}\n响应报文:\n{ResponseContent}",
            context.Response.StatusCode, responseXml);

        // 将响应内容写回原始输出流,正常返回给客户端
        cachedResponseStream.Position = 0;
        await cachedResponseStream.CopyToAsync(originalResponseStream);
        context.Response.Body = originalResponseStream;
    }
}
  1. 在Startup.cs的Configure方法中注册中间件,必须放在SoapCore端点注册代码之前,否则拦截不到响应内容:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他原有中间件逻辑...
    
    // 注意:必须在app.UseSoapEndpoint()之前调用
    app.UseMiddleware<SoapMessageLoggingMiddleware>();

    // 原有SoapCore端点配置,示例:
    // app.UseSoapEndpoint<IUpdateService>("/microsoftupdate/webservices.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
}

该方案可根据需求扩展,比如将XML格式化后输出、脱敏敏感字段、将报文持久化到本地文件等。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:54:09