引言
在 Roy Fielding 的論文中,他將 REST 作為目前 Web 體繫結構的一種基礎概念進行了詳細介紹。他為 REST 提出了下列標準:
1、為現代 Web 體繫結構進行建模的一組約束。
2、REST 原則已應用於 HTTP 和 URI 規範。
3、在 HTTP 的發展過程中是可見的。
REST 不是一種協議,而是一種體繫結構風格,這是非常重要的區別。
對於 Web 服務,W3C 對 Web 服務的正式定義如下所示:
“Web 服務是由 URI 標識的一個軟體系統,並且使用 XML 對它的公用介面和綁定進行定義和描述。其他軟體系統可以發現它的定義。然後,這些系統就可以按照 Web 服務預先確定的方式與它進行互動,並使用通過 網際網路通訊協定 (IP)傳輸的基於 XML 的訊息。”
常識告訴我們,Web 服務主要用於電腦與電腦之間的通訊,而不是電腦與使用者之間的通訊。基於 REST 的 Web 服務是使用 REST 體繫結構風格建立的 Web 服務,下一個部分中將通過一個樣本來說明如何構建基於 REST 的 Web 服務。要掌握這一內容,您首先需要瞭解 Ajax,這是很重要的。(如果您是 Ajax 方面的新手,那麼請參考參考資料以擷取一些有價值的資訊的連結。)
建立基於 REST 的 Web 服務
要建立基於 REST 的 Web 服務,您首先需要確定希望作為 Web 服務進行公開的所有資源。一些資源的樣本包括僱員列表、僱員詳細資料、訂購單,等等。在 REST 中,每種資源都是通過唯一的統一資源識別項(Uniform Resource Identifier,URI)來標識的。您需要為每種資源確定唯一的 URI。例如,僱員列表可以標識如下:http://www.employee-details.com/employees-list。僱員詳細資料可 以使用如下所示的 URI 進行標識:http://www.employee-details.com/employees/01234。
使用 HTTP 操作 GET、PUT、POST 和 DELETE 以檢索和修改您的資源。在您的資源表示中提供一些超連結,以提供更多的相關資訊。為這些資源的請求和響應資料指定格式,這需要 PUT 和 POST 操作。
實現基於 REST 的 Web 服務
您可以使用 HTTP Servlet 來實現基於 REST 的 Web 服務。本文使用一個虛擬服務示範了實現的過程,而這個服務提供了有關公司僱員的詳細資料。僱員列表資源使用一個邏輯 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> |
類似地,僱員詳細資料可以使用一個邏輯 URI 進行表示,如 http://localhost:9080/AJAX_REST_Demo/RESTDemoServlet/employee/0124。當通過 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 顯示了這個 Servlet 的代碼。其中,所有操作都採用了寫入程式碼方式,但是可以很容易地將其擴充為與資料庫進行互動,以便成為一個即時的、基於 REST 的服務。
清單 3. Servlet 代碼
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 } } |
在下一個部分中,瞭解如何為這個基於 REST 的 Web 服務編寫 Ajax 用戶端。
為基於 REST 的 Web 服務編寫 Ajax 用戶端
如前所述,Ajax 表示 Asynchronous JavaScript + XML。它有時也稱為 XML HTTP 技術。在 Ajax 中,核心的技術主要是圍繞實現與伺服器的非同步通訊,而無需頁面重新整理。XMLHTTPRequest 對象支援對伺服器進行非同步 GET、POST、PUT 和 DELETE。這並不向使用者顯示任何內容,換句話說,不會顯示狀態訊息。您可以為狀態更改指定一個處理常式方法,並且當發生如下請求時將通知這個處理程 序:
初始化
啟動
在返回的過程中
完全完成
清單 4 顯示了一個基於 Ajax 的 HTML 頁面的代碼,它可以用作上述基於 REST 的 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 要求。為 XML HTTP 要求指定使用處理常式函數 handleEmpListResponse 來處理 readyState 更改。當伺服器完成了響應(readyState = 4)並且該響應為 OK 時,您可以解析 XML 並將其添加到頁面的文件物件模型(Document Object Model,DOM)後面,以顯示僱員列表。類似地,當使用者單擊 GetEmpDetails 按鈕時,處理常式函數 handleEmpDetailResponse 將處理來自伺服器的 XML 響應,並修改頁面的 DOM 以顯示特定僱員的詳細資料。
結束語
在本文中,您瞭解了如何使用 Servlet 和基於 Ajax 的用戶端來編寫基於 REST 的 Web 服務。希望您能夠發現,可以很容易地理解和實現基於 REST 的 Web 服務。請查看下面的參考資料部分中所提供的有價值的連結。