基于 REST 的 Web 服务及其基于 Ajax 的客户端
引言
在 Roy Fielding 的论文中,他将 REST 作为目前 Web 体系结构的一种基础概念进行了详细介绍(请参见本文结尾处的参考资料,以获取 Fielding 的论文的链接)。他为 REST 提出了下列标准:
- 为现代 Web 体系结构进行建模的一组约束。
- REST 原则已应用于 HTTP 和 URI 规范。
- 在 HTTP 的发展过程中是可见的。
REST 不是一种协议,而是一种体系结构风格,这是非常重要的区别。
对于 Web 服务,W3C 对 Web 服务的正式定义如下所示:
“Web 服务是由 URI 标识的一个软件系统,并且使用 XML 对它的公共接口和绑定进行定义和描述。其他软件系统可以发现它的定义。然后,这些系统就可以按照 Web 服务预先确定的方式与它进行交互,并使用通过 Internet 协议传输的基于 XML 的消息。”(请参见参考资料以获取有关这个摘录的链接。)
常识告诉我们,Web 服务主要用于计算机与计算机之间的通信,而不是计算机与用户之间的通信。基于 REST 的 Web 服务是使用 REST 体系结构风格创建的 Web 服务,下一个部分中将通过一个示例来说明如何构建基于 REST 的 Web 服务。要掌握这一内容,您首先需要了解 Ajax,这是很重要的。(如果您是 Ajax 方面的新手,那么请参考参考资料以获取一些有价值的信息的链接。)
要创建基于 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 操作。
图 1 中的关系图显示了与一个基于 REST 的 Web 服务之间的交互。
您可以使用 HTTP Servlet 来实现基于 REST 的 Web 服务。本文使用一个虚拟的服务演示了实现的过程,而这个服务提供了有关公司雇员的详细信息。雇员列表资源使用一个逻辑 URI 进行表示,http://localhost:9080/AJAX_REST_Demo/RESTDemoServlet/employee-list。当通过 HTTP GET 调用这个服务时,它将返回如清单 1 中所示的雇员列表。
<?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 中所示的雇员详细信息。
<?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 的服务。
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 客户端。
如前所述,Ajax 表示 Asynchronous JavaScript + XML。它有时也称为 XML HTTP 技术。在 Ajax 中,核心的技术主要是围绕实现与服务器的异步通信,而无需页面刷新。XMLHTTPRequest
对象支持对服务器进行异步 GET、POST、PUT 和 DELETE。这并不向用户显示任何内容,换句话说,不会显示状态消息。您可以为状态更改指定一个处理程序方法,并且当发生如下请求时将通知这个处理程序:
- 初始化
- 启动
- 在返回的过程中
- 完全完成
清单 4 显示了一个基于 Ajax 的 HTML 页面的代码,它可以用作上述基于 REST 的 Web 服务的客户端:
<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 服务。请查看下面的参考资料部分中所提供的有价值的链接。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- Roy Fielding 的论文“Architectural Styles and the Design of Network-based Software Architectures”是有关这个主题的开创性的文章。
- 来自 W3 的 Web services Architecture 为组成 Web 服务的内容提供了很好的概述。
- 查看 developerWorks 的“掌握 Ajax”系列,以获得有关这个主题的全面的概述。访问 Ajax 资源中心。
- 阅读“Building Web services the REST way”以获得本文中所介绍的主题的详细背景知识。
- 查看另一篇基于 REST 的 Web 服务方面的文章。
- IBM® developerWorks 的 SOA and Web services 专区,其中包含数百篇关于如何开发 Web 服务应用程序的文章以及入门级、中级和高级教程,您将大开眼界。
- IBM SOA 网站 提供 SOA 的概述,并介绍 IBM 是如何帮助您实现 SOA 的。
- 了解关于 developerWorks 技术事件和网络广播的最新消息。
- 访问 Safari 书店,浏览有关这些技术主题以及其他方面的书籍。
- 查看 Web 服务按需演示程序。
获得产品和技术
- 使用 IBM 试用软件开发您的下一个项目,可下载或索取 DVD 光盘。
讨论
- 参与论坛讨论。
- 参与 developerWorks Blog ,从而加入到 developerWorks 社区中来。
Shailesh K. Mishra是位于印度古尔加翁的 IBM Software Lab 的一位软件工程师。他目前正在参与“BizPortlets”项目的相关工作,他主要感兴趣的领域是业务集成。