目次


Web サービスと Ajax とを組み合わせて使う

2 つの最先端技術の組み合わせは思ったよりも容易です

Comments

Web サービスとは何か

Web サービスはアプリケーションの機能を World Wide Web を使って公開するための手段です。Web サービスはそれを、オープン・プロトコルを使って行います。そのため、Web にアクセスできるアプリケーションであれば、Web サービスにアクセスすることができます。

Web サービスの典型的なやり取りでは、コンシューマー (Web サービスを利用するリモート・アプリケーション) が Web サービスに対して XML メッセージを送信すると、Web サービスはそのメッセージを解析し、同じく XML フォーマットでレスポンスを返送します。次にコンシューマーはそのレスポンスを解析し、Web サービスから取得した情報を何らかの形で使用します。

多くの場合、Web サービスに使用される XML 言語は SOAP です。SOAP と言えば、元々は Simple Object Access Protocol の頭字語でしたが、今や、そうとは言えなくなっています。SOAP は、エンベロープ、データ型ルール、操作リクエストとレスポンスを定義する手段、という 3 つの部分から構成されています。

Web サービスは、WSDL (Web Services Description Language) として知られる XML 文書によって定義されます。WSDL によって、Web サービスを使って公開される操作、それらの操作に使用されるデータ型定義、その Web サービスとの通信に使われるプロトコル、そして Web サービス自体の場所を指定します。

Web サービスの利点は、多様なプログラミング言語で作成され、多様なプラットフォームにデプロイされたアプリケーション同士が広大に広がるインターネット上で相互に通信できることです。

Ajax とは何か

Ajax は最先端技術の 1 つであり、Ajax を利用することでリッチ・クライアントの表示を実現することができます。Ajax は現在の表示に影響を与えることなく新しいリクエストを呼び出すことで、リッチ・クライアントの表示を実現します。リクエストに対して XML 文書が返され、その XML 文書は、多くの場合は現在の表示のサブページとしてユーザーに表示されます。つまり Ajax によって、一見クライアント・サイドの動的コンテンツのように見せながら、サーバー・サイドの動的コンテンツを活用することができます。

Ajax は通常、そうした機能を XMLHttpRequest という DOM API を使って実現します。Ajax が登場するまで、XMLHttpRequest はほとんど使用されることはありませんでした。リクエスト自体は GET または POST のいずれかになります。

他のリクエストの場合と同様、返されたレスポンスがエラーの場合もあります。レスポンスがエラーではない場合には、そのレスポンスの実際のテキストを使って現在の表示が更新されます。

ボルテール (Voltaire) の名言「神聖ローマ帝国は、神聖でもなければ、ローマ的でもなく、そもそも帝国ですらない (the Holy Roman Empire was neither holy, nor Roman, nor an empire)」を思い出し、さまざまな Ajax 実装を詳しく調べてみると、Ajax に JavaScript は必要なく、XML も必要なく、非同期である必要もありません。それらをすべて取り除くと、Ajax という頭字語の中で残る部分は接続詞 (and) のみです。しかし Ajax という響きはクールなため、業界はこの頭字語を使い続けることにしたのです。

Ajax と Web サービスとをどう組み合わせるのか

次のように考えてみてください。「リッチなクライアント・エクスペリエンスと、インターネット上のどこからでもアクセス可能なサービスとの組み合わせ」これは非常にクールです。

先ほど説明したように、Ajax は裏でリクエストを送信し、通常はレスポンスを (またはその一部を) 分割して Web ページに反映させ、Web ページ全体は更新しません。この場合のリクエストは単純な HTTP リクエストにすることも、公開された Web サービスに対して送信される SOAP メッセージにすることもできます。この場合には Ajax ルーチンの JavaScript 側が (同じく SOAP フォーマットの) レスポンスを解析して必要なデータを抽出し、そのデータがアプリケーションに返されてユーザーに表示されます。

