2010-04-01 23 views
10

Tôi đang cố gắng thực hiện truy vấn xpath trên tài liệu xhtml. Sử dụng .NET 3.5.Đã xảy ra lỗi khi mở DTD bên ngoài (w3.org, xhtml1-transitional.dtd). 503 Máy chủ không có sẵn

Tài liệu này trông như thế này:

<!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> 

Bởi vì tài liệu bao gồm các đơn vị char khác nhau (&nbsp; và vân vân), tôi cần phải sử dụng DTD, để load nó với một XmlReader. Vì vậy, mã của tôi trông như thế này:

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

Nhưng khi tôi chạy này, nó sẽ trả

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.

Bây giờ, tôi biết tại sao Tôi nhận được lỗi 503. W3C explained it very clearly.

Tôi đã thấy "cách giải quyết" trong đó mọi người chỉ vô hiệu hóa DTD. Đây là những gì ProhibitDtd=true có thể làm, và nó giúp loại bỏ lỗi 503.

Nhưng trong trường hợp của tôi dẫn đến các vấn đề khác - ứng dụng không nhận được defintions thực thể và do đó không phải là XML được định dạng tốt. Làm cách nào tôi có thể xác thực với DTD và nhận định nghĩa thực thể mà không cần truy cập trang web w3.org?


Tôi nghĩ .NET 4.0 có khả năng tích hợp tiện lợi để xử lý tình huống này: XmlPreloadedResolver. Nhưng tôi cần một giải pháp cho .NET 3.5.


liên quan:
- java.io.IOException: Server returned HTTP response code: 503

Trả lời

7

Câu trả lời là, tôi phải cung cấp riêng tôi XmlResolver. Tôi không nghĩ rằng đây là built-in để NET 3.5. Điều đó thật khó hiểu. Nó cũng gây bối rối rằng nó đã đưa tôi này dài để vấp ngã vào vấn đề này. Nó cũng bối rối mà tôi không thể tìm thấy một người nào khác đã giải quyết vấn đề này chưa?

Ok, vậy .. XmlResolver. Tôi tạo ra một lớp mới, bắt nguồn từ XmlResolver và over-rode ba điều quan trọng: Credentials (set), ResolveUri và 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) 
    { 
     ... 
    } 
} 

Tài liệu về công cụ này khá đẹp, vì vậy tôi sẽ cho bạn biết những gì tôi đã học. Các hoạt động của lớp này là như vậy: XmlReader sẽ gọi ResolveUri đầu tiên, sau đó, đưa ra một giải quyết Uri, sau đó sẽ gọi GetEntity. Phương thức đó được mong đợi sẽ trả về một đối tượng Type t (được thông qua như một tham số). Tôi chỉ thấy nó yêu cầu một System.IO.Stream.

Ý tưởng của tôi là nhúng bản sao cục bộ của DTD và các phụ thuộc của nó cho XHTML1.0 vào lắp ráp, bằng cách sử dụng tùy chọn csc.exe /resource và sau đó truy xuất luồng để khôi phục.

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

Khá đơn giản. Điều này được gọi từ GetEntity().

Nhưng tôi có thể cải thiện điều đó. Thay vì nhúng các DTD vào bản rõ, tôi đã gzip chúng trước.Sau đó sửa đổi phương thức trên như sau:

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); 
} 

Mã đó mở luồng cho tài nguyên được nhúng và trả về GZipStream được định cấu hình để giải nén. Người đọc nhận được DTD bản rõ.

Điều tôi muốn làm là chỉ giải quyết các URI cho DTD từ Xhtml 1.0. Vì vậy, tôi đã viết ResolveUri và GetEntity để tìm những DTD cụ thể, và chỉ đáp ứng một cách chắc chắn cho họ.

Đối với tài liệu XHTML với câu lệnh DTD, luồng giống như thế này;

  1. XmlReader gọi ResolveUri với URI công khai cho XHTML DTD, là "-//W3C//DTD XHTML 1.0 Transitional//EN". Nếu XmlResolver có thể giải quyết, nó sẽ trả về ... một URI hợp lệ. Nếu nó không thể giải quyết, nó sẽ ném. Việc triển khai của tôi chỉ ném cho URI công khai.

  2. XmlReader sau đó gọi ResolveUri bằng Mã định danh hệ thống cho DTD, trong trường hợp này là "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd". Trong trường hợp này, XhtmlResolver trả về một Uri hợp lệ.

  3. XmlReader sau đó gọi GetEntity bằng URI đó. XhtmlResolver lấy luồng tài nguyên được nhúng và trả về nó.

Điều tương tự cũng xảy ra với các phụ thuộc - xhtml_lat1.ent, v.v. Để người giải quyết hoạt động, tất cả những thứ đó cần được nhúng vào.

