【第32回】RPGでRESTウェブサービスを構築する | IBM i 総合情報サイト

【第32回】RESTが必要? – RPGでRESTウェブサービスを構築する –


ウェブサービスは、現代のITアプリケーション構築における基本的かつ標準的なものとなっています。そこで、今回と次回の2回にわたり、IBM iによるウェブサービス構築に関する記事をお届けします。今回ご紹介する記事は約10年前の古いものですが、RESTサービス入門のさらに入り口ともいうべき内容です。「いまさら聞けないIBM iのRESTサービス」的な記事としてお読みいただきたいと思います。(編集部)

02/22/2012 ジョン・パリス

今月の記事は、最近私達が最も頻繁に訊かれる「どうやってウェブサービスを構築するのか?」という質問に答えることから始めます。

私達の元々の答えは、組み込みアプリケーションサーバー上にウェブサービスとしてRPG(またはCOBOLなど)を配置するために、システムの組み込みウェブサービス・ウィザードを使うよう提案するものでした。これは優れたツールであるものの、それはプログラムをSOAPサービスとして配置します。SOAPはとても人気がありますが、その構造が複雑なことおよびXMLに依存していることから、多くの簡単なタスクにとっては過剰なものです。

このことが分かっているので、現在は多くの人がRESTfulウェブサービスに向きを変えてきています。技術的に言うと、RESTはREpresentational State Transferの略語ですが、これでは実際のところ何も分かりません。私達がそうであったように、掘り下げる程皆さんは混乱するかも知れません

混乱は、RESTの完全な定義が、伝統的なCRUDアプリケーション(つまり、Create、Read、Update、Delete)が要求するすべてのタスクを実行する能力を網羅するという事実から生じます。これはこれで良いのですが、私達が話をしたほとんどの人は、単に情報を得ることを許すウェブサービス(つまり、Read機能)を構築することに注目しています。ですから、この記事の目的に合わせて、正式なREST定義ではHTTP GETとなる領域に話を限定します。

  1. SOAPはSimple Object Access Protocolの略ですが、この情報は恐ろしく役に立ちません。そして、“S”はXML SAXの中の”S”であると同時に、Simpleという言葉の意味を新たに追加しています。ここでSOAPに対する説明を行うよりも、単にこの話題に関するウィキペディアを参照することで良しとしましょう。これによって、読者の中の好奇心の強い人たちが何時間も読み続けるのに十分なリンクが提供されるはずです。
  2. スコット・クレメント氏のHTTPAPIは、RPGプログラマーにRESTとSOAPの両方を使わせる優れたツールです。彼のウェブサイトにはソフトウェアおよびその使用法を説明した記事とプレゼンテーションへのリンクがあります。

純粋主義的な意味では技術的に正しくないかも知れませんが、私達が特に好きなこの種のRESTfulサービスの定義は、midrange.comの議論でRJSのリチャード・シェーン氏が提案したものでした。シェーン氏はそれを次のように簡潔に定義しました:


「RESTfulとは、フォーマットされたURLをある種のパラメータと一緒に渡し、XML、JSONまたは任意の望む形式でデータを取得するものすべてです。」

RESTサービスは、ブラウザで使われるのと同じHTTPプロトコルを使うので、これに関するもう1つの考え方は、RESTfulサービスは人ではなくプログラムで読まれる一種の「ウェブページ」であるというものです。プログラムはPHP、Java、C#、RPGのどれでも構いません。

この議論の範囲が分かったところで、RESTサービスが形作られる2つの最もありふれた方式を見てみましょう。どちらもHTTP GET要求を使用しますが、パラメータ定義の方法はまったく異なります。2つのタイプに対して特別な名前があるかもしれませんが、これまでそうした名前にお目にかかったことはありません。

  1. 完全な要求がURL形式であるもの。つまり、要求される関数とそのパラメータの両方がURLの一部であるもの。パラメータは単にその相対位置で識別されます。例を以下に挙げます:
    http://xyz.com/price/P2058/50
    ここで、“price”は要求される情報のタイプを、“P2058”は質問の対象となる部品番号、”50”は価格計算される数量を指します。それが書かれている順序でそれを論理的に見ることができます。たとえば、”price”という名前のディレクトリには販売するすべての部品のディレクトリが含まれています。これらの各ディレクトリには、可能な数量ごとのファイルがあり、そのファイルには関連する価格が入っています。もちろん、実際のところシステムをこのように実装するのはまったく実現困難でしょうが、要求がこのような方法で形成される理由の心理モデルを提供するのには役立つかも知れません。
  2. 要求される情報のタイプ(つまり、アクション)はURL内に含まれているが、詳細は照会文字列内でパラメータ化されているというもの。パラメータは名前で識別されます。例は、http://xyz.com/price?part=P2058&quantity=50です。

