*.NETプログラミング研究 第74号 [#q8960d7b] **.NET Tips [#xd9d44ce] ***XML-RPCを使ったWebサービスにアクセスする [#v9fa81fa] 今回はXML-RPCによるWebサービスにアクセスする方法について説明します。XML-RPCはHTTPのPOSTでXMLデータを送信し、XMLデータとして結果を取得します。なおXML-RPCの仕様については、「XML-RPC 仕様書」をご覧ください。 -[[XML-RPC 仕様書>http://lowlife.jp/yasusii/stories/9.html]] 現在公に公開されているWebサービスでは、XML-RPCを使用したサービスは少ないようです。しかし、XML-RPCはブログ関係のAPIとしてはよく使われてきました。ブログへの投稿や、記事の更新、削除、情報の取得などを行うためのAPIであるBlogger APIや、Pingサーバーにブログの更新を伝えるためのWeblogs.Com XML-RPC interfaceなどはXML-RPCです。 -[[Weblogs.Com XML-RPC interface>http://www.xmlrpc.com/weblogsCom]] そこでここでは、XML-RPCクライアントを作成する例として、Blogger APIを使用する方法を紹介します。 ***Blogger APIを使う [#z8c18df0] 前述した通り、Blogger APIは、ブログへの投稿や、記事の更新、削除、さらに投稿された記事の情報の取得等を可能にするAPIです。さらにBlogger APIを拡張したmetaWeblog APIやMovable Type独自の拡張であるMovableType APIもよく知られています。Blogger APIはMovable Typeをはじめ多くのブログアプリケーションや、ホスティング型のブログサービスで利用できます。 -[[Movable Type 3.3 マニュアル - XML-RPC API>http://www.sixapart.jp/movabletype/manual/3.3/03_blog_username_guide/customizing_blogs/xmlrpc_api.html]] -[[MovableType で使える XML-RPC API>http://www.na.rim.or.jp/~tsupo/program/blogTool/mt_xmlRpc.html]] 早速ですが、Blogger APIを使用する具体例として、blogger.getRecentPostsメソッドを呼び出して一番最近投稿した記事の内容(content)を取得し表示するサンプルを紹介します。POST送信するXMLデータの作成や、結果として返されたXMLデータの解析は手抜きですが(faultが返された時の処理も省略しています)、大雑把にはこんな感じです。 #code(vbnet){{ <%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub Button1_Click( _ ByVal sender As Object, ByVal e As System.EventArgs) 'リクエストURL Dim requestUrl As String = _ "http://localhost/mt/mt-xmlrpc.cgi" 'メソッド名 Dim methodName As String = "blogger.getRecentPosts" 'ブログのID Dim blogID As String = "1" 'ユーザー名とパスワード Dim userName As String = "username" Dim password As String = "password" '取得する投稿の数 Dim numberOfPosts As Integer = 1 'POST送信するXMLを作成 Dim postData As String = "<?xml version=""1.0""?>" _ + "<methodCall>" _ + "<methodName>{0}</methodName>" _ + "<params>" _ + "<param><value><string>{1}</string></value></param>" _ + "<param><value><string>{2}</string></value></param>" _ + "<param><value><string>{3}</string></value></param>" _ + "<param><value><string>{4}</string></value></param>" _ + "<param><value><i4>{5}</i4></value></param>" _ + "</params>" _ + "</methodCall>" postData = String.Format(postData, _ methodName, _ "", _ blogID, _ userName, _ password, _ numberOfPosts) Dim postDataBytes As Byte() = _ System.Text.Encoding.UTF8.GetBytes(postData) 'HttpWebRequestの作成 Dim request As System.Net.HttpWebRequest = _ CType(System.Net.WebRequest.Create(requestUrl), _ System.Net.HttpWebRequest) 'POST request.Method = "POST" request.ContentType = "text/xml" request.ContentLength = postDataBytes.Length Dim reqStream As System.IO.Stream = request.GetRequestStream() reqStream.Write(postDataBytes, 0, postDataBytes.Length) reqStream.Close() Dim response As System.Net.HttpWebResponse = Nothing Dim doc As New System.Xml.XmlDocument() Try 'レスポンスの取得 response = CType(request.GetResponse(), _ System.Net.HttpWebResponse) Dim strm As System.IO.Stream = response.GetResponseStream() 'XmlDocumentに読込む doc.Load(strm) Catch ex As Exception Label1.Text = "エラー:" + Server.HtmlEncode(ex.Message) Return Finally If Not (response Is Nothing) Then response.Close() End If End Try '結果を解析する Dim paramsNode As System.Xml.XmlNode = _ doc.SelectSingleNode("/methodResponse/params") If paramsNode Is Nothing Then Label1.Text += "該当する投稿が見つかりませんでした。" Return End If Dim node As System.Xml.XmlNode For Each node In paramsNode.SelectNodes("//member") If node.SelectSingleNode("name").InnerText = "content" Then Label1.Text += node.SelectSingleNode("value").InnerText _ + "<hr />" End If Next node End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Blogger APIのサンプル</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="開始" /><br /> <br /> <asp:Label ID="Label1" runat="server" EnableViewState="False"></asp:Label></div> </form> </body> </html> }} #code(csharp){{ <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Button1_Click(object sender, EventArgs e) { //リクエストURL string requestUrl = "http://localhost/mt/mt-xmlrpc.cgi"; //メソッド名 string methodName = "blogger.getRecentPosts"; //ブログのID string blogID = "1"; //ユーザー名とパスワード string userName = "username"; string password = "password"; //取得する投稿の数 int numberOfPosts = 1; //POST送信するXMLを作成 string postData = @"<?xml version=""1.0""?> <methodCall> <methodName>{0}</methodName> <params> <param> <value> <string>{1}</string> </value> </param> <param> <value> <string>{2}</string> </value> </param> <param> <value> <string>{3}</string> </value> </param> <param> <value> <string>{4}</string> </value> </param> <param> <value> <i4>{5}</i4> </value> </param> </params> </methodCall>"; postData = string.Format(postData, methodName, "", blogID, userName, password, numberOfPosts); byte[] postDataBytes = System.Text.Encoding.UTF8.GetBytes(postData); //HttpWebRequestの作成 System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create( requestUrl); //POST request.Method = "POST"; request.ContentType = "text/xml"; request.ContentLength = postDataBytes.Length; System.IO.Stream reqStream = request.GetRequestStream(); reqStream.Write(postDataBytes, 0, postDataBytes.Length); reqStream.Close(); System.Net.HttpWebResponse response = null; System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); try { //レスポンスの取得 response = (System.Net.HttpWebResponse)request.GetResponse(); System.IO.Stream strm = response.GetResponseStream(); //XmlDocumentに読込む doc.Load(strm); } catch (Exception ex) { Label1.Text = "エラー:" + Server.HtmlEncode(ex.Message); return; } finally { if (response != null) response.Close(); } //結果を解析する System.Xml.XmlNode paramsNode = doc.SelectSingleNode("/methodResponse/params"); if (paramsNode == null) { Label1.Text += "該当する投稿が見つかりませんでした。"; return; } foreach (System.Xml.XmlNode node in paramsNode.SelectNodes("//member")) { if (node.SelectSingleNode("name").InnerText == "content") { Label1.Text += node.SelectSingleNode("value").InnerText + "<hr />"; } } } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Blogger APIのサンプル</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="開始" /><br /> <br /> <asp:Label ID="Label1" runat="server" EnableViewState="False"></asp:Label></div> </form> </body> </html> }} リクエストを送信するURLは、Movable Typeの場合、mtフォルダ内にあるmt-xmlrpc.cgiとなります。また、XML-RPCには文字コードに関する明確な規定がないようですが、送信するリクエストは適当な文字コード(多分ブログで使用している文字コードとなるでしょう)で送受信する必要があるようです。 ***XML-RPC.NETを使用する [#z8ce0572] 上記の例はかなり手を抜きましたが、それでも結構大変です。ちゃんとしたものを作るとなると、なおさらです。ありがたい事に、XML-RPCを実装するためのオープンソースのライブラリが存在します。それがXML-RPC.NETです。 -[[XML-RPC.NET>http://www.xml-rpc.net/]] 先と同じようにblogger.getRecentPostsメソッドを今度はXML-RPC.NETを使って呼び出してみましょう。 まずはDLLをプロジェクトの参照に追加します。XML-RPC.NETには2つのDLLがあります。CookComputing.XmlRpc.dllは.NET Frameworkのすべてのバージョンで使用できますが、CookComputing.XmlRpcV2.dllは2.0以上でしか使用できません。ここではすべてのバージョンで使用できるCookComputing.XmlRpc.dllを使います。 次に下に示すような構造体を宣言します。呼び出すメソッドのパラメータや戻り値に構造体が含まれないのであればその必要はありませんが、blogger.getRecentPostsメソッドの場合は、戻り値が構造体の配列となりますので、これに対応する構造体を宣言する必要があります(「Movable Type 3.3 マニュアル - XML-RPC API」をご覧ください)。なぜこのような構造体になるのかについては簡単にお分かりいただけると思いますので、特に説明の必要は無いでしょう。ただ、ISO.8601はDateTime型となります。これは、「XML-RPC.NET FAQ」で説明されています。 -[[XML-RPC.NET FAQ>http://www.xml-rpc.net/faq/xmlrpcnetfaq.html#1.9]] #code(vbnet){{ Imports System Namespace BloggerAPI Public Structure Post Public dateCreated As DateTime Public userid As String Public postid As String Public content As String End Structure End Namespace }} #code(csharp){{ using System; namespace BloggerAPI { public struct Post { public DateTime dateCreated; public string userid; public string postid; public string content; } } }} 次に、IXmlRpcProxyを継承した以下のようなインターフェイスを宣言します。XmlRpcMethod属性で対応するXML-RPCのメソッドを指定します。ここではblogger.getRecentPostsメソッドしかありませんが、その他のメソッドを記述してももちろんOKです。もしリクエスト先のURLが固定されているならば、インターフェイスにXmlRpcUrl属性を付けることもできます。 #code(vbnet){{ Imports System Imports CookComputing.XmlRpc Namespace BloggerAPI Public Interface IBlogger Inherits IXmlRpcProxy <XmlRpcMethod("blogger.getRecentPosts")> _ Function getRecentPosts( _ ByVal appkey As String, _ ByVal blogid As String, _ ByVal username As String, _ ByVal password As String, _ ByVal numberOfPosts As Integer) As Post() End Interface End Namespace }} #code(csharp){{ using System; using CookComputing.XmlRpc; namespace BloggerAPI { public interface IBlogger : IXmlRpcProxy { [XmlRpcMethod("blogger.getRecentPosts")] Post[] getRecentPosts( string appkey, string blogid, string username, string password, int numberOfPosts); } } }} ここまでできればあとは実に簡単です。XmlRpcProxyGen.Createメソッドでプロキシクラスのインスタンスを作成し、getRecentPostsメソッドを呼び出すだけです。実際のコードは以下のようになります。 #code(vbnet){{ <%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub Button1_Click( _ ByVal sender As Object, ByVal e As System.EventArgs) 'プロキシクラスのインスタンスを作成 Dim proxy As BloggerAPI.IBlogger = _ CType(CookComputing.XmlRpc.XmlRpcProxyGen.Create( _ GetType(BloggerAPI.IBlogger)), BloggerAPI.IBlogger) 'URLを指定 proxy.Url = "http://localhost/mt/mt-xmlrpc.cgi" Dim ps() As BloggerAPI.Post Try 'blogger.getRecentPostsを呼び出す ps = proxy.getRecentPosts("", "1", "username", "password", 1) Catch ex As Exception Label1.Text = "エラー:" + Server.HtmlEncode(ex.Message) Return End Try '結果を表示する If ps Is Nothing Or ps.Length = 0 Then Label1.Text = "該当する投稿が見つかりませんでした。" Return End If Label1.Text = "" Label1.Text += "<ul>" Dim p As BloggerAPI.Post For Each p In ps 'contentを表示 Label1.Text += "<li>" + p.content Next p Label1.Text += "</ul>" End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Blogger APIのサンプル</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="開始" /><br /> <br /> <asp:Label ID="Label1" runat="server" EnableViewState="False"></asp:Label></div> </form> </body> </html> }} #code(csharp){{ <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void Button1_Click(object sender, EventArgs e) { //プロキシクラスのインスタンスを作成 BloggerAPI.IBlogger proxy = (BloggerAPI.IBlogger) CookComputing.XmlRpc.XmlRpcProxyGen.Create( typeof(BloggerAPI.IBlogger)); //URLを指定 proxy.Url = "http://localhost/mt/mt-xmlrpc.cgi"; BloggerAPI.Post[] ps; try { //blogger.getRecentPostsを呼び出す ps = proxy.getRecentPosts( "", "1", "username", "password", 1); } catch (Exception ex) { Label1.Text = "エラー:" + Server.HtmlEncode(ex.Message); return; } //結果を表示する if (ps == null || ps.Length == 0) { Label1.Text = "該当する投稿が見つかりませんでした。"; return; } Label1.Text = ""; Label1.Text += "<ul>"; foreach (BloggerAPI.Post p in ps) { //contentを表示 Label1.Text += "<li>" + p.content; } Label1.Text += "</ul>"; } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Blogger APIのサンプル</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="開始" /><br /> <br /> <asp:Label ID="Label1" runat="server" EnableViewState="False"></asp:Label></div> </form> </body> </html> }} ちなみにCookComputing.XmlRpcV2.dllを使用する場合は、次のようにしてプロキシクラスのインスタンスを作成します。 #code(vbnet){{ 'プロキシクラスのインスタンスを作成 Dim proxy As BloggerAPI.IBlogger = _ CookComputing.XmlRpc.XmlRpcProxyGen.Create( _ Of BloggerAPI.IBlogger)() }} #code(csharp){{ //プロキシクラスのインスタンスを作成 BloggerAPI.IBlogger proxy = CookComputing.XmlRpc.XmlRpcProxyGen.Create<BloggerAPI.IBlogger>(); }} //これより下は編集しないでください #pageinfo(,2010-03-19 (金) 02:59:54,DOBON!,2010-03-19 (金) 02:59:54,DOBON!) |