You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XML验证时第三方DTD间歇性抛HTTP 400错误问题求助

第三方DTD间歇性返回HTTP 400导致XML验证失败

问题详情

验证XML时,第三方DTD服务间歇性返回HTTP 400(Bad Request)错误,错误栈如下:

打开外部DTD 'http://example.com/fake/path/MyDTD.dtd' 时发生错误:响应状态码表示请求失败: 400 (Bad Request)。
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.PushExternalEntityOrSubset(String publicId, String systemId, Uri baseUri, String entityName)
at System.Xml.XmlTextReaderImpl.DtdParserProxy_PushExternalSubset(String systemId, String publicId)
at System.Xml.XmlTextReaderImpl.DtdParserProxy.System.Xml.IDtdParserAdapter.PushExternalSubset(String systemId, String publicId)
at System.Xml.DtdParser.ParseExternalSubset()
at System.Xml.DtdParser.ParseInDocumentDtd(Boolean saveInternalSubset)
at System.Xml.DtdParser.Parse(Boolean saveInternalSubset)
at System.Xml.DtdParser.System.Xml.IDtdParser.ParseInternalDtd(IDtdParserAdapter adapter, Boolean saveInternalSubset)
at System.Xml.XmlTextReaderImpl.ParseDtd()
at System.Xml.XmlTextReaderImpl.ParseDoctypeDecl()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlValidatingReaderImpl.Read()
at MyApi.Infrastructure.Serializers.MyCustomXmlSerializer.ValidateAgainstDtd(String xml, Boolean throwOnFailure)
in D:\a\1\s\src\Infrastructure\Serializers\MyCustomXmlSerializer.cs:line 198

当前使用的XML验证代码:

public bool ValidateAgainstDtd(string xml, bool throwOnFailure = false)
{
    var messages = new List<string>();

    try
    {
        var settings = new XmlReaderSettings
        {
            ValidationType = ValidationType.DTD,
            DtdProcessing = DtdProcessing.Parse,
            XmlResolver = new XmlPreloadedResolver(new XmlUrlResolver())
        };

        settings.ValidationEventHandler += (sender, args) => messages.Add(args.Message);

        using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
        using var reader = XmlReader.Create(stream, settings);

        while (reader.Read()) { }  //this is line 198 in the exception
    }
    catch (Exception e)
    {
        messages.Add($"DTD Validation Exception: {e.Message}{Environment.NewLine}{e.StackTrace}");
    }

    if (messages.Count == 0)
    {
        return true;
    }

    if (throwOnFailure)
    {
        throw new CustomXmlSerializationException(messages.ToArray());
    }

    return false;
}

附加信息

  • 仅特定调用间歇性失败,其他场景该调用可正常运行
  • 本地NUnit集成测试无问题,仅预发环境出现间歇性DTD拉取失败
  • 无其他XML验证错误,序列化模型在多调用中通用

可能的原因与解决办法

1. 第三方服务限流/请求频率限制

预发环境的请求量可能触发了第三方DTD服务的限流策略,导致间歇性400错误。

  • 解决办法:
    • 联系第三方服务提供商确认请求频率限制,调整调用频率
    • 实现本地DTD缓存:将DTD文件下载到本地,验证时直接读取本地文件,避免频繁请求第三方服务。示例修改:
      // 替换XmlResolver为本地文件读取
      var settings = new XmlReaderSettings
      {
          ValidationType = ValidationType.DTD,
          DtdProcessing = DtdProcessing.Parse,
          XmlResolver = new LocalDtdResolver()
      };
      
      // 自定义LocalDtdResolver示例
      public class LocalDtdResolver : XmlResolver
      {
          public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
          {
              // 根据DTD的systemId映射到本地文件路径
              if (absoluteUri.AbsolutePath.EndsWith("MyDTD.dtd"))
              {
                  string localPath = Path.Combine(AppContext.BaseDirectory, "DTDs", "MyDTD.dtd");
                  return new FileStream(localPath, FileMode.Open, FileAccess.Read);
              }
              throw new FileNotFoundException($"DTD file not found for {absoluteUri}");
          }
      }
      

2. 预发环境网络波动

预发环境与第三方服务之间的网络可能存在间歇性波动,比如DNS解析延迟、连接超时或请求头不符合要求。

  • 解决办法:
    • 检查预发环境的网络连通性,确认能稳定访问第三方DTD地址
    • 自定义XmlUrlResolver,添加重试逻辑和自定义请求头:
      public class RetryXmlUrlResolver : XmlUrlResolver
      {
          private const int MaxRetries = 3;
          private readonly TimeSpan _retryDelay = TimeSpan.FromSeconds(1);
      
          public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
          {
              int retryCount = 0;
              while (retryCount < MaxRetries)
              {
                  try
                  {
                      // 添加自定义请求头,比如User-Agent
                      var request = WebRequest.Create(absoluteUri);
                      request.Headers.Add("User-Agent", "MyApi/1.0");
                      using var response = request.GetResponse();
                      return response.GetResponseStream();
                  }
                  catch (WebException ex) when (((HttpWebResponse)ex.Response)?.StatusCode == HttpStatusCode.BadRequest && retryCount < MaxRetries - 1)
                  {
                      retryCount++;
                      Thread.Sleep(_retryDelay);
                  }
              }
              return base.GetEntity(absoluteUri, role, ofObjectToReturn);
          }
      }
      
      // 使用RetryXmlUrlResolver替换原XmlResolver
      var settings = new XmlReaderSettings
      {
          ValidationType = ValidationType.DTD,
          DtdProcessing = DtdProcessing.Parse,
          XmlResolver = new XmlPreloadedResolver(new RetryXmlUrlResolver())
      };
      

3. XML中的DTD引用存在动态参数问题

如果XML中的DTD引用包含动态生成的参数(比如时间戳、签名),可能存在参数生成错误导致间歇性400。

  • 解决办法:
    • 检查失败请求的XML内容,确认DTD的systemId是否正确,无无效参数
    • 对比成功和失败请求的XML差异,定位是否是DTD引用格式问题

4. 第三方服务间歇性故障

第三方服务本身可能存在不稳定情况,导致间歇性返回400。

  • 解决办法:
    • 监控第三方服务的状态,确认是否有服务波动
    • 实现降级策略:当DTD拉取失败时,允许跳过验证(需评估业务风险),或使用备用DTD文件

内容的提问来源于stack exchange,提问作者Josh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:53:33