実際、これほど単純なのです。

fishinhole.com に対するビジネス要件

fishinhole.com の取締役会は皆さんに対し、fishinhole.com の在庫を他の Web アプリケーションからアクセスしやすくするように要求しています。取締役会は、スポーツ・フィッシングのフォーラムやブログ、さらには競合する釣具の小売業者まで含めた他の Web サイトが fishinhole.com の在庫リストに容易にアクセスできるなら、売り上げを 23.7% 増加できると考えています。

彼らがどのようにして 23.7% という数字を算出するに至ったのかを考えたい衝動に耐え、皆さんはどうすれば他の Web アプリケーションに在庫を公開できるかに焦点を絞ることにします。するとすぐに、Web サービスを作成する必要がある、という結論に達します。Web サービスを利用することで、呼び出し側である利用者はルアーのタイプを基に、ルアーの在庫に対するリクエストを送信することができます。現在入手可能なルアーのタイプは、CastingTrollingOther です。この Web サービスは、利用者が指定したルアーのタイプを基に、ルアーのリストを返します。

また皆さんは、現在、Other タイプのルアーが在庫切れであることにも気付きます。この問題を Web サービスの中で適切に処理しなければなりません。

単純な Web サービスをデプロイする

ここでは PHP を使って単純な Web サービスを作成します。PHP と NuSOAP を組み合わせる方法は、素早く Web サービスを作成する方法として私が今まで見た中で、最も容易な方法の 1 つです。

まず NuSOAP を入手し (「参考文献」を参照)、PHP ファイルをすべて、PHP による Web サービスがデプロイされるディレクトリーと同じディレクトリーに配置します。

NuSOAP をインストールしたら、実際の Web サービスの作成を開始します。リスト 1 には Web サービスのすべてが含まれています。

リスト 1. webservice.php
<?php
require_once('nusoap.php');
$server = new soap_server;
$server->register('hello');
$server->register('retrieveByType');
function hello($name) {
    return 'Hello, ' . $name;
}

function retrieveByType($type) {
	if ($type == 'trolling') {
		$arr[0] = 'Donzai Deep Swimmer 5 1/4 inch';
		$arr[1] = 'Yosubi Squid-like 4 inch';
		$arr[2] = 'Fortunata Imperial High Action';
	} else if ($type == 'casting') {
		$arr[0] = 'Silver Spring Mirrors Size 00';
		$arr[1] = 'Gold Spring Mirrors Size 0';
		$arr[2] = 'Mini Minnow Blue';
	} else {
		$arr[0] = 'None found!';
	}

	return $arr;
}

$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>

まず、require_once('nusoap.php') という行に注目してください。この行によって、PHP の Web ページは nusoap.php の中で定義されたクラスを使えるようになります。NuSOAP と関連付けられた他の PHP ファイル群がなぜ必要なのか、疑問に思う人がいるかもしれません。実は、このページが nusoap.php に依存するのと同じように、nusoap.php はこれらのファイルに依存するのです。

その次の行は soap_server オブジェクトをインスタンス化します。当然ですが、soap_server オブジェクトをインスタンス化することで、SOAP プロトコルを使う Web サービスを作成できるようになります。

その次の行は retrieveByType 関数を登録し、この関数が Web サービスの操作として公開されるようにします。このコードのさらに下の方を見ると、retrieveByType という関数を定義していることがわかります。ではなぜ、この登録ステップが必要なのでしょう。この関数を登録しない場合には、この単純な PHP 関数は、この PHP ページでしか利用できないか、もしくはこの関数を含む他の PHP ページでしか利用できないからです。つまりこの行は soap_server オブジェクトに対し、この関数を 1 つの操作として公開し、Web サービスの利用者から利用できるようにすることを指示しています。

次のコード・ブロックは retrieveByType メソッドを実際に実装しています。この retrieveByType は単純な PHP 関数であり、type という 1 つの引数を取ります。typetrollingcastingother のいずれかです。おわかりのように、これらは fishinhole.com の顧客が入手できる 3 つのタイプのルアーです。

