Произошла ошибка при открытии внешнего DTD (w3.org, xhtml1-transitional.dtd). 503 Сервер недоступен

Я пытаюсь выполнить запросы xpath по документу xhtml. Использование .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>

Поскольку документ содержит различные объекты char (&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 объяснил это очень четко.

Я видел "обходные пути", когда люди просто отключили DTD. Это то, что может сделать ProhibitDtd=true, и устраняет ошибку 503.

Но в моем случае это приводит к другим проблемам - приложение не получает определения объекта и, следовательно, не является корректным XML. Как я могу проверить с DTD и получить определения сущности, не попав на сайт w3.org?


Я думаю, что у .NET 4.0 есть отличная встроенная возможность справиться с этой ситуацией: XmlPreloadedResolver. Но мне нужно решение для .NET 3.5.


связаны:
- java.io.IOException: сервер вернул код ответа HTTP: 503

Ответ 1

Ответ: я должен предоставить свой XmlResolver. Я не думаю, что это встроено в .NET 3.5. Это озадачивает. Это также озадачивало то, что мне пришлось так долго споткнуться о этой проблеме. Это также озадачивало то, что я не мог найти кого-то еще, кто решил эту проблему уже?

Хорошо, так.. XmlResolver. Я создал новый класс, полученный из XmlResolver, и выполнил три ключевых действия: учетные данные (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.

Моя идея - вставить локальные копии DTD и его зависимостей для XHTML1.0 в сборку, используя опцию csc.exe /resource, а затем получить поток для этого ресурса.

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 открытого текста.

Я хотел бы разрешить только URI для DTD из Xhtml 1.0. Поэтому я написал ResolveUri и GetEntity, чтобы искать эти DTD и отвечать утвердительно только на них.

Для документа XHTML с оператором DTD поток подобен этому;

  • XmlReader вызывает ResolveUri с открытым URI для DTD XHTML, который является "-//W3C//DTD XHTML 1.0 Transitional//EN". Если XmlResolver может разрешить, он должен вернуть... действительный URI. Если он не может решить, он должен бросить. Моя реализация просто бросает публичный URI.

  • Затем XmlReader вызывает ResolveUri с системным идентификатором для DTD, который в этом случае равен "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd". В этом случае XhtmlResolver возвращает действительный Uri.

  • Затем XmlReader вызывает GetEntity с этим URI. XhtmlResolver захватывает встроенный поток ресурсов и возвращает его.

То же самое происходит для зависимостей - xhtml_lat1.ent и т.д. Чтобы распознаватель работал, все эти вещи необходимо внедрить.

И да, если Resolver не может разрешить URI, ожидается, что он исключит исключение. Насколько мне известно, это официально не документировано. Кажется, это немного удивительно. (Вопиющее нарушение принцип наименьшего удивления). Если вместо этого ResolveUri возвращает значение null, XmlReader вызовет GetEntity на нулевом URI, который... ах, безнадежен.


Это работает для меня. Он должен работать для всех, кто обрабатывает XML на XHTML с .NET. Если вы хотите использовать это в своих приложениях, захватить DLL. Почтовый индекс включает полный исходный код. Лицензируется в соответствии с 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

Вы можете запретить XmlReader открывать любые внешние ресурсы, установив для свойства XmlReaderSettings.XmlResolver значение null.

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

Ответ 3

Когда ваш метод ResolveUri получает запрос на "общедоступную" форму URI, например -//W3C//ELEMENTS XHTML Images 1.0//EN, тогда ваш метод бросает и ждет последующий веб-URI, начинающийся с http://?

Вместо того, чтобы бросать, я разрешаю публичный URI соответствующему URI http:// (а затем в моем методе GetEntity я перехватываю запросы в URI http://).

Поэтому мне никогда не нужно бросать, что я считаю правильным решением.


Это умный способ сделать это. Насколько велик ваш словарь? В библиотеке я указал, что вы обрабатываете только XHTML 1.0, и есть только одна общедоступная база URI, которая должна отображаться.

Я использую XHTML 1.1, который является "модульным", поэтому мне нужно отобразить около 40 файлов.

Помните, что поведение Framework могло быть изменено! У меня есть библиотека (включая мой класс XhtmlUrlResolver), которая построена с помощью .NET Framework 2, но она вызывается по-разному в зависимости от того, выполняется ли приложение (которое использует библиотеку) для .NET 2 или .NET 4.

С .NET 2, когда мой метод ResolveUri всегда делегируется только прозрачно в XmlUrlResolver, он:

  • Попросите ResolveUri публиковать DTD.
  • Попробуйте GetEntity DTD с диска (выбрасывает одно исключение DirectoryNotFoundException)
  • Попробуйте GetEntity DTD с http (который я буду обслуживать из локальных ресурсов)
  • Попробуйте GetEntity каждый другой файл с 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" }
            };
        }
    }
}