如何配置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报文序列化日志开关,无需额外编写拦截逻辑,只需两步配置:
- 在
ConfigureServices中注册SoapCore时,开启报文日志选项:
public void ConfigureServices(IServiceCollection services) { services.AddSoapCore(options => { // 开启SOAP报文日志输出 options.LogSoapMessage = true; // 同时输出SOAP头内容 options.LogSoapHeaders = true; }); // 其余原有服务注册逻辑保持不变 }
- 在原有日志配置中添加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实际收到的内容完全一致,不存在日志与实际返回不符的问题,重点关注响应内容的场景优先选用。
实现步骤:
- 编写自定义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; } }
- 在
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
相关产品推荐
相关产品推荐

