如何实现ASP.NET Core WebApi兼容旧版WCF客户端XML POST请求
问题1:WCF默认序列化器认知确认
你的认知完全准确。WCF默认对标注[DataContract]/[DataMember]的契约类型使用DataContractSerializer执行XML序列化,只有显式给操作/服务契约标记[XmlSerializerFormat]时才会切换为XmlSerializer。你遇到的http://schemas.datacontract.org/开头的命名空间、i前缀对应的XMLSchema-instance命名空间,都是DataContractSerializer序列化时默认生成的标准声明。
问题2:兼容实现方案
完全可以通过自定义InputFormatter/OutputFormatter实现兼容,这是对现有业务侵入最小的方案,核心思路是替换.NET Core WebAPI内置的基于XmlSerializer的XML处理逻辑,改用和WCF一致的DataContractSerializer做序列化/反序列化,天然适配WCF生成的命名空间规则,具体实现如下:
实现步骤
- 移除默认内置的XML序列化格式化器,避免其拦截请求抛出命名空间未定义异常
- 自定义基于
DataContractSerializer的请求格式化器,处理WCF客户端传入的XML反序列化 - 自定义基于
DataContractSerializer的响应格式化器,输出匹配旧WCF契约命名空间规则的XML响应 - 将自定义格式化器注入到MVC配置中,设置为高优先级匹配对应Content-Type的请求
代码实现
1. 自定义请求InputFormatter
public class WcfCompatibleInputFormatter : TextInputFormatter { public WcfCompatibleInputFormatter() { // 匹配WCF客户端常用的请求Content-Type SupportedMediaTypes.Add("text/xml"); SupportedMediaTypes.Add("application/xml"); SupportedMediaTypes.Add("application/soap+xml"); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); } protected override bool CanReadType(Type type) { // 可按需增加过滤规则,比如仅处理特定命名空间下的契约DTO return true; } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { var requestStream = context.HttpContext.Request.Body; using var xmlReader = XmlReader.Create(requestStream, new XmlReaderSettings() { // 忽略多余的命名空间校验,兼容非标准声明 DtdProcessing = DtdProcessing.Ignore }); var serializer = new DataContractSerializer(context.ModelType); var result = serializer.ReadObject(xmlReader); return await InputFormatterResult.SuccessAsync(result); } }
2. 自定义响应OutputFormatter
public class WcfCompatibleOutputFormatter : TextOutputFormatter { // 配置为和旧WCF服务完全一致的契约命名空间,保证客户端校验通过 private const string ContractNamespace = "http://schemas.datacontract.org/2004/07/你的旧WCF契约模型命名空间"; public WcfCompatibleOutputFormatter() { SupportedMediaTypes.Add("text/xml"); SupportedMediaTypes.Add("application/xml"); SupportedMediaTypes.Add("application/soap+xml"); SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); } protected override bool CanWriteType(Type type) { return true; } public override async Task WriteResponseBodyAsync(OutputFormatterContext context, Encoding encoding) { var responseStream = context.HttpContext.Response.Body; var serializer = new DataContractSerializer(context.Object.GetType(), new DataContractSerializerSettings { RootNamespace = ContractNamespace }); using var xmlWriter = XmlWriter.Create(responseStream, new XmlWriterSettings { Encoding = encoding, Indent = false }); serializer.WriteObject(xmlWriter, context.Object); await xmlWriter.FlushAsync(); } }
3. 注册格式化器
在Program.cs中配置MVC服务时,注入自定义格式化器:
builder.Services.AddControllers(options => { // 移除内置XML格式化器 var builtinXmlInput = options.InputFormatters.OfType<XmlSerializerInputFormatter>().FirstOrDefault(); if (builtinXmlInput != null) options.InputFormatters.Remove(builtinXmlInput); var builtinXmlOutput = options.OutputFormatters.OfType<XmlSerializerOutputFormatter>().FirstOrDefault(); if (builtinXmlOutput != null) options.OutputFormatters.Remove(builtinXmlOutput); // 注入自定义WCF兼容格式化器,插入到集合头部保证优先匹配 options.InputFormatters.Insert(0, new WcfCompatibleInputFormatter()); options.OutputFormatters.Insert(0, new WcfCompatibleOutputFormatter()); });
特殊场景处理
- 如果旧WCF客户端使用SOAP信封格式传输(请求带
<s:Envelope>/<s:Body>节点),只需要在InputFormatter的读取逻辑中先提取Body节点下的业务契约内容,在OutputFormatter的写入逻辑中给响应内容包裹对应SOAP信封结构即可,核心序列化逻辑不需要改动。 - 如果个别字段名、命名空间和旧契约存在差异,直接在DTO类上通过
[DataContract(Name = "", Namespace = "")]、[DataMember(Name = "")]特性配置对齐即可,不需要修改格式化器代码。 DataContractSerializer默认输出就会携带WCF客户端要求的xmlns:i实例命名空间声明,不需要额外配置即可通过客户端的契约校验。
内容的提问来源于stack exchange,提问作者pera
相关产品推荐
相关产品推荐

