深入理解 RESTful Api 架构
不要以为 RESTful Api 就是设计得像便于 SEO 的伪静态,例如一篇博客资源 http://mengkang.net/blog/1 ,我们可以通过浏览器访问该 URL 而读取文章,但是这并不代表着它就是 RESTful Api 。
也不要认为URL 里有 queryString 就不是 RESTful Api ,例如 http://mengkang.net/blogs/?page=10&page_size=30
RESTful Api 与 SOAP Web API 在 URL 形式上的对比
从设计一个删除评论的 api 说起
我们可以这样设计成类似于:
http://api.mengkang.net/?method=comment.del&id=x ①
http://mengkang.net/comment/del/id/x ②
或者其他形式的 url。
而 RESTful Api 则是:
[DELETE] http://mengkang.net/comments/1 ③
我们对比可以发现①和② URL 中,都有del
的动作指示。
SOAP Web API采用RPC风格,它采用面向功能的架构,所以我们在设计SOAP Web API的时候首相考虑的是应高提供怎样的功能(或者操作)。
而 RESTful Api 是面向资源的架构。是查询、新增、修改、删除,都该资源无关。
所以我们在③ URL 中没有看到del
的关键字,对比②和③最为明显。
进一步认识 RESTful Api
我们知道 URL 全称为“统一资源定位符(Uniform Resource Locator)”,用于描述 Web 资源所在的位置。RESTful Api 是以 HTTP 协议为强烈依托的,将类似于①和②这种以功能为主导的URL风格舍弃,还原 URL 的本质,它的宗旨就是一个 URL 就应该是一个资源,不能包含任何动作。
[POST] http://mengkang.net/users // 新增 [GET] http://mengkang.net/users/1 // 查询 [PATCH] http://mengkang.net/users/1 // 更新 [PUT] http://mengkang.net/users/1 // 覆盖,全部更新 [DELETE] http://mengkang.net/users/1 // 删除
PUT
和PATCH
的功能都可以代表更新,但略有不同,PUT
大多时候表示更新该资源的全部信息,而PATCH
则更新部分信息。
PUT
和POST
又一些功能的重叠,都可以是新建一个资源,POST
时,新建资源的地址是由服务器返回给客户端的。也就是说客户端在发送POST
请求资源之前还无法预知该资源的地址,这在我们的 Api 开发中非常常见,新建一个帖子,新建一条评论,都如此。
而资源的地址是客户端预先知道的情况则比较少。也有人如此设计而使用PUT
,比如需要对 github 上的某人的项目 star ,则可能会设计成:http://github.com/xxx/zhoumengkang/projectname/star 这里把“对这个项目已经点赞过”看成了一个资源,那么就可以用PUT
,因为要删除这个资源时,还是访问这个 URL。
关于这些功能上有交集的地方,可能后面会有一些更加标准的规范或者协议吧。
最后说下HEAD
和OPTIONS
,HEAD
请求的是资源的元数据,比如一张照片,的元数据则可能包含了,照片拍摄的设备,地点,时间等。服务器一般将对应资源的元数据置于响应的报头集合返回给客户端,这样的响应一般不具有主体部分。OPTIONS
则是发送一种“探测”请求以确定针对某个目标地址的请求必须具有怎样的约束(比如应该采用怎样的HTTP方法以及自定义的请求报头),然后根据其约束发送真正的请求。
关于是过滤筛选,排序和 token 等 query string 是支持使用,和唯一资源的概念不冲突。
Server 端代码的基本设计思想
按照 RESTful Api 的规范,严格来说根据endpoint
找到在 server 端映射成一个资源对象,例如通过 http://mengkang.net/users/1 找到UserResource
对象
而在这个对象里对应着该资源支持的 Http 协议的方法。如果一个对象支持7种方式的请求,则类似于:
public class UserResource { private User user; public UserResource() { } public UserResource(int id) { //从数据查询处该用户的数据 String name = "xxx"; short age = 23; user = new User(id,name,age); } /** * 获取单个用户 * 从 new UserResource(int) 开始 * @return */ public User get(){ return user; } /** * 覆盖用户的全部信息 * 从 new UserResource(int) 开始 * @return */ public boolean put(){ return true; } /** * 只更新用户的部分信息 * 从 new UserResource(int) 开始 * @return */ public boolean patch(){ return true; } /** * 创建一个 UserResource * @return */ public int post(){ return 1; } /** * 删除用户 * @return */ public boolean delete(){ return true; } }
如果有关注的 api 那么对于该用户的 star 操作则应新建一个UserStarResource
资源对象,因为每个资源最多只有这7中方法,构造方法除外,而不能有其他的相关的动作方法。
假如我们规定 URL 为 http://mengkang.net/user/1/star 表示是访问者对 id 为 1 的这个用户的 star 状态的一个资源。(当前访问者的信息通过 query string 传递 auth token 的形式获取)
public class UserStarResource { public boolean get(){ return true; } public boolean post(){ return true; } public boolean delete(){ return true; } }
一个基于 netty 的轻量级的高性能的 RESTful Api Server https://github.com/zhoumengkang/netty-restful-server
更多更详细的信息可以阅读下面两篇文章