retrieveByType メソッドは配列を返します。この配列は、要求された特定タイプのルアーのリストで構成されています。現在、trolling 用には 3 つ、casting 用にも 3 つの異なるルアー・タイプがあります。Other や認識できないすべてのルアー・タイプを含む「その他すべて」があることに注意してください。そうしたタイプに対し、この Web サービスは配列の唯一の要素として単純に「None Found! (何も見つかりません)」を返します。

最後の 2 行は、Web サービスがアクセスされると実行されます。この最初の行は、POST されたデータがあるかどうかをチェックします。ない場合には、この行によって POST データは空のストリングに設定されます。2 行目は POST のデータを使って Web サービスを実行します。POST データには SOAP メッセージが含まれています。この詳細は利用者を検証する際に説明します。

このページを webservice.php として保存し、NuSOAP をインストールしたディレクトリーと同じディレクトリーに配置します。当然ですが、PHP ファイルを処理できる場所にこのページを配置する必要があります。最近では大部分のホスティング・ソリューションは PHP をサポートしているので、PHP プロセッサーを持っていなくても PHP の処理環境を探すために苦労することはないはずです。

では、次の URL (http://yourhost/yourdirectory/webservice.php) にアクセスして、この Web サービスの簡単なテストをしてみます。当然ですが、yourhostyourdirectory を、実際に PHP ファイルを配置したホストとディレクトリーで置き換える必要があります。

得られたレスポンスは SOAP レスポンスのはずです (リスト 2)。そうでない場合には、Web サービスが適切に動作していないことになります。

リスト 2. SOAP レスポンス
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<SOAP-ENV:Envelope 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> 
<SOAP-ENV:Body> 
<SOAP-ENV:Fault> 
<faultcode xsi:type="xsd:string">SOAP-ENV:Client</faultcode> 
<faultactor xsi:type="xsd:string" /> 
<faultstring xsi:type="xsd:string">method '' not defined in service</faultstring> 
<detail xsi:type="xsd:string" /> 
</SOAP-ENV:Fault> 
</SOAP-ENV:Body> 
</SOAP-ENV:Envelope>

Web サービスにアクセスするページを作成する

まず、実際に Web サービスにアクセスする JavaScript コードから始めます (リスト 3)。

リスト 3. JavaScript 関数 invokeService()
function invokeService(type) {
 soapMessage = '<?xml version="1.0" encoding="ISO-8859-1"?>';
 soapMessage+='<SOAP-ENV:Envelope SOAP-ENV:encodingStyle=""';
 soapMessage+=' xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"';
 soapMessage+=' xmlns:xsd="http://www.w3.org/2001/XMLSchema"';
 soapMessage+=' xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">';
 soapMessage+=' <SOAP-ENV:Body> <ns1:retrieveByType xmlns:ns1="http://fishinhole.com">';
 soapMessage+=' <type xsi:type="xsd:string">' + type + '</type>';
 soapMessage+=' </ns1:retrieveByType> </SOAP-ENV:Body> </SOAP-ENV:Envelope>';
 
 if(window.XMLHttpRequest) {
  httpRequest=new XMLHttpRequest();
 } else if (window.ActiveXObject) { 
  httpRequest=new ActiveXObject("Microsoft.XMLHTTP"); 
 }
                  
 httpRequest.open("POST",url,true);
 if (httpRequest.overrideMimeType) { 
  httpRequest.overrideMimeType("text/xml"); 
 }
 httpRequest.onreadystatechange=populateDiv;
      
 httpRequest.setRequestHeader("Man", url + " HTTP/1.1")
 httpRequest.setRequestHeader("MessageType", "CALL");
 httpRequest.setRequestHeader("Content-Type", "text/xml");

 httpRequest.send(soapMessage);
 valTimeout=setTimeout("timeout(httpRequest);",120000);
}

この invokeService という名前の関数は、type という 1 つのパラメーターを受け付けます。言うまでもありませんが、この type パラメーターは、Web サービス操作 (retrieveByType) で受け付けられる type パラメーターに対応します。つまり type パラメーターは、castingtrollingother のいずれかで構成されるストリングです。

この関数の最初の数行で SOAP メッセージを組み立てます。この記事では SOAP の概要を延々と説明することはしません。ただし、この XML の一部は非常に直感的です。XML 要素の 1 つが操作の名前 (retrieveByType) に直接対応していることに注意してください。この要素の子要素の名前は、webservice.php ファイルの中で指定されたパラメーター (type) の名前に従って付けられています。そしてこの要素の値はストリング・パラメーターであり、この JavaScript 関数に渡され、同じく type という名前が付けられています。

次の数行では、ブラウザーの種類に依存しないリクエスト・オブジェクトを作成します。このオブジェクトが Web サービスへのアクセスに使われます。

リクエスト・オブジェクトを作成した後、この invokeService() 関数はコールバック関数を設定します。この例では、コールバック関数は populateDiv() 関数です。この populateDiv() 関数によって、返された在庫リストが Web ページ上に表示されます。

次に、invokeService() 関数はヘッダーを設定します。ここで特に興味深い点は、コンテンツ・タイプを SOAP 準拠の text/xml に設定している点です。また、url 変数が使われていることにも注意してください。皆さんが独自の Web ページを作成する場合、この変数を皆さんの Web サービスが使用する URL に設定する必要があります。この変数は http://www.myhost/myservicedir/webservice.php のようになります。

最後に、リクエスト・オブジェクトを使って SOAP メッセージを送信し、サービスが応答しない場合のためにタイムアウトを設定します。

次に、Ajax 呼び出しに応答して在庫を表示するJavaScript コードを見てみましょう (リスト 4)。

リスト 4. JavaScript 関数 populateDiv()
function populateDiv(){
 try {
      if(httpRequest.readyState==4) {
         if(httpRequest.status==200) {
            clearTimeout(valTimeout);
     	    var text = httpRequest.responseText;
	    if (window.DOMParser) {
		parser=new DOMParser();
		xmlDoc=parser.parseFromString(text,"text/xml");
	    } else {
	        xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async="false";
		xmlDoc.loadXML(text); 
	    } 
	
            var html = "";
	    for (i=0;i<xmlDoc.getElementsByTagName("item").length;i++) {
	     html += "<br/>" + 
             xmlDoc.getElementsByTagName("item")[i].childNodes[0].nodeValue;
	    }

	    var resultDiv=document.getElementById("resultDiv");
	    resultDiv.innerHTML = html;         		
         }
      } 
 } catch(e) { 
     alert("Error!"+e.description); 
 }      
}

最初の数行は、Ajax を使ったことのある人には見慣れたもののはずです。この関数はリクエスト・オブジェクトの状態が変化するたびに呼び出されることを思い出してください。ここで特に興味深い点は、リクエストによって有効なレスポンス (レスポンス・コードは 200) が返される場合です。

有効なレスポンスが返されると、タイムアウトをクリアし、レスポンス・テキストを取得します。レスポンス・テキストが実際には SOAP レスポンスであり、従って XML フォーマットであることを思い出してください。つまり、JavaScript プログラミング言語を使って XML を構文解析し、重要情報を抽出する必要があるということです。

そこで、次の数行に進みます。この数行によって、ブラウザーの種類に依存しない XML 文書オブジェクトがインスタンス化され、JavaScript プログラミング言語で構文解析できるようになります。SOAP レスポンスは XML 文書なので、他の任意の XML 文書とまったく同じように構文解析することができます。

次に、いくつかの HTML を作成します。最初は空の HTML ストリングから始めます。次に、SOAP レスポンスを構文解析して item 要素を見つけます。この Web サービスが配列を返すことを思い出してください。つまり item 要素は複数ある可能性があります。for ループは要するに、「各 item 要素に対し、以下のことを実行しなさい」と言っています。

その「以下のこと」の内容は次のとおりです。JavaScript コードは item 要素の最初の子を取得します。この場合、子は 1 つしかなく、単純なストリングです。次に JavaScript コードはその子の値を抽出しますが、その値は在庫の中の 1 つの品目です。ここでは見た目を良くするために、在庫の品目の前に <br/> タグを追加しています。こうすることで、リストの各品目が 1 行として表示されます。最後に、この行全体が、それまでに作成された HTML に連結され、for ループが完了すると完全なリストが得られます。

HTML が完成したら、今度はこの HTML をページ上に配置します。この場合、resultDiv という名前の div 要素の内容は、上で作成された HTML によって上書きされます。その結果、在庫品目のリストが作成され、ユーザーがページ上のドロップダウン・ボックスから新しいタイプを選択すると、このリストが画面上に表示されます。

HTML の話題のついでに、実際の Web ページに必要な HTML を見てください (リスト 5)。

リスト 5. クライアント用の HTML
<body>    
   <div style="position:relative;left:0px;background-color:blue;margin:0px;">   
   <h2 align="center"><font color="#ffffff">FishinHole.com Web Service</font></h2></div>
   <table align="center" cellpadding="6px" cellspacing="6px" width="400" border="0">
      <tr>
         <td width="80" valign="center"><font color="black">
          Lure Type:</font></td>
         <td>
	  <select name="lureType" id="lureType" onchange="changeTypes()">
	   <option value="">-SELECT-</option>
	   <option value="trolling">Trolling</option>
	   <option value="casting">Casting</option>
	   <option value="other">Other</option>
	  </select>
         </td>
	 <td width="150"> </td>
         <tr>
          <td colspan="3">
            <div id="resultDiv"></div>
          </td>
       </tr> 
    </table>    
</body>

ここには特別に複雑なものはありません。単純なドロップダウン・ボックスがあり、選択肢は trolling、casting、other という 3 つのルアーのタイプです。ここでは onchange 属性を使っています。こうすることで、ユーザーが新しいルアー・タイプを選択すると、Ajax リクエストを呼び出す JavaScript コードが自動的に実行されます。

また、resultDiv という div 要素があることにも注意してください。この要素に在庫リストが表示されます。

テスト

今作成した HTML ページを、HTML コードと JavaScript コードを解釈する任意のプラットフォーム上に配置します。Microsoft® Windows® オペレーティング・システムを使っている場合には、この HTML をローカルのハード・ディスクに配置することができます。

次に、この Web ページにアクセスします。画面の中央には、ドロップダウン・ボックスが表示されているはずです。最初の状態では、-SELECT- が選択品目として表示されているはずなので、選択品目を Trolling に変更します。数秒待つと、3 つの品目の在庫リストが画面上に表示されるはずです。おめでとうございます。テストは成功です。

問題がある場合には、url 変数が適切に設定されているかどうかを確認します。また、JavaScript コードによって例外がレポートされ、その説明がポップアップ・ウィンドウに表示されるかもしれません。その説明を見ることで、何が悪いのか、手がかりを得ることができます。

まとめ

Web サービスはインターネットにアクセスできる人達に操作を公開するための強力な手段です。また Ajax は、ページを更新せずに表示を変更することで、Web アプリケーションのユーザーにリッチなエクスペリエンスを提供するための手段です。この 2 つを組み合わせることで、分散型オブジェクト・アプリケーションをエミュレートし、プロフェッショナルなインターフェースを表示する、強力な Web アプリケーションを作成することができます。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, SOA and web services
ArticleID=495712
ArticleTitle=Web サービスと Ajax とを組み合わせて使う
publish-date=05182010