读书人

关于支持RESTful的思忖

发布时间: 2012-10-24 14:15:58 作者: rapoo

关于支持RESTful的思考
现在基本上所有的MVC框架都叫喧着支持RESTful(http://zh.wikipedia.org/wiki/REST),
最近写的Struts(for)RCP(http://struts4rcp.googlecode.com)也来凑下热闹,
这里讲下基本思路,作个参考。
REST的一些要求,如:
1. 客户端和服务器结构
2. 连接协议具有无状态性
3. 能够利用Cache机制增进性能
4. 层次化的系统
5. Code On Demand - Javascript
通过RCP/RIA和HTTP协议本身就可以达到,就不多说了,
主要关注REST的设计风格,如:
1. 资源是由URI来指定。
2. 对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
3. 通过操作资源的表形来操作资源。
4. 资源的表现形式则是XML或者HTML,取决于是读者是机器还是人,消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。
下面一一论述以上四点的实现:
1. URI数据映射
鉴于RESTful要从URI上取值,并注入到相应属性,这就需要一种方式,来声明哪一截数据应该注入到哪个属性,
在参考了多个RESTful框架后,决定采用常见的"/catalogs/{categoryId}/books/{bookId}"风格声明URI数据提取方式,如:


最终觉得第2种方案比较简单友好, 并且客户端便于创建对等接口, 标注和配置也可以考虑支持。
还有一个问题是,ResourceAction应不应该提供list, find等函数,用于列表和查找,如:

要求除POST以外的方法都具有幂等性(idempotence),
也就是调用这些方法n次与调用1次的结果一致,
比如,你删除一个资源8次,和删除这个资源1次没什么区别,
还要求OPTIONS和TRACE方法无副作用(side effects),
也就是不修改任何状态(包括数据库数据与对象属性),
调用这些方法n次与调用0次结果一致,
按照RESTful的要求,GET方法也应该是无副作用的,
这些与契约式设计(Design By Contract)的“区分命令与查询”原则是一致的。
但框架在这方面没法做太多限制,只能在文档中声明并告知业务开发人员。
3. 客户端操作方式
要保证的语义是:客户端看起来是在操作资源表形本身。
(1) 客户端
首先,客户端需要一个代表资源的接口,它的所有函数都是操作该资源本身,如:

4. 资源多重表述
也就是客户端,可以通过修改"Accept"请求头信息,来要求服务器端返回不同类型的数据结果,如:
Accept: text/json 返回JSON数据
Accept: text/xml 返回XML数据
Accept: text/html 返回HTML页面
Accept: text/wml 返回WML页面
这个可以通过读取根据请求头信息来切换序列化器实现,首先需要留有策略接口,如:
@Path("/users//{id}") // 双斜杠用于分隔集合资源URI和单一资源URIpublic class UserResourceAction extends ResourceAction<User> {// 统计资源列表 (接收集合资源的"HEAD"请求)protected long count(User user) throws Exception {if (user == null)return userService.countAllUser();return userService.countByUser(user);}// 获取资源列表 (接收集合资源的"GET"请求)protected User[] list(User user) throws Exception {if (user == null)return userService.findAllUser().toArray(new User[0]);return userService.findByUser(user).toArray(new User[0]);}// 创建资源 (接收集合资源的"POST"请求)protected void create(User user) throws Exception {userService.saveUser(user);}// 读取资源 (接收单一资源的"GET"请求)protected User read(User user) throws Exception {return userService.loadUser(user.getId());}// 修改资源 (接收单一资源的"PUT"请求)protected void update(User user) throws Exception {userService.updateUser(user);}// 删除资源 (接收单一资源的"DELETE"请求)protected void delete(User user) throws Exception {userService.removeUser(user.getId());}}
2 楼 Beyon_javaeye 2008-12-18 我个人认为用 标注 和 配置文件 灵活性更大些, 比如: get()、find()、read()等都可以标注成@Get, 感觉这样隔离了实现! 3 楼 javatar 2008-12-18 Beyon_javaeye 写道
我个人认为用 标注 和 配置文件 灵活性更大些,  比如: get()、find()、read()等都可以标注成@Get, 感觉这样隔离了实现!

是的,用标注是个好想法,JSR311就是全部用标注实现的,但我觉得REST与RPC的一个重要区别就在于它统一了“动词”,并以“名词”为中心,要的就是不“灵活”(零风格是最灵活的),虽然会对某些特殊功能实现带来麻烦,但对常见功能非常简化,而且我想实现的是MVC框架,而不是REST版的WebService(这是JSR311的目标),将REST作为控制器层实现,所以固定函数名,会比较合理些,并且可以保留对"Action"概念的支持,以处理特殊情况。 4 楼 Jekey 2008-12-19 标记一下,最近比较忙,有空研究一下。 5 楼 DanielYWoo 2009-01-14 目前HP的Symphony SDK和社区的Restlet和Jersey还算是比较好用,Struts和Axis2所宣称的Restful WS都很糟糕.
另外,我觉得Atom和JSON已经是Restful WS的既定事实的必须支持的两种representation了

引用如:@Path({"/users/{id}","/users","/users/add","/users/{id}/edit"})
当然,可以用命名约定来减少路径的个数,问题在于add和edit已经超出(甚至违背了)RESTful风格,

如果是自增主键的话,add可以POST到resources上啊,这是Restful WS推荐的方式也是Atom协议要求的. Edit可以put到resources/{id}上



读书人网 >软件架构设计

热点推荐