スクリプトのお勉強

Mojolicious XML-RPC Pluginの開発(1)

投稿日:2019年5月6日 更新日:

はじめに

最近、お仕事関連で、perlとWebアプリケーションを触っています。

両方一度にお勉強、ということで、perlのWAF(Webアプリケーションフレームワーク)を調べてみようと思いました。
ここでは、perlのMojolicious上で動作する、XML-RPC Pluginを作ってみようと思います。

説明は以下の順で行っていきます。記述内容が多くなってしまったので、(1)と(2)に分けます。

  1. Mojoliciousとは (1)
  2. Mojolicious上でXML-RPC関数を直接実装(Pluginなし)(1)
  3. 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を実装しようと思います。

-スクリプトのお勉強

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

PythonでAESを使用して暗号/復号する

1.つづき Pythonで、PKCS#12の公開鍵で暗号、秘密鍵で復号するプログラムの続きです。 今回はAESで暗号化/復号を行い、通信しながらRSA暗号/復号,AES暗号/復号を組み合わせたいと思い …

Nuxt.js – CRUDアプリケーションのフォーム/一覧を作成する

前回で作ったAPIのフロントエンドアプリケーションを作ろうと思います。 どういうアプリ? サンプルとして作ったAPIが住所録的だったので、住所録を作りました。 以下の機能があります。 登録(確認付き) …

React.js の Ant Design使ってみる(DateTimePicker編)

DateTimePickerサンプル DateTimePickerを使うサンプルで、いいのがなかなかないです。Dateだけとか、Timeだけってのはあるのですが。 ということで作ってみようと思いました …

Thunderbird プラグインの開発(プラグインでの実現方法)

前回 Thunderbird プラグインの WebExtension版開発方法を調べたので、開発したいプラグインと、WebExtension版プラグインでの実現方法について書いていきます。 開発するプ …

言語別ログイン機能パスワード保存処理方針

ちょっと前に、ログイン機能を作成した際、パスワードを暗号化するか、という議論を目にしたことがありました。 昔だと、「パスワードを暗号化しない」方で実装していましたが、最近はセキュリティが当たり前になっ …