はじめに
最近、お仕事関連で、perlとWebアプリケーションを触っています。
両方一度にお勉強、ということで、perlのWAF(Webアプリケーションフレームワーク)を調べてみようと思いました。
ここでは、perlのMojolicious上で動作する、XML-RPC Pluginを作ってみようと思います。
説明は以下の順で行っていきます。記述内容が多くなってしまったので、(1)と(2)に分けます。
- Mojoliciousとは (1)
- Mojolicious上でXML-RPC関数を直接実装(Pluginなし)(1)
- Mojolicious XML-RPC Plugin経由でXML-RPC実装 (2)
サンプルの実装は、[mojoxmlrpc]に置いてあります。
1. Mojoliciousとは
“Mojolicious”とはperlのWAFです。なんとなくモダンそうなので調べてみました。
[Mojolicious] については、[Mojoliciousの日本語版wiki]によくまとまってます。インストール方法から概要まで、一通り記述があります。
1.1 XML-RPCとは
[XML-RPC]とは、呼び出しをXMLで符号化し、HTTPを通信プロトコルに使用する RPC プロトコルです。(リンク先より引用)
SOAPと呼ばれていた規格の前身といえる規格です。特徴としては扱いやすいことだと思います。
XML-RPCはSOAP(のWSDL)のように、呼び出し方法を規定する正式なやりかたはありません。呼び出し方法は別プロトコル(主に人間が判断する文書)にて定義します。
perlのXML-RPCの実装は、[Frontier-RPC2]を使用しました。(リンク先の内容はよくまとまっていておすすめです)
1.2 動作環境
動作させたのは、以下の環境です。
- perl: 5.26.1
- Mojolicious: 7.64
- Frontier::RPC2: 0.07b4
2. Mojolicious上でXML-RPC関数を直接実装
ここでは、Mojoliciousが直接「XML-RPCサーバ」となり、XML-RPCクライアントを待ち受けるサーバを作ってみようと思います。
作成手順としては以下になります。MojoliciousやFrontier::RPC2が、既にインストールしてある場合は(3)以降の手順を行ってください。
なお、XML-RPCクライアントは、簡単に作るため、pythonにしています。
(1) Mojoliciousインストール
$ curl -L https://cpanmin.us | perl - -M https://cpan.metacpan.org -n Mojolicious
(2) Frontier::RPCのインストール
CPANよりインストールします。
$ cpanm Frontier::RPC2
(3)「XML-RPCサーバ」作成
詳細は、以降(2.1 XML-RPCサーバ作成詳細)に記述します。
(4) 「XML-RPCサーバ」起動
$ script/xmlrpctest daemon --listen http://0.0.0.0:8000
(5) XML-RPCクライアントから通信
別マシン(ここではWindows上)でpythonからXML-RPC呼び出しを行います。 詳細は、以降(3.
XML-RPCクライアント側)に記述します。
$ python xmlrpc_client.py 1 + 2 : 3 3 - 2 : 1 {'a': 'b', 'c': [1, 3, 2], 'b': 1500000000}
2.1 XML-RPCサーバ作成詳細
以下に「XML-RPCサーバ」の概要、および作成手順を説明します。
2.1.1 概要(各コンポーネント)
まず、「XML-RPCサーバ」内部には、以下のコンポーネント(部品)があります。
- Mojolicious(雛形)
Mojoliciousによって自動生成される、アプリケーションの雛形。
雛形を修正していくことでWebアプリケーションを実装できる。 - アプリケーションクラス Webアプリケーション全体を制御するクラス
- コントローラクラス WebアプリケーションのMVC中、コントローラを実装するクラス
XML-RPCクライアントも含めた、各コンポーネントの関連は、以下のようになっています。
XML-RPCクライアント(python) -> 「XML-RPCサーバ」 -> Mojolicious(雛形) -> アプリケーションクラス -> コントローラクラス
次に、各コンポーネントをどのように作成するのか、作成手順を示します。
2.1.2 各コンポーネント作成手順
「XML-RPCサーバ」の各コンポーネントを、以下のように実装しました。
(1) Mojoliciousによる雛形作成
Mojoliciousでの雛形は以下のように作成します。
$ mojo generate app Xmlrpctest
上記のコマンドにて、「Xmlrpctestアプリケーション」という形でアプリケーションを作成します。起動すると、以下のようなファイルが生成されます。
xmlrpctest/ xmlrpctest.conf lib/ Xmlrpctest/ Controller/ Example.pm Xmlrpctest.pm public/ index.html script/ xmlrpctest t/ basic.t templates/ example/ welcome.html.ep layouts/ default.html.ep
次に、アプリケーションクラスとコントローラクラスを追加します。主に修正/追加するファイルは以下です。
lib/Xmlrpctest.pm lib/Xmlrpctest/Method.pm
(2) アプリケーションクラス(lib/Xmlrpctest.pm)修正
アプリケーションクラスとは、アプリケーションを起動した際に、最初に呼ばれるクラスです。
主に、「ルート設定」(後述)を行います。
「XML-RPCサーバ」アプリケーションクラスは、XML-RPCのURL(/RPC2)にアクセスされた場合に、
どのコントローラに遷移するか設定しています。
以下がアプリケーションクラスの中心です。
アプリケーションが起動されると、startup関数が呼び出される仕様になっています。
package Xmlrpctest; use Mojo::Base 'Mojolicious'; # This method will run once at server start sub startup { my $self = shift; # Router my $r = $self->routes; # $r->get('/RPC2')->to('method#process'); $r->post('/RPC2')->to('method#process'); } 1;
特に重要なのが以下です。/RPC2というURLにアクセス(GETメソッド)が来たら、
Methodというコントローラクラスのprocessという関数に遷移するという設定です。
$r->get('/RPC2')->to('method#process');
(3) コントローラクラス(lib/Xmlrpctest/Method.pm)追加
コントローラクラスとは、MVCモデルでの、いわゆるコントローラです。要するに、リクエストを受信しています。
「XML-RPCサーバ」でのコントローラクラスは、Method.pmという形で実装しています。
XML-RPCリクエスト(URLは「/RPC2」)を受信した場合の処理を担当しています。
Method.pm内は、以下の処理を行います。
- (1) XML-RPCリクエスト受信
- (2) 「Frontier::RPCライブラリ」(後述)に「XML-RPCリクエスト」入力
- (3) 「Frontier::RPCライブラリ」から返却された、「出力XMLデータ」を、まるごとWebクライアントに返却
XMLのHTTPレスポンス生成は、Mojoliciousのレンダラー(render)に、formatをXMLを設定すれば可能です。
lib/Xmlrpctest/Method.pmの具体的内容は以下の通りです。
package Xmlrpctest::Controller::Method; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Home; use Frontier::RPC2; my $log = Mojo::Log->new; #外部メソッド名と内部関数の紐付け設定 my $methods = { 'xml.rpctestplus' => \&xmlrpcplus , 'xml.rpctestminus' => \&xmlrpcminus , 'xml.rpctestmixed' => \&xmlrpcmixed }; #受信 sub process { my $self = shift; #my $c = $self->ctx; $log->debug("in progress"); my $server = Frontier::RPC2->new( 'encoding' => 'UTF-8' ); if ( !defined $server ) { $self->render_text("frontier failed"); return ; } my $body = $self->req->body; $log->debug("body: $body "); #request(XML)からresponseに変換 my $response = $server->serve($body, $methods); $log->debug("res: $response "); #HTTPレスポンスに変換 $self->render(text=>$response,format=>"xml"); } #API sub xmlrpcplus { (my $a , my $b) = @_; return $a + $b; } sub xmlrpcminus { (my $a , my $b) = @_; return $a - $b; } sub xmlrpcmixed { (my $a , my $b) = @_; my @c = (1,3,2); my $data = {a=>'b',b=>$a,c=>\@c}; return $data; }
「Frontier::RPCライブラリ」の詳細は、今回は扱いません。「Frontier::RPCライブラリ」とは、以下の「XML-RPCリクエスト」を受信し、「出力XMLデータ」(XML-RPCレスポンス)を生成するライブラリだと思ってください。
「XML-RPCリクエスト」
body: <?xml version='1.0'?> <methodCall> <methodName>xml.rpctestmixed</methodName> <params> <param> <value><int>1500000000</int></value> </param> <param> <value><array><data> <value><int>1</int></value> <value><int>2</int></value> <value><struct> <member> <name>foo</name> <value><string>test</string></value> </member> <member> <name>bar</name> <value><string>data</string></value> </member> </struct></value> <value><int>3</int></value> </data></array></value> </param> </params> </methodCall>
「出力XMLデータ」
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param><value><struct> <member><name>c</name><value><array><data> <value><i4>1</i4></value><value><i4>3</i4></value><value><i4>2</i4></value></da ta></array></value> </member> <member><name>a</name><value><string>b</string></value></member> <member><name>b</name><value><i4>1500000000</i4></value></member> </struct></value> </param> </params> </methodResponse>
(4) 不要部分の削除
不要部分を削除すると以下になります。
xmlrpctest/ lib/ Xmlrpctest/ Controller/ Method.pm Xmlrpctest.pm log/ script/ xmlrpctest t/ basic.t
3. XML-RPCクライアント側
クライアント側アプリケーションは、pythonで作成しました。以下の通り数行です。
IPアドレスは適宜変更してください。
try: import xmlrpclib as client except ImportError: import xmlrpc.client as client proxy = client.ServerProxy("http://192.168.132.131:8000/RPC2") print("1 + 2 : %d" % ( proxy.xml.rpctestplus(1,2) )) print("3 - 2 : %d" % ( proxy.xml.rpctestminus(3,2) )) d={'foo':'test','bar':'data'} b=[1,2,d,3] a = proxy.xml.rpctestmixed(1500000000,b) print( a )
Windows上で起動した結果は以下の通りです。
$ pyton xmlrpc_client.py 1 + 2 : 3 3 - 2 : 1 {'a': 'b', 'c': [1, 3, 2], 'b': 1500000000}
4. Mojoliciousで、はまったところ
私はサンプルを理解する際に、ファイル名や関数を変えて動作させることがあります。その際に、ファイル/関数の命名規則等を知らなくて、ちょっとはまることがあります。
Mojociliousの場合は、以下が分かりませんでした。
- アプリケーションの最初がどこなのか分からなかった。
- コントローラクラスの追加方法が分からなかった。
アプリケーションの最初とは、要するに全体処理の開始点です。起動したら、まずどこから処理が開始されるか、ですが、「XML-RPCサーバ」では、lib/Xmlrpctest.pmが該当します。
コントローラクラスの追加方法は、上記の通り、アプリケーションクラスのstartup関数で、ルート(routes)に設定することにより行えます。
ちなみに、雛形ですとlib/「アプリケーション名」/Example.pmがコントローラとして、あらかじめ追加しています。
5.おわりに
これで、一通りMojoliciousでのアプリケーション作成は可能になったと思います。
次は、XML-RPCサーバとしてではなく、Mojoliciousのプラグイン機能を使用してXML-RPCを実装しようと思います。