.net - 打开外部 DTD(w3.org、xhtml1-transitional.dtd)时发生错误。 503 服务器不可用

标签 .net xml xhtml w3c dtd

我正在尝试对 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>

因为文档包含各种字符实体(  等),我需要使用 DTD,以便使用 XmlReader 加载它.所以我的代码看起来像这样:

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

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

An error has occurred while opening external DTD 'http://www.w3.org/TR/xhtml1-transitional.dtd': The remote server returned an error: (503) Server Unavailable.

现在,我知道为什么我会收到 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

最佳答案

答案是,我必须提供我自己的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 嵌入纯文本,而是先将它们压缩。然后像这样修改上面的方法:

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 的公共(public) URI 调用 ResolveUri,即 "-//W3C//DTD XHTML 1.0 Transitional//EN"。如果 XmlResolver 可以解析,它应该返回...一个有效的 URI。如果它不能解决,它应该抛出。我的实现只是抛出公共(public) 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,等等。为了让解析器工作,所有这些东西都需要嵌入。

是的,如果 Resolver 无法解析 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())
    {
     ...
    }

关于.net - 打开外部 DTD(w3.org、xhtml1-transitional.dtd)时发生错误。 503 服务器不可用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2558021/

相关文章:

c# - 哪个类 "owns"是非托管资源(并且实现了 IDisposable)?

xml - 如何为自定义对象检测创建自己的 Haar 级联分类器?

php - 我应该使用 JSON 或 XML 或其他东西来每隔 X 时间获取页面内容吗?

javascript - 外部 JS 与内部 JS - MBean 值

css - 如何使用 Twitter Bootstrap 将菜单项行为更改为 "on hover change background"?

c# - Vista下如何为所有用户保存设置

c# - 保护用户的云服务 API key 安全

c# - 如何在 MVC 3 中使用 LabelFor

c# - 如何将动态对象序列化为xml C#

Javascript 幻灯片不滑动