RESTful Web Services in Spring 3(上)
通过本文,我将介绍REST的特点,基本设计原则及其简单讲解,最后给出spring3.0下开发的RESTful Web Services 简单实例,其中许多内容是在网络上摘得,并通过自己理解写上的本人观点的博客,如有不同意见请指正。
?
?
??? REST(Representational State Transfer ),有中文翻译为"具象状态传输"(也有:"代表性状态传输")。是由 Roy Thomas Fielding博士 在2000年就读加州大学欧文分校期间在学术论文中提出的一个术语。他首次系统全面地阐述了REST的架构风格和设计思想。这篇论文是Web发展史上一篇非常重要的技术文献,他也为WEB架构的设计与评判奠定了理论基础。
?
注:附件里有论文的中文版,有兴趣的朋友可以下载看看。
?
??? REST 定义了一组体系架构原则,您可以根据这些,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。所以在事实上,REST 对 Web的影响非常大,由于其使用相当方便,已经普遍地取代了基于 SOAP 和 WSDL 的接口设计。在多年以后的今天,REST的主要框架已经开始雨后春笋般的出现。
??? 个人理解:
(一)? 首先REST只是一种风格,不是一种标准
(二)? REST是以资源为中心的
(三)? REST充分利用或者说极端依赖HTTP协议
?
一.对于今天正在吸引如此多注意力的最纯粹形式的 REST Web 服务,其具体实现应该遵循以下基本设计原则:
1.1.显式地使用不同的 HTTP 请求方法
1.2.无状态
1.3.公开目录结构式的 URI(通过逻辑URI定位资源)。
?
1.1.显式地使用不同的 HTTP 请求方法
??? 我们在 Web 应用中处理来自客户端的请求时,通常只考虑 GET 和 POST 这两种 HTTP 请求方法。实际上,HTTP 还有 HEAD、PUT、DELETE 等请求方法。而在 REST 架构中,用不同的 HTTP 请求方法来处理对资源的 CRUD(创建、读取、更新和删除)操作:
??? 若要在服务器上创建资源,应该使用 POST 方法。?
??? 若要检索某个资源,应该使用 GET 方法。?
??? 若要更改资源状态或对其进行更新,应该使用 PUT 方法。?
??? 若要删除某个资源,应该使用 DELETE 方法。
?
经过这样的一番扩展,我们对一个资源的 CRUD 操作就可以通过同一个 URI 完成了:
[url]http://www.example.com/photo/logo[/url](读取)
仍然保持为 [GET] [url]http://www.example.com/photo/logo[/url]
?
[url]http://www.example.com/photo/logo/create[/url](创建)
改为 [POST] [url]http://www.example.com/photo/logo[/url]
?
[url]http://www.example.com/photo/logo/update[/url](更新)
改为 [PUT] [url]http://www.example.com/photo/logo[/url]
?
[url]http://www.example.com/photo/logo/delete[/url](删除)
改为 [DELETE] [url]http://www.example.com/photo/logo[/url]
?
从而进一步规范了资源标识的使用。
通过 REST 架构,Web 应用程序可以用一致的接口(URI)暴露资源给外部世界,并对资源提供语义一致的操作服务。这对于以资源为中心的 Web 应用来说非常重要。
?
1.2.无状态
?
在 REST 的定义中,一个 Web 应用总是使用固定的 URI 向外部世界呈现一个资源。
它认为Web是由一系列的抽象资源组成,这些抽象的资源具有不同的具体表现形式。
譬如,定义一个资源为photo,含义是照片,它的表现形式可以是一个图片,也可以是一个.xml的文件,其中包含一些描述该照片的元素,或是一个html文件。 并且这些具体的表现可以分布在不同的物理位置上。
1.3.通过逻辑URI定位资源
实现这种级别的可用性的方法之一是定义目录结构式的 URI。
此类 URI 具有层次结构,其根为单个路径,从根开始分支的是公开服务的主要方面的子路径。 根据此定义,URI 并不只是斜杠分隔的字符串,而是具有在节点上连接在一起的下级和上级分支的树。 例如,在一个收集photo的相册中,您可能定义类似如下的结构化 URI 集合:
?
http://www.example.com/photo/topics/{topic}
?
如:http://www.example.com/photo/topics/home
?
根 / photo之下有一个 /topics 节点。 该节点之下有一系列主题名称,例如生日照片,聚会照片等等,每个主题名称指向某个讨论线。 在此结构中,只需在 {topic}输入某个内容即可容易地收集讨论线程。
在某些情况下,指向资源的路径尤其适合于目录式结构。 例如,以按日期进行组织的资源为例,这种资源非常适合于使用层次结构语法。
此示例非常直观,因为它基于规则:
?
http://www.example.com/photo/2010/02/22/{topic}
?
第一个路径片段是四个数字的年份,第二个路径片断是两个数字的月份,第三个片段是两个数字的日期。这就是我们追求的简单级别。 在语法的空隙中填入路径部分就大功告成了,因为存在用于组合 URI 的明确模式:
http://www.example.com/photo/{year}/{day}/{month}/{topic}
从而不需要我们去这样去传递信息:http://www.example.com/photo?year=xxxx&day=xxx$month=xxx&topic=xxxx
二.Restful web service的优点:
?
2.1?HTTP头中可见的统一接口和资源地址
通过对于HTTP Head 的解析,我们便可以了解到当前所请求的资源和请求的方式。
这样做对于一些代理服务器的设置,将带来很高的处理效率。
REST 系统中所有的动作和要访问的资源都可以从HTTP和URI中得到,这使得代理服务器、缓存服务器和网关很好地协调工作。而RPC模型的SOAP 要访问的资源仅从 URI无法得知,要调用的方法也无法从HTTP中得知,它们都隐藏在 SOAP 消息中。
同样的,在REST系统中的代理服务器还可以通过 HTTP 的动作 (GET 、 POST)来进行控制。
2.2?返回一般的XML格式内容
一般情况下,一个RESTful Web Service将比一个传统SOAP RPC Web Service占用更少的传输带宽。
?
这里声明了名字为“article”的Spring DispatcherServlet,并匹配所有“/*” 的“article”servlet,在Spring 3里,当它发现有 “article” servlet时,它会自动在WEB-INF目录下寻找“article”-servlet.xml,我现在贴出article-servlet.xml 内容:
?
?
?
这里它做了几件事情:
?
1.Spring会扫描com.informit.articleservice包或他的子包来作为他的servlet组件
2.声明了一个articleXmlView bean 为了初始化XStreamMarshaller,这个类会把我们接口中得到结果以XML文档形式展现出来?
通过这个配置文档,我们声明我们的类和注释后,spring自己会照顾rest,现在我们看下Spring MVC ArticleController class:
?
?
?
?
ArticleController class 被@Controller注释后,他会自动作为一个Spring MVC controller class,而@RequestMapping annotation(注释)会告诉Spring有关的URI,比如“/article”。“method = RequestMethod.GET”代表以GET方式传递HTTP请求,
?
我们可以通过http://localhost:8080/articleservice/article看下效果。
?
而"/article/{category}/{id}"代表{category}和{id}为传递进来的值作为URI,如通过http://localhost:8080/articleservice/article/kk/22来把相应的值传递进方法中,“@PathVariable String category”即为category赋值,@RequestParam(value = "mode", required = false) String mode即可获得mode参数值,如:http://localhost:8080/articleservice/article/kk/22?mode=jizhong
然后处理逻辑后再ModelAndView中通过“articleXmlView”bean把loadArticle()方法的article对象或loadArticleCategories()方法的list返回
?
?
下面给出业务逻辑类:
?
?
?
?
package com.informit.articleservice.model;import com.thoughtworks.xstream.annotations.XStreamAlias;@XStreamAlias("category")public class Category {private String name;public Category() {}public Category(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}?
?
?
?
他们被@XStreamAlias()注释了,那么他们在XML文档下的显示别名就以其属性名显示,如"title"
至此,服务端配置就完成了,您可以通过连接:
http://localhost:8080/articleservice/article
http://localhost:8080/articleservice/article/fun/1
http://localhost:8080/articleservice/article/fun/1?mode=summary
因为篇幅,下一讲我将专门写客户端调用的工程: RESTful web services using Spring's RestTemplate class?
注:附件里提供源码,有兴趣的朋友下载看吧
?
下篇的文章地址为:http://yangjizhong.iteye.com/admin/blogs/600680
?
?
?
1 楼 随性而舞 2010-03-02 我刚刚走入java领域,关于restful接口的实现,我有个疑问。
javaee6标准JAX-RS 1.1已经发布,我在新开发的项目里,是否有必要使用javaee标准的annotation而不是spring专有的annotation? 如果是,应该如何将javaee与spring集成,这方面的best practice 是什么样的. 2 楼 grandboy 2010-03-02 我一直也没有想明白,为什么Spring不是基于JSR311标准来实现REST, 而是自己搞一套?如果一直用Spring也没有什么关系,但是要想换其他的实现方案的话,就得重新实现。不过幸好,换实现方案的情况还不是太常见,只是在有些项目里会有。还有就是学习成本的问题,像我对JSR311标准已经比较熟悉了,现在还要再学一套。 3 楼 ricoyu 2011-01-26 关于REST讲得很好, 不过请教一下Service层你为什么要抽象出一个接口ArticleService ? 你的ArticleService永远只会对应一个ArticleServiceImpl, 如果你要增加一个业务方法, 你必须同时在两个类里面添加, 这是毫无意义的. Interface Oriented programming并不是这么体现的. 4 楼 萧_瑟 2012-04-06 org.springframework.web.servlet.DispatcherServlet cannot be cast to javax.servlet.Servlet
哎,启动tomcat一直报这个错,网上看了些解决方法还是不行,不知道怎么办了。我没有用你的源代码,pom.xml文件加载的一些包和你的不一样,但不影响,因为你的是旧版的,有些现在是不存在的了,比如:org.springframework.web.servlet 换成了 spring-webmvc
不知道有方法解决没有?