#navi(contents-page-name): No such page: .NET プログラミング研究
.NETプログラミング研究 第74号 †
.NET Tips †
XML-RPCを使ったWebサービスにアクセスする †
今回はXML-RPCによるWebサービスにアクセスする方法について説明します。XML-RPCはHTTPのPOSTでXMLデータを送信し、XMLデータとして結果を取得します。なおXML-RPCの仕様については、「XML-RPC 仕様書」をご覧ください。
現在公に公開されているWebサービスでは、XML-RPCを使用したサービスは少ないようです。しかし、XML-RPCはブログ関係のAPIとしてはよく使われてきました。ブログへの投稿や、記事の更新、削除、情報の取得などを行うためのAPIであるBlogger APIや、Pingサーバーにブログの更新を伝えるためのWeblogs.Com XML-RPC interfaceなどはXML-RPCです。
そこでここでは、XML-RPCクライアントを作成する例として、Blogger APIを使用する方法を紹介します。
Blogger APIを使う †
前述した通り、Blogger APIは、ブログへの投稿や、記事の更新、削除、さらに投稿された記事の情報の取得等を可能にするAPIです。さらにBlogger APIを拡張したmetaWeblog APIやMovable Type独自の拡張であるMovableType APIもよく知られています。Blogger APIはMovable Typeをはじめ多くのブログアプリケーションや、ホスティング型のブログサービスで利用できます。
早速ですが、Blogger APIを使用する具体例として、blogger.getRecentPostsメソッドを呼び出して一番最近投稿した記事の内容(content)を取得し表示するサンプルを紹介します。POST送信するXMLデータの作成や、結果として返されたXMLデータの解析は手抜きですが(faultが返された時の処理も省略しています)、大雑把にはこんな感じです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
| | <%@ 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 requestUrl As String = _
"http://localhost/mt/mt-xmlrpc.cgi"
Dim methodName As String = "blogger.getRecentPosts"
Dim blogID As String = "1"
Dim userName As String = "username"
Dim password As String = "password"
Dim numberOfPosts As Integer = 1
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)
Dim request As System.Net.HttpWebRequest = _
CType(System.Net.WebRequest.Create(requestUrl), _
System.Net.HttpWebRequest)
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()
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>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
| | <%@ 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)
{
string requestUrl =
"http://localhost/mt/mt-xmlrpc.cgi";
string methodName = "blogger.getRecentPosts";
string blogID = "1";
string userName = "username";
string password = "password";
int numberOfPosts = 1;
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);
System.Net.HttpWebRequest request =
(System.Net.HttpWebRequest)System.Net.WebRequest.Create(
requestUrl);
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();
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を使用する †
上記の例はかなり手を抜きましたが、それでも結構大変です。ちゃんとしたものを作るとなると、なおさらです。ありがたい事に、XML-RPCを実装するためのオープンソースのライブラリが存在します。それが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」で説明されています。
1
2
3
4
5
6
7
8
9
10
| | 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
|
1
2
3
4
5
6
7
8
9
10
11
12
| | 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属性を付けることもできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| | 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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| | 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メソッドを呼び出すだけです。実際のコードは以下のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| | <%@ 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)
proxy.Url = "http://localhost/mt/mt-xmlrpc.cgi"
Dim ps() As BloggerAPI.Post
Try
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
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>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| | <%@ 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));
proxy.Url = "http://localhost/mt/mt-xmlrpc.cgi";
BloggerAPI.Post[] ps;
try
{
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)
{
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を使用する場合は、次のようにしてプロキシクラスのインスタンスを作成します。
1
2
3
4
| | Dim proxy As BloggerAPI.IBlogger = _
CookComputing.XmlRpc.XmlRpcProxyGen.Create( _
Of BloggerAPI.IBlogger)()
|
1
2
3
| | BloggerAPI.IBlogger proxy =
CookComputing.XmlRpc.XmlRpcProxyGen.Create<BloggerAPI.IBlogger>();
|
コメント †