レベル: 中級 Shailesh K. Mishra (shailekm@in.ibm.com), Software Engineer, IBM
2007年 7月 05日 RESTful な Web サービスとは、REST (REpresentational State Transfer) アーキテクチャー・スタイルを用いてビルドされたWeb サービスのことです。この記事では、単純なプロキシー・サーブレットと Ajax (Asynchronous XML + JavaScript) ベースのクライアントを使って RESTful な Web サービスを作成する 1 つの方法を紹介します。
はじめに
Roy Fielding は彼の論文のなかで、REST を今日の Web アーキテクチャーの基礎となるコンセプトだと述べています (Fielding
の論文へのリンクは、記事の終わりに記載した「参考文献」を参照してください)。彼は REST の基準を以下のように設定しました。
- 最新の Web アーキテクチャーをモデル化するための一連の制約である
- HTTP および URI 仕様には、REST の原則が適用されている
- HTTP の進化のなかで目に見えて明らかである
REST はプロトコルではなく、むしろアーキテクチャー・スタイルであり、この点が重要な違いとなっています。
Web サービスに関しては、W3C では正式見解として以下のように Web サービスを定義しています。
「Web サービスは URI によって識別されるソフトウェア・システムで、その公開インターフェースとバインディングは XML を使用して定義および記述されます。XML による定義は、他のソフトウェア・システムを使って見つけ出すことが可能です。他のシステムが定義を見つけると、インターネット・プロトコルで送信されたXML ベースのメッセージを使用して、その定義で規定された方法で Web サービスと対話動作できるようになります」(この文を引用した原文へのリンクは、「参考文献」に記載しています)。
 | |
