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

Я пытаюсь выполнить запросы 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

Ответы [ 3 ]

7 голосов
/ 01 апреля 2010

Ответ таков: я должен предоставить свой 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.

Моя идея - встроить локальные копии 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 поток выглядит следующим образом;

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

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

  3. Затем 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())
    {
     ...
    }
3 голосов
/ 02 апреля 2010

Вы можете запретить 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);
2 голосов
/ 11 ноября 2010

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

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

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


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

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

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

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

  1. Попросить разрешения для общественности DTD.
  2. Попробуйте получить DTD с диска DTD (выдает одно исключение DirectoryNotFoundException)
  3. Попробуйте GetEntity DTD с http (который я бы обслуживал с локальных ресурсов)
  4. Попробуйте GetEntity каждый второй файл с http (который я бы обслуживал с локальных ресурсов)

В .NET 4 был дополнительный вызов для каждого ресурса:

  • Запрос ResolveUri на общедоступный субресурс (например, файл *.mod), который моя реализация только что делегировала XmlUrlResolver
  • Попросить GetEntity 'разрешенную' общедоступную часть подресурса, которая на самом деле не была разрешена вообще, к ней просто добавлен http-подобный префикс (выдает исключение WebException)

Создание всех этих исключений WebException значительно замедлило обработку, поэтому я снова обратился к этому, чтобы найти исправление.

Ваше предложение, которое я выкинул из 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" }
            };
        }
    }
}
...