c# - 如何在不提供凭据的情况下将安全的 rss 提要读入 SyndicationFeed?

标签 c# rss .net-4.0 syndication-feed syndicationfeed

无论出于何种原因,IBM 对其 RSS 提要使用 https(无需凭据)。我正在尝试消费 https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en使用 .NET 4 SyndicationFeed。我可以在浏览器中打开这个提要,它加载得很好。这是代码:

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

异常(exception)情况:

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 提要?

最佳答案

我认为这与安全无关。 500 错误是服务器端错误。 XmlReader.Create(url) 生成的请求中的某些内容混淆了 ibm 网站。如果这只是一个安全问题,正如您在问题中所建议的那样,那么您会收到 403 错误或“授权被拒绝”。但是你得到了 500,这是一个应用程序错误。

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

我查看了传出的 HTTP 请求 header ,使用 Fiddler .对于 IE 生成的请求, header 如下所示:

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) 的请求, header 如下所示:

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 header ,在 500 响应中,在对 IE 的响应中不存在。

基于此,我推测是请求 header (尤其是 cookie)的差异让 ibm.com 感到困惑。


我不知道如何说服 XmlReader.Create() 嵌入我想要的所有请求 header ,包括 cookie。但我知道如何使用 HttpWebRequest 来做到这一点。所以我用了那个。

我必须清除一些障碍。

  1. 我需要 ibm.com 的持久性 cookie。为此,我不得不求助于 Win32 的 p/invoke InternetGetCookie .请参阅 WebRequest 文档页面底部用户提供的内容中附加的 PersistentCookies 类。 ,如何做到这一点。附加 cookie 后,我不再收到 500 错误。万岁!

  2. 但是 XmlReader.Create() 无法读取生成的流。对我来说它看起来是二进制的。我意识到我需要解压缩 gzip 或压缩的内容。为此,我必须在接收到的响应流周围包装一个 GZipStream 或 DeflateStream,并使用 XmlReader 的解压缩流。 设置 AutomaticDecompression HttpWebRequest 上的属性。我本可以通过不在 Accept-Encoding 上包含“gzip, deflate”来避免这种需要出站请求中的 header 。实际上,在设置 AutomaticDecompression 属性后,这些 header 会在出站 HTTP 请求中隐式设置。

  3. 当我这样做时,我得到了实际的文本。但是一些字节码被关闭了。接下来,我需要在 TextReader 中使用正确的文本编码,如 HttpWebResponse 中所示。

  4. 这样做之后,我得到了一个合理的字符串,但是生成的解压缩 rss 流导致 XmlReader 阻塞,

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

    我看了看,发现了一个小的 <script> block ,在那个位置,在 <copyright> 内rss 文档中的元素。似乎 IBM 正试图通过附加将在浏览器中运行的逻辑来格式化日期来让浏览器“本地化”版权日期。对我来说似乎有点矫枉过正,甚至是 IBM 的错误。但是因为元素的文本节点中的尖括号困扰了 XmlReader,所以我用 Regex 替换删除了脚本 block 。


清除这些障碍后,它奏效了。 .NET 应用程序能够从该 https url 读取 RSS 流。

我没有做任何进一步的测试 - 看看是否改变 Accept header 或 Accept-Encoding header 会改变行为。如果你关心的话,那是你要弄清楚的。

结果代码如下。它比你简单的 3 线更难看。我不知道如何让它更简单。

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

关于c# - 如何在不提供凭据的情况下将安全的 rss 提要读入 SyndicationFeed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2813989/

相关文章:

c# - eBay FileTransferService - 请指定具有有效格式的文件

c# - 避免在 Action 中渲染 View 的正确方法

javascript - 替换/忽略字符串中的 JavaScript

c# - 为什么这个 LINQ 查询没有返回正确的日期?

c# - 从混合了 XML 和纯文本的流中读取

c# - 如何将内部数字日期转换为 Access 日期/时间格式?

linux - 捕获数据包后会发生什么?

ios - 意外发现 nil URL SWIFT

c# - GetType() 在父类(super class)构造函数中返回子类 Type

c# - 优化 Dictionary.TryGetValue()