代码之家  ›  专栏  ›  技术社区  ›  John Kaster

如何在不提供凭据的情况下将安全RSS源读取到联合源中?

  •  3
  • John Kaster  · 技术社区  · 16 年前

    无论出于什么原因,IBM都将HTTPS(不需要凭证)用于其RSS源。我想消费 https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en 使用.NET 4联合源。我可以在浏览器中打开这个提要,它可以很好地加载。代码如下:

            using (XmlReader xml = XmlReader.Create("https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en"))
            {
                var items = from item in SyndicationFeed.Load(xml).Items
                            select item;
            }
    

    例外情况如下:

    System.Net.WebException was unhandled by user code
    Message=The remote server returned an error: (500) Internal Server Error.
    Source=System
    StackTrace:
       at System.Net.HttpWebRequest.GetResponse()
       at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
       at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials, IWebProxy proxy, RequestCachePolicy cachePolicy)
       at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
       at System.Xml.XmlReaderSettings.CreateReader(String inputUri, XmlParserContext inputContext)
       at System.Xml.XmlReader.Create(String inputUri, XmlReaderSettings settings, XmlParserContext inputContext)
       at System.Xml.XmlReader.Create(String inputUri)
       at EDN.Util.Test.FeedAggTest.LoadFeedInfoTest() in D:\cdn\trunk\CDN\Dev\Shared\net\EDN.Util\EDN.Util.Test\FeedAggTest.cs:line 126
    

    如何配置读卡器以使用HTTPS源?

    1 回复  |  直到 16 年前
        1
  •  9
  •   Cheeso    16 年前

    我认为这与安全没有任何关系。500错误是服务器端错误。xmlreader.create(url)生成的请求中的某些内容会混淆IBM网站。如果这仅仅是一个安全问题,如您的问题中所建议的,那么您将得到403错误,或者“授权被拒绝”。但你得到了500,这是一个应用程序错误。

    即便如此,也许客户端应用程序可以做些什么,以避免混淆服务器。

    我查看了传出的HTTP请求头,使用 Fiddler . 对于由IE生成的请求,标题如下:

    GET https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en HTTP/1.1
    Accept: image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/x-shockwave-flash, application/x-silverlight-2-b2, */*
    Accept-Language: en-us
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; .NET CLR 3.5.30729;)
    Accept-Encoding: gzip, deflate
    Host: www.ibm.com
    Connection: Keep-Alive
    Cookie: UnicaNIODID=Ww06gyvyPpZ-WPl6K7y; conxnsCookie=en; IBMPOLLCOOKIE=""; UnicaNIODID=QridYHCNf7M-WYM8Usr
    

    对于来自xmlReader.create(url)的请求,标题如下:

    GET https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en HTTP/1.1
    Host: www.ibm.com
    Connection: Keep-Alive
    

    很不一样。另外,在对后者的回应中,我得到了 Set-Cookie 头,在500响应中,它不在对ie的响应中。

    基于这一点,我推测是请求头(特别是cookie)中的差异导致了ibm.com的混乱。


    我不知道如何说服xmlReader.create()嵌入我想要的所有请求头,包括cookie。但我知道如何使用httpwebrequest来实现这一点。所以我用了。

    我不得不跨过几道障碍。

    1. 我需要ibm.com的持久cookie。为此,我不得不求助于对Win32的P/Invoke InternetGetCookie . 参见文档页面底部用户贡献内容中附加的PersistentCookies类 WebRequest 关于如何做到这一点。在附加cookie之后,我不再得到500个错误。万岁!

    2. 但xmlReader.create()无法读取结果流。在我看来,它是二元的。我意识到我需要去压缩gzip或泄气的内容。为此我不得不 围绕接收到的响应流包装gzipstream或deflatestream,并使用用于xmlReader的解压缩流。 设置 AutomaticDecompression HttpWebRequest上的属性。我本来可以避免这种需要,不包括“gzip,deflate”在 Accept-Encoding 出站请求中的头。实际上,在设置automaticdecompression属性之后,这些头在出站HTTP请求中被隐式设置。

    3. 当我这样做的时候,我得到了真实的文本。但有些字节码是关闭的。接下来,我需要在文本阅读器中使用正确的文本编码,如httpwebresponse中所示。

    4. 在这样做之后,我得到了一个合理的字符串,但是结果是解压缩的RSS流导致了xmlReader阻塞,

      ReadElementString method can only be called on elements with simple or empty content. Line 11, position 25.

      我找了一个小的 <script> 块,在那个位置,在 <copyright> RSS文档中的元素。IBM似乎正试图通过附加在浏览器中运行以格式化日期的逻辑,让浏览器“本地化”版权日期。对我来说,似乎是杀戮过度,甚至是IBM的一个bug。但由于元素文本节点内的尖括号干扰了xmlReader,所以我用regex替换移除了脚本块。


    在克服了这些障碍之后,它成功了。.NET应用程序能够从该https URL读取RSS流。

    我没有做任何进一步的测试-看看是否改变 Accept 报头或 接受编码 头会改变行为。如果你愿意的话,那就由你来决定。

    结果代码如下。它比你简单的三行式要丑得多。我不知道怎么让它简单一点。

    public void Run()
    {
        string url;
        url = "https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en";
    
        HttpWebRequest hwr = (HttpWebRequest) WebRequest.Create(url);
        // attach persistent cookies
        hwr.CookieContainer =
            PersistentCookies.GetCookieContainerForUrl(url);
        hwr.Accept = "text/xml, */*";
        hwr.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-us");
        hwr.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; .NET CLR 3.5.30729;)";
        hwr.KeepAlive = true;
        hwr.AutomaticDecompression = DecompressionMethods.Deflate |
                                     DecompressionMethods.GZip;
    
        using (var resp = (HttpWebResponse) hwr.GetResponse())
        {
            using(Stream s = resp.GetResponseStream())
            {            
                string cs = String.IsNullOrEmpty(resp.CharacterSet) ? "UTF-8" : resp.CharacterSet;
                Encoding e = Encoding.GetEncoding(cs);
    
                using (StreamReader sr = new StreamReader(s, e))
                {
                    var allXml = sr.ReadToEnd();
    
                    // remove any script blocks - they confuse XmlReader
                    allXml = Regex.Replace( allXml,
                                            "(.*)<script type='text/javascript'>.+?</script>(.*)",
                                            "$1$2",
                                            RegexOptions.Singleline);
    
                    using (XmlReader xmlr = XmlReader.Create(new StringReader(allXml)))
                    {
                        var items = from item in SyndicationFeed.Load(xmlr).Items
                            select item;
                    }
                }
            }
        }
    }
    
    推荐文章