3

なんらかの理由で、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;
        }

例外は次のとおりです。

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 フィードで動作するようにリーダーを構成するにはどうすればよいですか?

4

1 に答える 1

9

セキュリティとは関係ないと思います。500 エラーはサーバー側のエラーです。XmlReader.Create(url) によって生成された要求の何かが、ibm Web サイトを混乱させています。質問で示唆されているように、単にセキュリティ上の問題である場合は、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

かなりの違い。また、後者への応答ではSet-Cookie、IE への応答には存在しなかった 500 応答でヘッダーを取得しました。

これに基づいて、ibm.com を混乱させたのは、要求ヘッダー、特に Cookie の違いであると理論付けました。


XmlReader.Create() を説得して、Cookie を含め、必要なすべての要求ヘッダーを埋め込む方法がわかりません。しかし、私は HttpWebRequest でそれを行う方法を知っています。だから私はそれを使いました。

クリアしなければならないハードルがいくつかありました。

  1. ibm.com の永続 Cookie が必要でした。そのためには、 Win32 InternetGetCookieの ap/invoke に頼らなければなりませんでした。その方法については、 WebRequestのドキュメント ページの下部にあるユーザー投稿コンテンツに添付されている PersistentCookies クラスを参照してください。Cookie を添付した後、500 エラーが発生しなくなりました。万歳!

  2. しかし、結果のストリームを XmlReader.Create() で読み取ることができませんでした。私にはバイナリに見えました。gzip または圧縮されたコンテンツを解凍する必要があることに気付きました。そのためには、受信した応答ストリームを GZipStream または DeflateStream でラップし、XmlReader の解凍ストリームを使用する必要がありました。HttpWebRequestでAutomaticDecompressionプロパティを 設定します。Accept-Encoding送信リクエストのヘッダーに「gzip、deflate」を含めないことで、この必要性を回避できたはずです。実際には、AutomaticDecompression プロパティを設定した後、これらのヘッダーは送信 HTTP 要求で暗黙的に設定されます。

  3. 私がそれをしたとき、私は実際のテキストを手に入れました。しかし、一部のバイトコードがオフでした。次に、HttpWebResponse に示されているように、TextReader で適切なテキスト エンコーディングを使用する必要がありました。

  4. それを行った後、私は賢明な文字列を取得しましたが、結果の圧縮解除された 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;
                }
            }
        }
    }
}
于 2010-05-12T01:31:05.690 に答える