无论出于何种原因,IBM都使用https(不需要凭据)来提供RSS源。我正在尝试使用.NET 4 SyndicationFeed消耗https://www.ibm.com/developerworks/mydeveloperworks/blogs/roller-ui/rendering/feed/gradybooch/entries/rss?lang=en。我可以在浏览器中打开这个Feed,它加载得很好。这是代码:
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源?
答案 0 :(得分:9)
我认为这与安全无关。 500错误是服务器端错误。 XmlReader.Create(url)生成的请求中的某些内容让ibm网站感到困惑。如果它只是一个安全问题,正如您的问题中所建议的那样,那么您可能会收到403错误或“授权被拒绝”。但你有500,这是一个应用程序错误。
即使如此,也许客户端应用程序可以执行某些操作,以避免混淆服务器。
我使用Fiddler查看了传出的HTTP请求标头。对于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
相当不同。此外,在对后者的响应中,我在500响应中得到了一个Set-Cookie
标头,这在IE的响应中没有出现。
基于此,我认为它是请求标头的差异,特别是cookie,这让ibm.com感到困惑。
我不知道如何说服XmlReader.Create()嵌入我想要的所有请求标头,包括cookie。但我知道如何使用HttpWebRequest做到这一点。所以我用过它。
我必须清除一些障碍。
我需要ibm.com的持久性cookie。为此,我不得不求助于Win32 InternetGetCookie的p / invoke。有关如何执行此操作,请参阅WebRequest文档页面底部的用户提供内容中附带的PersistentCookies类。附加cookie后,我不再收到500个错误。万岁!
但是XmlReader.Create()无法读取生成的流。它对我来说看起来很二元我意识到我需要解压缩gzip或缩小的内容。为此,我必须围绕收到的响应流包装GZipStream或DeflateStream,并使用解压缩流进行XmlReader。在HttpWebRequest上设置AutomaticDecompression属性。我可以通过在出站请求中的Accept-Encoding
标头上不包括“gzip,deflate”来避免这种需要。实际上,在设置AutomaticDecompression属性后,这些标头会在出站HTTP请求中隐式设置。
当我这样做时,我得到了实际的文字。但有些字节代码已关闭。接下来,我需要在TextReader中使用正确的文本编码,如HttpWebResponse中所示。
执行此操作后,我得到了一个合理的字符串,但生成的解压缩的rss流导致XmlReader被阻塞,
ReadElementString method can only be called on elements with simple or empty content. Line 11, position 25.
我在rss文档的<script>
元素中查找并找到了位于该位置的小<copyright>
块。似乎IBM试图通过附加将在浏览器中运行的逻辑来格式化日期,从而使浏览器“本地化”版权日期。对我来说似乎有点过分,甚至是IBM的错误。但是因为元素的文本节点中的尖括号支撑着XmlReader,所以我删除了带有Regex替换的脚本块。
在清除这些障碍后,它起作用了。 .NET应用程序能够从该https URL读取RSS流。
我没有进行任何进一步的测试 - 看看Accept
标头或Accept-Encoding
标头的变化是否会改变行为。如果你在意的话,那就是你要弄清楚的。
结果代码如下。它比你的简单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;
}
}
}
}
}