含DOCTYPE的cXML无法通过C#接口提交,求解决方案
解决带DOCTYPE声明的cXML在Asp.Net Core中反序列化时的400错误
问题背景
通过VS的「粘贴特殊内容->XML转类」功能基于Coupa测试文件生成了cXML的C#实体类,结构无问题。但在Swagger提交包含<!DOCTYPE>声明的XML请求时,接口返回400错误,提示「xml字段是必填项」;移除DOCTYPE声明后请求正常。
需求限制:无法预先剔除DOCTYPE声明,且需要保留完整XML内容存入本地文件;由于DOCTYPE与cXML根元素为同级关系,无法通过添加DocType对象解决。
问题原因
Asp.Net Core默认的XML模型绑定器无法将包含DOCTYPE的完整XML文档映射到XElement参数——XElement仅代表XML元素节点,不包含DOCTYPE这类文档级元数据,导致绑定失败,触发必填字段验证错误。
解决方案
放弃直接使用XElement作为请求参数,改为接收原始字符串或请求流,完整获取包含DOCTYPE的XML内容,再手动解析为cXML实体类,同时保存完整XML到本地。
修改后的代码示例
方式1:接收原始字符串
[HttpPost(Name = "PostPurchaseOrder")] public ContentResult Post([FromBody] string xmlContent) { // 保存完整XML到本地文件 System.IO.File.WriteAllText(@"C:\your\storage\path\cxml_request.xml", xmlContent); cXML cxml = new cXML(); var serializer = new XmlSerializer(typeof(cXML)); // 配置XmlReader允许解析DTD,避免DOCTYPE被忽略 var readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse }; using (var stringReader = new StringReader(xmlContent)) using (var xmlReader = XmlReader.Create(stringReader, readerSettings)) { cxml = (cXML)serializer.Deserialize(xmlReader); } // 执行后续业务逻辑 return Content("请求处理完成"); }
方式2:直接读取请求流
如果担心字符串处理的性能问题,可直接读取请求体流:
[HttpPost(Name = "PostPurchaseOrder")] public async Task<ContentResult> Post() { // 读取完整请求体内容 using var streamReader = new StreamReader(Request.Body); var xmlContent = await streamReader.ReadToEndAsync(); // 保存完整XML到本地文件 System.IO.File.WriteAllText(@"C:\your\storage\path\cxml_request.xml", xmlContent); cXML cxml = new cXML(); var serializer = new XmlSerializer(typeof(cXML)); var readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse }; using (var stringReader = new StringReader(xmlContent)) using (var xmlReader = XmlReader.Create(stringReader, readerSettings)) { cxml = (cXML)serializer.Deserialize(xmlReader); } // 执行后续业务逻辑 return Content("请求处理完成"); }
关键注意事项
- 必须设置
XmlReaderSettings.DtdProcessing = DtdProcessing.Parse,否则XmlReader会默认忽略或拒绝处理DOCTYPE声明,导致解析异常。 - 保存文件时注意路径权限,确保应用程序有写入目标目录的权限。
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