Và có, nếu Người giải quyết không thể giải quyết URI, dự kiến ​​là ném Ngoại lệ. Đây không phải là tài liệu chính thức theo như tôi thấy. Nó có vẻ hơi ngạc nhiên. (Vi phạm nghiêm trọng the principle of least astonishment). Nếu thay vào đó, ResolveUri trả về null, XmlReader sẽ gọi GetEntity trên URI rỗng, mà .... ah, là vô vọng.


Điều này phù hợp với tôi. Nó sẽ hoạt động cho bất kỳ ai xử lý XML trên XHTML từ .NET. Nếu bạn muốn sử dụng điều này trong các ứng dụng của riêng bạn, grab the DLL. Mã zip bao gồm mã nguồn đầy đủ. Được cấp phép theo số MS Public License.

Bạn có thể cắm nó vào các ứng dụng XML của bạn mà fiddle với XHTML. Sử dụng nó như thế này:

// 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()) 
    { 
    ... 
    } 
+0

+1 Cảm ơn bạn đã đăng bài đó. – ChrisW

3

Bạn có thể không cho phép một XmlReader để mở bất kỳ nguồn lực bên ngoài bằng cách thiết lập thuộc tính null XmlReaderSettings.XmlResolver.

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

Phải, nhưng sau đó, tôi không thể thực hiện xác thực mà tôi muốn thực hiện và không có định nghĩa đối tượng, tôi không thể thực hiện truy vấn xpath trên xhtml. – Cheeso

2

Khi phương pháp ResolveUri của bạn nhận được một yêu cầu cho một hình thức "công cộng" của URI như -//W3C//ELEMENTS XHTML Images 1.0//EN sau đó không phương pháp ném và bạn chờ sau URI web giống như bắt đầu bằng http://?

Thay vì ném, tôi giải quyết URI công khai với http:// URI tương ứng (và sau đó theo phương pháp GetEntity của tôi tôi chặn yêu cầu đến các URI http://).

Do đó, tôi không bao giờ phải ném, mà tôi nghĩ là giải pháp đúng.


That's a smart way to do it. How big is your dictionary? The library I pointed you to handles only XHTML 1.0, and there is just one public URI base that would need to be mapped.

Tôi đang sử dụng XHTML 1.1 đó là 'mô-đun' vì vậy tôi có để ánh xạ khoảng 40 tập tin.

Hãy coi chừng hành vi của Khung có thể đã thay đổi! Tôi có một thư viện (bao gồm cả lớp XhtmlUrlResolver của tôi) được xây dựng bằng .NET Framework 2, nhưng nó được gọi khác nhau tùy thuộc vào ứng dụng (sử dụng thư viện) được xây dựng cho .NET 2 hay .NET 4.

Với .NET 2, khi phương thức ResolveUri của tôi luôn chỉ được ủy nhiệm một cách minh bạch cho XmlUrlResolver, thì nó sẽ:

  1. Yêu cầu giải quyết công khai của DTD.
  2. Cố gắng GetEntity DTD từ đĩa (ném một DirectoryNotFoundException)
  3. Cố gắng GetEntity DTD từ http (mà tôi muốn phục vụ từ nguồn lực địa phương)
  4. Cố gắng GetEntity mỗi tập tin khác từ http (mà tôi d phục vụ từ nguồn lực địa phương)

với .NET 4 đã có một cuộc gọi bổ sung cho tất cả các tài nguyên:

  • Yêu cầu ResolveUri công chúng của tiểu tài nguyên (ví dụ file *.mod), whi ch thực hiện của tôi chỉ giao cho XmlUrlResolver
  • Yêu cầu GetEntity các 'giải quyết' công cộng của tiểu tài nguyên, mà đã không thực sự giải quyết nào cả, nó chỉ có một tiền tố http giống như gia tăng (ném WebException)

Ném tất cả những WebExceptions làm chậm quá trình xử lý rất nhiều, đó là lý do tại sao tôi xem lại để tìm một sửa chữa.

Đề xuất của bạn, rằng tôi ném từ ResolveUri, giải quyết vấn đề đó, mà tôi cảm ơn bạn; nhưng thay vì ném, trả lại thứ gì đó từ ResolveUri thì thanh lịch hơn (và nhanh hơn một chút: 40 ngoại lệ).

Đây là mã nguồn hiện tại của tôi.

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" } 
      }; 
     } 
    } 
} 
+0

có, XhtmlResolver tôi đã viết ném khi được trình bày với một URI không phải http. – Cheeso

+0

@Cheeso Có. Tôi đã làm việc thay vì ném, 'ResolveUri' có thể trả về Uri giống như http. Đó thậm chí có thể là những gì nó cho, và tốt hơn so với ném. Vì vậy, cũng như có một bộ nhớ cache địa phương của các tập tin, tôi giữ một từ điển của dịch Uri công-to-http. – ChrisW

+0

Đó là một cách thông minh để làm điều đó. Từ điển của bạn lớn đến mức nào? Thư viện tôi đã chỉ cho bạn xử lý chỉ XHTML 1.0 và chỉ có một cơ sở URI công khai cần được ánh xạ. – Cheeso

Các vấn đề liên quan