RESTサービスのプログラミング例

先に述べたように、照会(GET)のために使用されるRESTサービスは、コンピュータ・プログラムで「見られる」ように設計された単なるウェブページとして考えることができます。それ故、他のあらゆるウェブページの構築に使用されるかも知れないツールや技法を使ってこれを構築できます。私達の例題のために、CGIDEV2 toolkitを使用します。これは多くの人に馴染みのあるものの筈です。

最初の例は以下の形式の要求を受け入れる単純なRESTサービスです:
http://www.mysystem.com/restsrv1/part1/part2/…/partn

要求されるサービス(restsrv1)は、URLの先頭で識別され、一連の部品番号がそれに続きます。そのサービスは、入手可能な数量と要求された各部品の説明を含む単純なXML文書を返します。それは以下のようなXML文書です。

< XMLDATA >
  < PARTDATA >
    < PARTNUMBER >part1< /PARTNUMBER >
    < DESCRIPTION >Description for part1< /DESCRIPTION >
    < QUANTITY >1.33< /QUANTITY >
  < /PARTDATA >
  ....
  < PARTDATA >
    < PARTNUMBER >partn< /PARTNUMBER >
    < DESCRIPTION >Description for partn< /DESCRIPTION >
    < QUANTITY >9.99< /QUANTITY >
  < /PARTDATA >
< /XMLDATA >

この例は、単に基本原理を例示することを意図しているので、部品の検索さえも行いません ― データは単なる作り物です。私達はただURLデータの取得法とその構文解析法を示したいだけです。

コードの関連する部分を見てみましょう。

(A) GetHTMLIFS( ‘/Partner400/RESTSRV1.xml’: ‘< !-- ‘: ‘ -- >‘);
    WrtSection(‘ResponseHeader’: *off: ‘‘);

// URLの後端の問い合わせデータを取得するためにgetEnvを使う
(B) pathInfo = getEnv(‘PATH_INFO’: qusec );

  // 個々の要素を抽出するために経路文字列を構文解析する
(C) elementCount = ParsePath( pathInfo: elementList);

// 結果の要素リストをループする
(D) For i = 1 to elementCount;
      ...
      WrtSection(‘responseBody’: *off: ‘‘);
    EndFor;

// すべて完了 ― バッファにトレイラーを追加し、応答を送信する
(E) WrtSection(‘responseTrailer *fini’: *off: ‘‘);

(A) この最初のセクションでは要求を送信するための準備を行います。ファイルRESTSRV1.xmlは生成するXML応答文書用のスケルトンを含んでおり、書き込みはバッファに標準のHTTP応答ヘッダを加えます。ここでは、主プロセスを開始する前にレポート用ページヘッダを書いていると考えてください。

(B)次に環境変数PATH_INFOの内容を取り出すためにgetEnv関数を使用しました。この結果には、URLの中でApacheがプログラムにメッセージを送るのに使う部分の後にあるURLの一部分が含まれます。元々のURLがhttp://www.mysystem.com/restsrv1/part1/part2であるなら、PATH_INFOには“/part1/part2”が含まれることになります。

(C)次のビジネス命令は、経路情報を構文解析して様々な部品番号に分解することです。これがParsePath関数の目的で、この関数は遭遇した部品の数を返します。この後、私達は直ぐにそのコードを見ることになります。

(D)個々の部品番号を識別した後、各部品を順番に処理し、それらのデータをバッファに追加する操作を繰り返します。

(E)すべての部品が処理されたら、単純に応答トレイラーと*finiという特別なセクションを書き出し、応答が送信されるようにします。

ParsePath関数は、斜線記号を区切り文字として文字列を個々の構成要素に分割するというとても単純な処理です。唯一の捻りは、最初のスキャンが2番目の斜線記号から始まることですが、これはPATH_INFO内の最初の文字が斜線記号であることが分かっているからです。後の処理のために、ループしながら文字列を調べ、抽出された各要素を配列に格納します。そのコードを次に示します。

   DoU position = 0;  // 処理するものが無くなったら終了する
     count += 1;
     position = %Scan(‘/’: path: start);
     If position > 0;
       elements(count) = %Subst(path: start: ( position – start ));
          start = position + 1;
     Else;
     
     // 最終要素の後ろに/が無いので最後の要素を取得する
         elements(count) = %Subst(path: start);
     EndIf;
   EndDo;

データはプログラムで使用されることを意図していますが、要求はURLの形式なのでブラウザー内でそれをテストすることもできます。これはSOAPウェブサービスではできません。このhttp://systemideveloper.com:1241/restsrv1/part1/Part2/part3というURLを使って自分の例題サービスを自分で試すことができます。URLに幾つかの部品番号を追加し、返されるXMLがブラウザーに表示されます。見た目にはあまり期待しないでください。これは人間ではなくプログラムで使用することを意図したものだということを忘れないでください。

