【问题标题】:An error has occurred opening extern DTD (w3.org, xhtml1-transitional.dtd). 503 Server Unavailable打开外部 DTD(w3.org、xhtml1-transitional.dtd)时发生错误。 503 服务器不可用
【发布时间】:2011-02-03 05:18:35
【问题描述】:

我正在尝试对 xhtml 文档进行 xpath 查询。使用 .NET 3.5。

文档如下所示:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
   ....
  </head>
  <body>
    ...
  </body>
</html>

由于文档包含各种字符实体(&amp;nbsp; 等),我需要使用 DTD,以便使用 XmlReader 加载它。所以我的代码如下所示:

var s = File.OpenRead(fileToRead)
var reader = XmlReader.Create(s, new XmlReaderSettings{ ProhibitDtd=false });

但是当我运行它时,它会返回

打开外部 DTD 'http://www.w3.org/TR/xhtml1-transitional.dtd' 时出错:远程服务器返回错误:(503) 服务器不可用。

现在,我知道为什么我收到 503 错误。 W3C explained it very clearly

我见过人们只是禁用 DTD 的“变通方法”。这是ProhibitDtd=true 可以做的,它消除了503 错误。

但在我的情况下,这会导致其他问题 - 应用程序没有获得实体定义,因此不是格式良好的 XML。如何在不访问 w3.org 网站的情况下使用 DTD 进行验证并获取实体定义?


我认为 .NET 4.0 有一个很好的内置功能来处理这种情况:XmlPreloadedResolver。但我需要 .NET 3.5 的解决方案。


相关:
- java.io.IOException: Server returned HTTP response code: 503

