.NET Core 3.1 Web API含DOCTYPE的XML POST请求反序列化报错求助
解决.NET Core 3.1 API处理含DOCTYPE的XML请求反序列化失败问题
你的问题根源在于:当XML请求包含DOCTYPE声明时,默认的XML序列化器(无论是XmlSerializer还是DataContractSerializer)会尝试解析外部DTD文件,但.NET Core出于安全考虑,默认禁止了外部实体的解析,导致反序列化过程抛出异常,返回400验证错误。
为什么会失败?
.NET Core中XmlReader的默认DtdProcessing配置是Prohibit,这意味着一旦检测到DOCTYPE声明就会直接抛出异常。你的请求里的DOCTYPE指向了一个外部DTD地址,序列化器尝试加载它时被安全策略阻止,最终触发了"An error occurred while deserializing input data."的错误。
解决方案:自定义XML格式化器,修改DTD处理策略
我们需要自定义XML输入格式化器,调整XmlReaderSettings中的DtdProcessing选项,同时禁用外部实体解析以保证安全。下面分两种序列化器的情况给出解决方案:
情况1:使用XmlSerializer(对应你代码中的AddXmlSerializerFormatters())
- 创建自定义的
XmlSerializerInputFormatter,重写CreateXmlReader方法:
using Microsoft.AspNetCore.Mvc.Formatters; using System.Xml; public class CustomXmlSerializerInputFormatter : XmlSerializerInputFormatter { public CustomXmlSerializerInputFormatter(MvcOptions options) : base(options) { } protected override XmlReader CreateXmlReader(Stream readStream, Encoding encoding) { var readerSettings = new XmlReaderSettings { // 忽略DOCTYPE声明,继续反序列化XML内容 DtdProcessing = DtdProcessing.Ignore, // 禁用外部实体解析,防止XXE攻击 XmlResolver = null }; return XmlReader.Create(readStream, readerSettings); } }
- 在
Startup.cs中替换默认的XmlSerializerInputFormatter:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; // 移除默认的XmlSerializer输入格式化器 var defaultXmlInputFormatter = options.InputFormatters.OfType<XmlSerializerInputFormatter>().FirstOrDefault(); if (defaultXmlInputFormatter != null) { options.InputFormatters.Remove(defaultXmlInputFormatter); } // 添加自定义的XmlSerializer输入格式化器 options.InputFormatters.Add(new CustomXmlSerializerInputFormatter(options)); }) .AddXmlDataContractSerializerFormatters(); // 如果不需要DataContractSerializer可以移除这行 }
情况2:使用DataContractSerializer(对应你代码中的AddXmlDataContractSerializerFormatters())
如果你的API实际使用的是DataContractSerializer,可以创建对应的自定义格式化器:
- 自定义
XmlDataContractSerializerInputFormatter:
using Microsoft.AspNetCore.Mvc.Formatters; using System.Xml; using System.Xml.Serialization; public class CustomXmlDataContractSerializerInputFormatter : XmlDataContractSerializerInputFormatter { public CustomXmlDataContractSerializerInputFormatter(MvcOptions options) : base(options) { } protected override XmlDictionaryReader CreateXmlReader(Stream readStream, Encoding encoding) { var readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, XmlResolver = null }; var xmlReader = XmlReader.Create(readStream, readerSettings); return XmlDictionaryReader.CreateDictionaryReader(xmlReader); } }
- 在
Startup.cs中替换默认的XmlDataContractSerializerInputFormatter:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.RespectBrowserAcceptHeader = true; // 移除默认的DataContractSerializer输入格式化器 var defaultDcInputFormatter = options.InputFormatters.OfType<XmlDataContractSerializerInputFormatter>().FirstOrDefault(); if (defaultDcInputFormatter != null) { options.InputFormatters.Remove(defaultDcInputFormatter); } // 添加自定义的DataContractSerializer输入格式化器 options.InputFormatters.Add(new CustomXmlDataContractSerializerInputFormatter(options)); }) .AddXmlSerializerFormatters(); // 如果不需要XmlSerializer可以移除这行 }
额外说明
- 如果你的业务确实需要解析DTD(而不是忽略它),可以将
DtdProcessing设置为DtdProcessing.Parse,但务必保持XmlResolver = null,避免加载外部实体导致安全风险(比如XXE注入攻击)。 - 你的
Name实体类和Transform控制器方法不需要做任何修改,只需要调整Startup.cs中的格式化器配置即可。
内容的提问来源于stack exchange,提问作者Naveed Ahmed
相关产品推荐
相关产品推荐