Ajax Resource Centerにアクセスしてください。ここには記事、チュートリアル、ディスカッション・フォーラム、ブログ、ウィキ、イベント、そしてニュースなど、Ajax プログラミング・モデルに関する情報が豊富に用意されており、ワンストップ・ショップになっています。新しい情報もここに記載されます。
|
|
常識からすると、Web サービスは、マシンとユーザー間の通信ではなくマシン間の通信を行うための手段だということがわかります。RESTful な
Web サービスは、REST アーキテクチャー・スタイルに従ってビルドされた Web サービスです。次のセクションでは、一例を用いて RESTful
な Web サービスをビルドする方法を説明します。この説明を理解するには、Ajax を理解していることが肝心です (Ajax が初めてという読者は、「参考文献」に記載した参考情報へのリンクを参照してください)。
RESTful な Web サービスを作成する
RESTful な Web サービスを作成するには、まず、Web サービスとして公開したいすべてのリソースを識別する必要があります。リソースの例としては、従業員リスト、従業員詳細、注文書などがあります。REST
では、1 つひとつのリソースが一意の URI (Uniform Resource Identifier) で識別されます。つまり、すべてのリソースにそれぞれ一意の
URI を決定しなければなりません。例えば従業員リストを識別するには http://www.employee-details.com/employees-list
のような URI を使用し、従業員詳細は http://www.employee-details.com/employees/01234 といった
URI で表すことができます。
リソースを取得および変更するために使用する HTTP 操作は、GET、PUT、POST、および DELETE です。追加 (および関連) 情報を許可するには、リソース表現にハイパーリンクを提供します。リソースのデータに対するリクエストおよびレスポンスを行うためのフォーマットを指定してください。そのために必要な操作は、PUT
および POST です。
図 1 に、RESTful な Web サービスとの対話を図解します。
図 1. RESTful な Web サービスとの対話
RESTful な Web サービスを実装する
RESTful な Web サービスは、HTTP サーブレットを使って実装することができます。この記事では、ある会社の従業員詳細を表示するダミー・サービスを使用して、実装方法を説明します。従業員リストのリソースは、論理
URI、http://localhost:9080/AJAX_REST_Demo/RESTDemoServlet/employee-list
で表します。このサービスを HTTP GET で呼び出すと、リスト 1 に示す従業員のリストが返されます。
リスト 1. HTTP GET で呼び出された従業員リスト
<?xml version='1.0' encoding='UTF-8'?>
<p:Employees xmlns:p='http://www.employee-details.com'>
<Employee id='00345' href='/employees/00345'/>
<Employee id='00346' href='/employees/00346'/>
<Employee id='00347' href='/employees/00347'/>
<Employee id='00348' href='/employees/00348'/>
</p:Employees>
|
同様に、従業員詳細を表すには http://localhost:9080/AJAX_REST_Demo/RESTDemoServlet/employee/0124
などの論理 URI を使用します。このサービスを HTTP GET で呼び出した場合には、リスト 2 の従業員詳細が返されます。
リスト 2. HTTP GET で呼び出された従業員詳細
<?xml version='1.0' encoding='UTF-8'?>
< EmpDetail xmlns:p='http://www.employee-details.com'>
<<Emp-ID>00345</Emp-ID>
<Name>David Henry</Name>
<Department>Finance</ Department >
</p:EmpDetail>
|
リスト 3 は、サーブレット・コードです。このリストではすべてをハードコーディングしていますが、データベースを操作するために拡張し、リアルタイムの
RESTful なサービスにすることもわけありません。
リスト 3. サーブレット・コード
public class RESTDemoServlet extends HttpServlet implements Servlet {
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#HttpServlet()
*/
Map map =new HashMap();
/* (non-Javadoc)
* @see javax.servlet.GenericServlet#init()
*/
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
Employee emp0 =new Employee("David","Finance");
Employee emp1 =new Employee("Smith","HealthCare");
Employee emp2 =new Employee("Adam","Information technology");
Employee emp3 =new Employee("Stephan","Life Sciences");
map.put("00345",emp0);
map.put("00346",emp1);
map.put("00347",emp2);
map.put("00348",emp3);
}
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#doGet
(HttpServletRequest arg0, HttpServletResponse arg1)
*/
protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
arg1.setContentType("text/xml");
PrintWriter out=arg1.getWriter();
System.out.println(map);
if(arg0.getPathInfo()!= null){
String EmpId=arg0.getPathInfo().substring(1,arg0.getPathInfo().length());
System.out.println(EmpId);
out.write("<?xml version='1.0' encoding='UTF-8'? >"+"\n");
out.write("<p:EmpDetail xmlns:p='http://www.employee-details.com' >"+"\n");
out.write("<Emp-ID>"+EmpId+" </Emp-ID >"+"\n");
out.write("<Name>"+((Employee)map.get(EmpId)).name+" </Name >"+"\n");
out.write("<Department >"+((Employee)map.get(EmpId)).dept+" </Department >"+"\n");
out.write("</p:EmpDetail >"+"\n");
out.flush();
}else{
out.write("<?xml version='1.0' encoding='UTF-8'? >"+"\n");
out.write("<p:Employees xmlns:p='http://www.employee-details.com' >"+"\n");
out.write("<Employee id='00345' href='http://localhost:9080/
AJAX_REST_Demo/RESTDemoServlet/employees/00345'/ >"+"\n");
out.write("<Employee id='00346' href='http://localhost:9080/
AJAX_REST_Demo/RESTDemoServlet/employees/00346'/ >"+"\n");
out.write("<Employee id='00347' href='http://localhost:9080/
AJAX_REST_Demo/RESTDemoServlet/employees/00347'/ >"+"\n");
out.write("<Employee id='00348' href='http://localhost:9080/
AJAX_REST_Demo/RESTDemoServlet/employees/00348'/ >"+"\n");
out.write("</p:Employees >");
out.flush();
}
}
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#doPost
(HttpServletRequest arg0, HttpServletResponse arg1)
*/
protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
|
次のセクションでは、この RESTful な Web サービスのための Ajax クライアントを作成する方法を説明します。
RESTful な Web サービスの Ajax クライアントを作成する
前述のとおり、Ajax とは Asynchronous JavaScript + XML の略で、XML HTTP 手法と呼ばれることもあります。Ajax
の中核となる手法が重点を置いているのは、ページの更新を行わないサーバーとの非同期通信です。XMLHTTPRequest オブジェクトにより、サーバーに対する GET、 POST、PUT、DELETE 操作を非同期で行うことが可能になります。この場合、ユーザーに対しては何も表示されません。つまり、ステータス・メッセージは表示されないということです。ただし状態変更に対するハンドラー・メソッドを指定すると、指定されたハンドラーにはリクエストの以下の状態が通知されます。
リスト 4 に、Ajax をベースとした HTML ページのコードを示します。このページは、上記の RESTful な Web サービスに対してクライアントとして機能します。
リスト 4. Ajax ベースの HTML ページのコード
<SCRIPT language="javascript" type="text/javascript" >
var req=null;
//This function initializes XHR
function initXHR() {
if (navigator.appName.indexOf("Microsoft") > -1 ) {
try{
req=new ActiveXObject("Microsoft.XMLHTTP");
}catch(e1){
alert("failed to create XHR in IE");
}
}else{
try{
req=new XMLHttpRequest();
}catch(error){
alert("failed to create XHR in FireFox");
}
}
}
//get an employee detail
function getEmpDetails(Empurl){
initXHR();
req.open("GET",Empurl, true);
req.onreadystatechange=handleEmpDetailResponse;
req.send(null);
}
//get employee list
function getEmployeeList(listurl){
initXHR();
req.open("GET", listurl, true);
req.onreadystatechange=handleEmpListResponse;
req.send(null);
}
function handleEmpDetailResponse(){
//if Response is complete
if(req.readyState==4){
//response is OK
if(req.status==200){
var str="";
var response=req.responseXML;
var root=response.documentElement;
for(i=0;i <root.childNodes.length;i++){
if(root.childNodes[i].nodeType != 1) continue;
var name=root.childNodes[i].nodeName;
var value=root.childNodes[i].firstChild.nodeValue;
str=str+name+"--- >"+value+" <br >";
}
document.getElementById("emp-div").style.display="";
document.getElementById("emp-detail-div").innerHTML=str;
}else{
document.getElementById("messageDiv").innerHTML=" <SPAN style='color:#FF0000;
font-size:12pt; text-decoration:none;' <Invalid URL or PartId </SPAN >";
}
req.abort();
}
}
function handleEmpListResponse(){
//if Response is complete
if(req.readyState==4){
//response is OK
if(req.status==200){
var pstr="";
var response=req.responseXML;
var root=response.documentElement;
for(i=0;i <root.childNodes.length;i++){
if(root.childNodes[i].nodeType != 1) continue;
var id=root.childNodes[i].getAttribute("id");
var href=root.childNodes[i].getAttribute("href");
pstr=pstr+"EmpId"+"--- >"+id+" <input type='button' value='
GetEmpDetails' onclick="+'"'+"getEmpDetails('"+href+"')"+'"'+">"+" <br >";
}
document.getElementById("emp-list-div").style.display="";
document.getElementById("emp-list").innerHTML=pstr;
}else{
document.getElementById("messageDiv").innerHTML=" <SPAN style='color:#FF0000;
font-size:12pt; text-decoration:none;' >Invalid Employee ID. </SPAN >";
}
}
}
</SCRIPT >
<center >
<input type="button" value="getEmployee-List" onclick="getEmployeeList
'http://localhost:9080/AJAX_REST_Demo/RESTDemoServlet/employee-list')" > <br > <br >
<div id="messageDiv" > </div >
<div id="emp-list-div" style='color:#FF0000; font-size:12pt; text-decoration:none;
display:none;' >Employee List : </div > <br >
<div id="emp-list" > </div > <br > <br >
<div id="emp-div" style='color:#FF0000; font-size:12pt; text-decoration:none;
display:none;' >Selected Employee Detail : </div > <br >
<div id="emp-detail-div" > </div >
</center >
|
リスト4 では、ユーザーが getEmployee-List ボタンをクリックすると、XML HTTP リクエストがサーバーに送信されます。readyState の変更に対しては、ハンドラー関数 handleEmpListResponse を XML HTTP リクエストに指定し、サーバーがレスポンスを完了して (readyState = 4) レスポンスが OK であれば、XML を解析してページの文書オブジェクト・モデル (DOM) に付加し、従業員リストを表示することになっています。同様に、ユーザーがGetEmpDetails ボタンをクリックすると、ハンドラー関数 handleEmpDetailResponse がサーバーからの XML レスポンスを処理し、ページの DOM を変更して特定の従業員の詳細を表示します。
まとめ
この記事では、サーブレットと Ajax ベースのクライアントを使用して RESTful な Web サービスを作成する方法を説明しました。この記事を読んで、RESTful
な Web サービスは理解しやすく、実装するのも簡単だとわかっていただければ本望です。以下の「参考文献」セクションに記載した有益なリンクにも是非アクセスしてください。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Shailesh K. Mishra は、インド、グルガオン所在の IBM ソフトウェア研究所のソフトウェア・エンジニアです。現在、「BizPortlets」プロジェクトに従事しており、ビジネス・インテグレーションを専門分野としています。 |
記事の評価
|