【问题讨论】:

    标签: .net xml xhtml w3c dtd


    【解决方案1】:

    答案是,我必须提供我自己的XmlResolver。我不认为这是 .NET 3.5 内置的。真是莫名其妙。同样令人困惑的是,我花了这么长时间才偶然发现这个问题。还莫名其妙地找不到其他人已经解决了这个问题?

    好的,所以.. XmlResolver。我创建了一个从 XmlResolver 派生的新类,并覆盖了三个关键内容:Credentials (set)、ResolveUri 和 GetEntity。

    public sealed class XhtmlResolver : XmlResolver
    {
        public override System.Net.ICredentials Credentials
        {
            set { throw new NotSupportedException();}
        }
    
        public override object GetEntity(Uri absoluteUri, string role, Type t)
        {
           ...
        }
    
        public override Uri ResolveUri(Uri baseUri, string relativeUri)
        {
          ...
        }
    }
    

    关于这些东西的文档非常少,所以我会告诉你我学到了什么。这个类的操作是这样的:XmlReader 会先调用 ResolveUri,然后,给定一个解析的 Uri,再调用 GetEntity。该方法应返回类型 t 的对象(作为参数传递)。我只看到它请求 System.IO.Stream。

    我的想法是使用 csc.exe /resource 选项将 DTD 的本地副本及其对 XHTML1.0 的依赖项嵌入到程序集中,然后检索该资源的流。

    private System.IO.Stream GetStreamForNamedResource(string resourceName)
    {
        Assembly a = Assembly.GetExecutingAssembly();
        return  a.GetManifestResourceStream(resourceName);
    }
    

    很简单。这从 GetEntity() 调用。

    但我可以改进。我没有将 DTD 嵌入纯文本中,而是先对它们进行 gzip 压缩。然后像这样修改上面的方法:

    private System.IO.Stream GetStreamForNamedResource(string resourceName)
    {
        Assembly a = Assembly.GetExecutingAssembly();
        return  new System.IO.Compression.GZipStream(a.GetManifestResourceStream(resourceName), System.IO.Compression.CompressionMode.Decompress);
    }
    

    该代码打开嵌入资源的流,并返回配置为解压缩的 GZipStream。阅读器得到明文DTD。

    我想要做的是只解析来自 Xhtml 1.0 的 DTD 的 URI。所以我编写了 ResolveUri 和 GetEntity 来寻找那些特定的 DTD,并且只为它们做出肯定的响应。

    对于带有 DTD 语句的 XHTML 文档,流程是这样的;

    1. XmlReader 使用 XHTML DTD 的公共 URI("-//W3C//DTD XHTML 1.0 Transitional//EN")调用 ResolveUri。如果 XmlResolver 可以解析,它应该返回...一个有效的 URI。如果它不能解决,它应该抛出。我的实现只是抛出公共 URI。

    2. XmlReader 然后使用 DTD 的系统标识符调用 ResolveUri,在本例中为 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"。在这种情况下,XhtmlResolver 返回一个有效的 Uri。

    3. XmlReader 然后使用该 URI 调用 GetEntity。 XhtmlResolver 抓取嵌入的资源流并将其返回。

    同样的事情发生在依赖项上——xhtml_lat1.ent 等等。为了让解析器工作,所有这些东西都需要嵌入。

    是的,如果解析器无法解析 URI,它应该抛出异常。据我所知,这没有正式记录。这似乎有点令人惊讶。 (严重违反the principle of least astonishment)。相反,如果 ResolveUri 返回 null,XmlReader 将在 null URI 上调用 GetEntity,这....啊,是没有希望的。


    这对我有用。它应该适用于从 .NET 对 XHTML 进行 XML 处理的任何人。如果您想在自己的应用程序中使用它,grab the DLL。 zip 包含完整的源代码。在MS Public License 下获得许可。

    您可以将它插入到您的 XML 应用程序中,这些应用程序使用 XHTML。像这样使用它:

    // for an XmlDocument...
    System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
    doc.XmlResolver = new Ionic.Xml.XhtmlResolver();
    doc.Load(xhtmlFile);
    
    // for an XmlReader...
    var xmlReaderSettings = new XmlReaderSettings
        {
            ProhibitDtd = false,
            XmlResolver = new XhtmlResolver()
        };
    using (var stream = File.OpenRead(fileToRead))
    {
        XmlReader reader = XmlReader.Create(stream, xmlReaderSettings);
        while (reader.Read())
        {
         ...
        }
    

    【讨论】:

      【解决方案2】:

      您可以通过将 XmlReaderSettings.XmlResolver 属性设置为 null 来禁止 XmlReader 打开任何外部资源。

      System.Xml.XmlReaderSettings xmlReaderSettings = new System.Xml.XmlReaderSettings ();
      xmlReaderSettings.XmlResolver = null;
      System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(myUrl, xmlReaderSettings);
      

      【讨论】:

      • 对,但是,我不能做我想做的验证,而且没有实体定义,我不能在 xhtml 上做 xpath 查询。
      【解决方案3】:

      当您的 ResolveUri 方法收到对“公共”形式的 URI(如 -//W3C//ELEMENTS XHTML Images 1.0//EN)的请求时,您的方法是否会抛出并等待随后以 http:// 开头的类似 Web 的 URI?

      我没有抛出,而是将公共 URI 解析为相应的 http:// URI(然后在我的 GetEntity 方法中,我拦截了对 http:// URI 的请求)。

      因此我永远不必扔,我认为这是正确的解决方案。


      这是一个聪明的方法。你的字典有多大?我向您指出的库仅处理 XHTML 1.0,并且只有一个需要映射的公共 URI 库。

      我使用的是“模块化”的 XHTML 1.1,所以我必须映射大约 40 个文件。

      请注意,框架的行为可能已经改变!我有一个使用 .NET Framework 2 构建的库(包括我的 XhtmlUrlResolver 类),但根据应用程序(使用该库)是为 .NET 2 还是 .NET 4 构建的,它的调用方式不同。

      在 .NET 2 中,当我的 ResolveUri 方法始终只透明地委托给 XmlUrlResolver 时,它会:

      1. 向 DTD 的公众请求 ResolveUri。
      2. 尝试从磁盘获取 DTD(抛出一个 DirectoryNotFoundException)
      3. 尝试从 http 获取 DTD(我将从本地资源中获取)
      4. 尝试从 http 获取所有其他文件(我将从本地资源中获取)

      在 .NET 4 中,每个资源都有一个额外的调用:

      • 请求 ResolveUri 公开子资源(例如 *.mod 文件),我的实现刚刚委派给 XmlUrlResolver
      • 向 GetEntity 询问子资源的“已解决”公共,这根本没有真正解决,它只是添加了一个类似 http 的前缀(抛出 WebException)

      抛出所有这些 WebExceptions 会大大减慢处理速度,这就是我重新审视它以寻找修复的原因。

      我从 ResolveUri 提出的您的建议解决了这个问题,对此我表示感谢;但不是抛出,而是从 ResolveUri 返回一些东西更优雅(而且更快一点:少了 40 个异常)。

      这是我当前的源代码。

      using System;
      using System.Collections.Generic;
      using System.Text;
      
      using System.Reflection;
      using System.IO;
      using System.Xml;
      
      //don't obfuscate the file names of the embedded resources,
      //which are contained in a "Files" subfolder of the project
      [assembly: Obfuscation(Feature = "Apply to ModelText.ModelXml.Files.*: all", Exclude = true, ApplyToMembers = true)]
      
      namespace ModelText.ModelXml
      {
          /// <summary>
          /// This class provides local (i.e. faster) access to the XHTML DTD.
          /// </summary>
          /// <remarks>
          /// Another way to implement this class is described in MSDN "Customizing the XmlUrlResolver Class"
          /// which shows as an example a "class XmlCachingResolver"
          /// and which is implemented using WebRequest and HttpRequestCachePolicy
          /// </remarks>
          [System.Reflection.ObfuscationAttribute(Feature = "renaming", ApplyToMembers = true)]
          public class XhtmlUrlResolver : XmlResolver
          {
              XmlUrlResolver m_xmlUrlResolver = new XmlUrlResolver();
              Assembly m_assembly = Assembly.GetExecutingAssembly();
              public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
              {
                  string uriString = absoluteUri.ToString();
                  if (s_resources.uriExists(uriString))
                  {
                      //Console.WriteLine("XhtmlUrlResolver Found {0} -- {1}", uriString, DateTime.Now);
      
                      //to get the filename of the embedded resource, remove the http: directory
                      //this is OK because the filenames are unique and map 1-to-1 with resource names
                      string filename = uriString.Substring(uriString.LastIndexOf('/') + 1);
                      Stream stream = m_assembly.GetManifestResourceStream(typeof(XhtmlUrlResolver), "Files." + filename);
                      return stream;
                  }
      
                  //Console.WriteLine("XhtmlUrlResolver Throwing {0} -- {1}", uriString, DateTime.Now);
                  throw new ArgumentException();
                  //Console.WriteLine("XhtmlUrlResolver Getting {0} -- {1}", uriString, DateTime.Now);
                  //object o = m_xmlUrlResolver.GetEntity(absoluteUri, role, ofObjectToReturn);
                  //Console.WriteLine("XhtmlUrlResolver Got {0} -- {1}", uriString, DateTime.Now);
                  //return o;
              }
      
              public override Uri ResolveUri(Uri baseUri, string relativeUri)
              {
                  string resolved = s_resources.resolve(relativeUri);
                  if (resolved != null)
                  {
                      //Console.WriteLine("ResolveUri resolving {0}, {1} -- {2}", baseUri, relativeUri, DateTime.Now);
                      return new Uri(resolved);
                  }
      
                  //Console.WriteLine("ResolveUri passing {0}, {1} -- {2}", baseUri, relativeUri, DateTime.Now);
                  return m_xmlUrlResolver.ResolveUri(baseUri, relativeUri);
              }
              public override System.Net.ICredentials Credentials
              {
                  set { m_xmlUrlResolver.Credentials = value; }
              }
      
              static Resources s_resources = new Resources();
      
              class Resources
              {
                  Dictionary<string, string> m_publicToUri = new Dictionary<string, string>();
      
                  internal Resources()
                  {
                      for (int i = 0, n = array.GetLength(0); i < n; ++i)
                      {
                          m_publicToUri.Add(array[i, 1], array[i, 0]);
                      }
                  }
      
                  internal bool uriExists(string absoluteUri)
                  {
                      return m_publicToUri.ContainsValue(absoluteUri);
                  }
      
                  internal string resolve(string relativeUri)
                  {
                      string resolved;
                      if (m_publicToUri.TryGetValue(relativeUri, out resolved))
                      {
                          return resolved;
                      }
                      return null;
                  }
      
                  static string[,] array = {
                     { "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", "-//W3C//DTD XHTML 1.1//EN" },
      
                     { "http://www.w3.org/MarkUp/DTD/xhtml11-model-1.mod", "-//W3C//ENTITIES XHTML 1.1 Document Model 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-attribs-1.mod", "-//W3C//ENTITIES XHTML Common Attributes 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-base-1.mod", "-//W3C//ELEMENTS XHTML Base Element 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-bdo-1.mod", "-//W3C//ELEMENTS XHTML BIDI Override Element 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-blkphras-1.mod", "-//W3C//ELEMENTS XHTML Block Phrasal 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-blkpres-1.mod", "-//W3C//ELEMENTS XHTML Block Presentation 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-blkstruct-1.mod", "-//W3C//ELEMENTS XHTML Block Structural 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-charent-1.mod", "-//W3C//ENTITIES XHTML Character Entities 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-csismap-1.mod", "-//W3C//ELEMENTS XHTML Client-side Image Maps 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-datatypes-1.mod", "-//W3C//ENTITIES XHTML Datatypes 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-edit-1.mod", "-//W3C//ELEMENTS XHTML Editing Elements 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-events-1.mod", "-//W3C//ENTITIES XHTML Intrinsic Events 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-form-1.mod", "-//W3C//ELEMENTS XHTML Forms 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-framework-1.mod", "-//W3C//ENTITIES XHTML Modular Framework 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-hypertext-1.mod", "-//W3C//ELEMENTS XHTML Hypertext 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-image-1.mod", "-//W3C//ELEMENTS XHTML Images 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-inlphras-1.mod", "-//W3C//ELEMENTS XHTML Inline Phrasal 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-inlpres-1.mod", "-//W3C//ELEMENTS XHTML Inline Presentation 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-inlstruct-1.mod", "-//W3C//ELEMENTS XHTML Inline Structural 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-inlstyle-1.mod", "-//W3C//ELEMENTS XHTML Inline Style 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-lat1.ent", "-//W3C//ENTITIES Latin 1 for XHTML//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-link-1.mod", "-//W3C//ELEMENTS XHTML Link Element 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-list-1.mod", "-//W3C//ELEMENTS XHTML Lists 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-meta-1.mod", "-//W3C//ELEMENTS XHTML Metainformation 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-object-1.mod", "-//W3C//ELEMENTS XHTML Embedded Object 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-param-1.mod", "-//W3C//ELEMENTS XHTML Param Element 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-pres-1.mod", "-//W3C//ELEMENTS XHTML Presentation 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-qname-1.mod", "-//W3C//ENTITIES XHTML Qualified Names 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-script-1.mod", "-//W3C//ELEMENTS XHTML Scripting 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-special.ent", "-//W3C//ENTITIES Special for XHTML//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-ssismap-1.mod", "-//W3C//ELEMENTS XHTML Server-side Image Maps 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-struct-1.mod", "-//W3C//ELEMENTS XHTML Document Structure 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-style-1.mod", "-//W3C//ELEMENTS XHTML Style Sheets 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-symbol.ent", "-//W3C//ENTITIES Symbols for XHTML//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-table-1.mod", "-//W3C//ELEMENTS XHTML Tables 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-target-1.mod", "-//W3C//ELEMENTS XHTML Target 1.0//EN" },
                     { "http://www.w3.org/MarkUp/DTD/xhtml-text-1.mod", "-//W3C//ELEMENTS XHTML Text 1.0//EN" },
      
                     { "http://www.w3.org/TR/ruby/xhtml-ruby-1.mod", "-//W3C//ELEMENTS XHTML Ruby 1.0//EN" }
                  };
              }
          }
      }
      

      【讨论】:

      • 是的,我编写的 XhtmlResolver 在呈现非 http URI 时会抛出异常。
      • @Cheeso 是的。我发现ResolveUri 可以返回类似 http 的 Uri,而不是抛出。这甚至可能是它的用途,而且比投掷更好。因此,除了拥有文件的本地缓存外,我还保留了一本 public-to-http Uri 翻译字典。
      • 这是一个聪明的方法。你的字典有多大?我向您指出的库仅处理 XHTML 1.0,并且只有一个需要映射的公共 URI 库。
      • @Cheeso 大约 40 个字典条目。我编辑了我的答案(上图)以更全面地回答您的评论,并警告 .NET 4 改变了它使用此类类的方式。
      猜你喜欢
      • 1970-01-01
      • 2011-08-04
      • 2017-01-14
      • 2012-04-23
      • 2012-12-04
      • 2015-08-30
      • 2012-04-18
      • 2013-10-31
      • 2015-07-07
      相关资源
      最近更新 更多