ASP.NET Core如何使用XmlSchemaSet验证上传的XML

5次阅读

xmlSchemaSet 验证必须显式设置 XmlResolver 和 ValidationType.Schema。需用 XmlUrlResolver 解析本地 XSD 引用,Add 后检查 count≥1;XmlReaderSettings 必须启用 ValidationType.Schema、DtdProcessing.Prohibit 并注册 ValidationEventHandler;ASP.net Core 不支持自动 Schema 验证,需手动验证 XML 流。

ASP.NET Core如何使用XmlSchemaSet验证上传的XML

XmlSchemaSet 验证前必须加载 XSD 并设置 XmlSchemaSet.XmlResolver

直接用 XmlSchemaSet 加载本地 XSD 文件却验证失败,大概率是没处理 xs:importxs:include 引用——XmlSchemaSet 默认的 XmlResolverNULL,遇到外部引用会静默跳过或抛 XmlSchemaException。必须显式赋值一个能解析本地路径的 XmlResolver,例如 new XmlUrlResolver(),并确保 XSD 路径可访问(推荐用 IWebHostEnvironment.WebRootPathIHostEnvironment.ContentRootPath 拼接绝对路径)。

  • 若 XSD 存于 wwwroot/schemas/invoice.xsd,构造 XmlResolver 时 baseUri 应设为该目录的完整路径
  • 调用 schemaSet.Add() 后检查 schemaSet.Count 是否 ≥1,避免因加载失败导致后续验证始终通过
  • 不要依赖相对路径字符串直接传给 Add()Add(null, "invoice.xsd") 在 ASP.NET Core 中几乎必然失败

验证 XML 流时必须用 XmlReaderSettings 绑定 XmlSchemaSet 并启用 ValidationType.Schema

仅把 XmlSchemaSet 塞进 XmlReaderSettings.Schemas 不够,还必须设置 settings.ValidationType = ValidationType.Schema,否则解析器完全忽略 Schema 规则。同时需注册 settings.ValidationEventHandler 捕获具体错误位置和消息,否则验证失败只抛泛型 XmlException,无法定位是元素缺失还是类型不匹配。

  • settings.DtdProcessing = DtdProcessing.Prohibit 必须显式设置,防止上传恶意 DTD 引发 XXE
  • 验证大文件时,XmlReader.Create(stream, settings)stream 必须支持 Seek(如 MemoryStream),否则会报 NotSupportedException
  • 若上传的是 IFormFile,先读入 MemoryStream 再验证,别直接用 file.OpenReadStream() —— 多数情况下该流不可回溯
var schemaSet = new XmlSchemaSet(); schemaSet.XmlResolver = new XmlUrlResolver {     Credentials = CredentialCache.DefaultCredentials }; var xsdPath = Path.Combine(env.ContentRootPath, "Schemas", "order.xsd"); schemaSet.Add(null, xsdPath); 

var settings = new XmlReaderSettings { Schemas = schemaSet, ValidationType = ValidationType.Schema, DtdProcessing = DtdProcessing.Prohibit, ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema | XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ReportValidationWarnings }; settings.ValidationEventHandler += (sender, e) => { // 记录 e.Severity、e.Message、e.Exception.LineNumber };

using var stream = new MemoryStream(); await file.CopyToAsync(stream); stream.Position = 0;

using var reader = XmlReader.Create(stream, settings); while (reader.Read()) { / 触发验证 / }

ASP.NET Core 模型绑定不支持自动 XML Schema 验证

别指望 [FromBody] 绑定到 XElement 或自定义类时自动走 XmlSchemaSet。ASP.NET Core 的 XML 输入格式化器(XmlSerializerInputFormatter)只做反序列化,不执行 Schema 校验。必须在 Action 内手动解析并验证,或封装ActionFilter 在模型绑定后介入。

  • 若用 XmlSerializer 反序列化,它本身不校验 Schema;得先用 XmlReader 过一遍验证,再用同一份流反序列化(注意流位置重置)
  • ValidateModel filter 中无法访问原始 XML 字节,所以验证必须放在 action body 或自定义 model binder
  • 不要在 Startup.ConfigureServices 里全局替换 XmlSerializerInputFormatter 来加验证——它不暴露 XmlReaderSettings 配置点,强行修改易破坏默认行为

常见错误:验证通过但实际结构不符

现象是 XmlReader.Read() 不抛异常,但业务逻辑读取 reader.GetAttribute("id") 时为 null。原因往往是 XSD 中该属性声明为 use="optional",而验证器只检查语法合法性,不强制存在性。真正的约束要靠 xs:assert(XSD 1.1)或应用层二次校验。

  • ASP.NET Core 默认使用的 .NET SDK 仅支持 XSD 1.0,xs:assert 会被忽略,别写
  • 对必填字段,XSD 中必须用 use="required";对内容格式(如日期格式),用 xs:pattern 而非正则注释
  • 验证后仍建议用 XDocument.Load(reader) 构建 dom,再用 linq to XML 抽取字段——比边读边验证更可控,也方便日志记录原始片段

XSD 加载和验证链路中,最容易被跳过的环节是 XmlResolver 设置和 ValidationType 开关,这两个漏掉,其余代码写得再全也白搭。

text=ZqhQzanResources