• 追加された行はこの色です。
  • 削除された行はこの色です。
*.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!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]