もう1つの例

次の例は、2番目のタイプに対するものです。つまり、要求される関数はURL内で指定されますが、特定のパラメータは照会文字列の中にあります。私達の例は、
http://systemideveloper.com:1241/restsrv2/quantityquery?partnum=0000011&quantity=5
のような要求をサポートします。要求される関数(quantifyquery)はURL内にありますが、パラメータ(partnumとquantity)は照会文字内の名前付けされたパラメータであり、最初の例の様にURL内で単に順番に並べられたパラメータではないことに注意してください。実際、このプログラムは現在2つのタイプの要求、つまり数量照会(quantityquery)と価格照会(pricequery)をサポートします。それ以外のものはすべてエラーメッセージと共に拒絶されます。私達の言う事を信じずに、上記のリンクを使って自分自身でテストしてください。もう少し詳細に調査したければ、この記事の最後に幾つかの有効な部品番号のリストを記載します。このサービスは1度に1つの部品照会しかサポートしないことに注意してください。

この2つ目のバージョンが何をするかについては既に述べましたので、基本的なコードを見てみましょう。処理の第1ステップ(スケルトンを取得し、応答ヘッダを書く処理)は最初の例と同じですので、ここでは省略しました。

(F) pathInfo = getEnv(‘PATH_INFO’: qusec );
    service = %Subst(pathInfo: 2);

(G) inputCount = ZhbGetInput(savedQuery: QUSEC);
   
  // 部品番号が存在すると仮定してそれを取得しに行く
    partNum = ZhbGetVar(‘partnum’);
    ...

(H) Select;
      When service = QUANTITY_QUERY;
        CheckQuantity(partNum);
      When service = PRICE_QUERY;
        CheckPrice(partNum);
    ...

(I)	Other; // 未知の要求 - エラー応答を作成
        request = getEnv(‘QUERY_STRING’: qusec );
        ...
        WrtSection(‘ServiceError’);

(F)前の例と同様、経路情報を取得するためにgetEnvを使用しています。というのも、この関数は要求されたサービスの名前を与えてくれるからです。

(G)次に、ZhbGetInputを呼び出します。これは照会文字列を構文解析し、パラメータを取り出し、後からZhbGetVarで抽出するためにそれらを格納します。このプログラムでは、このAPIから返されるパラメータの個数を使っていませんが、何個のパラメータが存在するかを確定するのに役立つ可能性があります。

(H)後は要求されている特定のサービスに基づいて処理を振り向けるだけです。

(I)要求されたサービスの名前が未知ならば、エラー報告を助けるために全照会文字列を取り出します。先に述べたURLを改変することにより、この結果を自身で見ることができます。

コードの他の部分で、注釈を加えておきたい唯一ものはCheckQuantity関数です。なぜなら、この関数はオプションのパラメータに対応するからです。どのようにこれらが取り扱われるかを示すために、quantityパラメータをオプションにし、数量の指定がない場合にはプログラムが数量1を使用する様にしました。これを処理するコードの一部は次のとおりです。

(J) If ( ZhbGetVarCnt(‘quantity’) > 0 );  //数量が指定されているか?
      Monitor;
        quantity = %Int( ZhbGetVar(‘quantity’) );
      On-error;  //エラーなら省略時値を設定する
        quantity = 1;
      EndMon;
    Else;
      quantity = 1;  //数量の指定が無ければ省略時値を設定する
    EndIf;

(J)数量が提供されているかどうかを調べるためにZhbGetVarCnt APIを使用します。提供されている場合は、その値を取得するためにZhbGetVarが使用されます。提供されていない(または、提供された値が数字でない)場合は、省略時の値1が使用されます。オプションのパラメータを処理する場合、あるいは、たとえば前の例でやったように1つの要求の中で複数の部品の照会が行われるのを許すようなサービスを作成する場合、ZhnGetVarCntは役に立ちます。

やるべきことはまだ沢山ある

私達はRESTfulウェブサービスで出来ることのほんの一部を学んだだけです。たとえば、ブラウザーからのAjax要求に対する応答の中にJSON文字列を返すサービスがあります。JSONとAjaxに興味が十分あるなら、将来この話題を掘り下げてみようと思います。

話は変わって、この記事で取り上げた例題の完全なソースコードは、Apacheの構成ディレクティブに関する追加の詳細と共に、私達のウェブサイトにあります。

RESTの技術的な側面をもっと詳しく調べたい人は、Googleを調べれば多くの説明が見つかるでしょう。しかし、私達のお勧めは、私達が最近遭遇したIBMのウェブサイトから始めることです。このウェブサイトは間違いなく最善のものの1つです。

Copyright © IGUAZU