RestAPI的实现
转自:http://blog.csdn.net/yanical/article/details/7856670
Rest的作者认为计算机发展到现在,最大的成就不是企业应用,而是web,是漫漫无边的互联网web世界。Web能有这么大的成就,它值得我们研究。所以Rest的作者仔细研究了Web,按照Web的世界一些关键特性,提出了我们在实现企业应用的时候应该遵循的一种风格,就是Restful。
Rest风格的API可以给我们很多好处,比如:简洁,统一,性能,可扩展性等等。可惜的是,在实现Rest的时候,总有一些Rest的关键特性没有实现,比如,无状态性,这在我做过的两个项目和我知道的另外一个项目都存在。事实上要实现无状态性在Java里不是那么容易,因为那意味着要把servlet的session抛弃了。除此之外,Rest的一些其他特性在各个项目中实现的也是各有不同。
接下来,我会列出一些我认为的,要实现Rest风格API的关键步骤:
1. 所有东西都是资源(Resource)
所有要给API操作的对象都只能是资源。不管实际上存在的,还是抽象上的。所有资源都会有一个不变的标识(ID),对资源的任何API操作都不应该改变资源的标识。资源和其他资源会有关系,资源与资源的关系通过资源的标识来引用。对资源的操作都应该是完整的,比如获取资源拿到的应该是一个完整的资源对象(根据企业引用特点有些例外,后面会提到)。
事实上,上面的这些完完全全是按照互联网的特性提出来的。互联网中,一个URL就是一个资源;资源的内容就是HTML页面;不管怎么改HTML内容,URL都不会改变;资源之间通过HTML里的连接联系起来;每次获取的时候,获取到的都是完整的HTML内容。
假设有一个博客系统,那么其中的资源可以包括:博主,每个博主都是一个资源;博客,每篇博客都是一个资源,博客和博主之间有联系,通过ID联系起来;每篇博客都会有回复,回复也算是资源,但是它是隶属于博客的,可以认为回复是博客的子资源(你也可以认为博客是博主的子资源,怎么抽象取决于具体的应用,这里不讨论)。
这步通常不难实现,因为这和ORM中的对象概念是类似的,实现上,如果用了hibernate之类的框架,改动也应该很小。
2. 规范对资源的操作,最好只包括CRUD
CRUD指创建(Create),读取(Read), 更新(Update),删除(Delete)
通常对资源的操作只包含CRUD是不可能的,CRUD里甚至连查找的操作的都没有。但这不妨碍我们对Rest的理解,Rest提出的要求是,对资源的操作都应该是统一的,不管是操作哪种类型的资源,API都应该是一致的。这样当调用API的客户端知道某种资源的时候,它不需要去学习对这个资源该怎么操作,因为对所有资源的操作都是一致的,它们都应该支持CRUD操作,以及一些其他操作,比如list(用来查找,或者列出所有资源), merge(部分更新资源,这应该是唯一的不操作资源所有内容的API)。
这和Web也是一样的,HTTP里只有GET,PUT,POST,HEAD等等几个统一的请求(参考:http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)。
要实现简单的几个操作不难,难在这几个简单操作没法支撑整个系统的需求。但是想想吧,互联网也够复杂了吧,还是不是成功了,而且鱼和熊掌不可兼得。有时候服务器端也不一定要实现所有东西,可以把一些逻辑交给客户端去做。比如显示,Rest里显示是完全交给客户端去处理的,服务器只管数据的存储,不管客户端是网页,还是一个手机应用程序,还是另外一个企业应用。各种各样的客户端进来,他们会有各种各样的需求,服务器端不可能一一满足,只能客户端使用统一的API去组合,实现自己的需求。
3. 规范URL的使用
好了,对资源的操作统一了,但是客户端还是要知道怎么触发对资源的某个具体的操作。Rest用URL的规范来保证这种统一性。
创建并保存一个博客:
- POST /blog/save
这个请求需要返回博客的保存后的结果,其中包括博客的标识(ID)。 获取一个已经保存的博客,并更新它:
- GET /blog/get/345
- //更新它
- POST /blog/update/345
这个博客的标识是345。获取博客的某个回复:
- GET /blog/get/345/reply/456
对待子资源,通常的做法就是和这个例子一样,是一级一级的往下找。我们还可以有其他方法,比如remove用来删除,merge用来部分更新,list用来查找。
有三种方式可以将参数传给API操作:
第一种是通过URL的地址传递,如前面的例子中把标识放在URL里;
第二种是通过URL的参数,比如,对于一个查找请求,可以把查找的过滤条件放在参数里:
- GET /blog/list?name=Azure:用InstanceInputEndpoint直接和指定instance通信
第三种是PUT或者POST请求的时候,把内容放在HTTP body里面。这里通常就是博客的内容。
前面我们的例子中有些请求是GET,有些是POST,其实这是有原则的。通常对资源内容没有改变的操作都实用GET,比如获取资源,查找资源;对资源有改变的操作都用POST,比如保存资源。
如果想做的更好,我们应该近一步的使用HTTP的请求方法,直接把HTTP方法和要做的操作映射起来。比如我喜欢认为GET请求就是获取资源(get),PUT方法就是更新整个内容(save,update,我觉得这两个没必要区分),POST方法就是更新部分内容(merge),DELETE方法就是删除资源(remove)。如果这样的话,请求的URL又能简化:
- PUT /blog //创建保存一个新的博客
- GET /blog/345 //获取博客345内容
- PUT /blog/345 //更新博客345
- GET /blog/345/reply/456 //获取博客345的回复456
- POST /blog/345 //更新博客345的部分内容
- DELETE /blog/345 //删除博客345
当然对于list操作,这里就没法满足了,还是需要在URL层面上做些区别。
对于merge操作,有很多人认为是不必要的,Rest不应该提供这个API,但是我觉得在某些情况下很有用。比如某个资源对象,它的内容在不断的扩充,怎么让老的客户端在内容扩充后还能继续使用呢? 如果我们要求所有更新请求都必须把所有内容都放在请求的body中,对于客户端来说就不是那么好做了,但是如果我们允许merge请求,客户端可以可以完全忽略新增加的字段,而只把自己知道的字段放在请求内容中即可。
4. 资源的多重表述
这一步我觉得不是必须的。
Rest里,资源的内容通常直接作为一段JSON或者XML返回给客户端。资源多重表述指的是,一个资源应该能够支持根据客户端的请求,返回相应的格式给客户端。服务器应该按照请求HTTP头中的Accept属性决定返回格式。比如对于Ajax请求,Accept头是application/json,服务端返回JSON格式;对于Android请求,Accept头是application/xhtml+xml,服务器返回XML格式。
我觉得这一步不是必须的因为至少从项目前期来说,我们应该都只会支持一种格式。资源的多重表述给我们一种处理多重请求格式的方式,但是我们不需要一开始就支持它。
5. 进一步合理利用HTTP
前面我们已经应用了HTTP的一些东西,比如请求方法,Accept头。事实上我们可以利用更多。
HTTP支持客户端缓存,在HTTP响应里利用Cache-Control,Expires,Last-Modified三个头字段,我们可以让浏览器缓存资源一段时间。Rest也可以利用这些头,告诉客户端在一定时间内不需要再次请求资源。这对提高性能有很大好处。更多HTTP头信息,可以参考http://en.wikipedia.org/wiki/List_of_HTTP_header_fields。
Rest的请求会出错,HTTP的请求也会出错。我们可以直接利用HTTP的response code来告诉客户端Rest请求出了什么错误。比如500,告诉客户端,服务器出错了;401告诉客户端需要把安全验证信息附上,需要登录系统;404告诉请求的资源不存在,等等。更多HTTP响应码,可以参考http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html。在实际的业务中,HTTP的那些response code肯定是不能满足所有需求的,适当的在response body中加上更详细的错误信息也是必须的。
还有其他很多,总之能利用上的就利用上,不比再次发明轮子。
6. 实现请求的无状态
Rest是无状态的。Rest的请求之间不应该有依赖,在调用一个请求前,不需要一定要去提前调用另外一个请求。Rest里面不应该有session,特别是Rest请求不应该保存信息在sesssion里,以便在后面的调用中使用。甚至包括安全验证,客户端不应该需要提前登录,然后把权限信息保存在session里,后面的请求用同一个session来调用。
实现无状态的方法就是,把所有信息都包含在当前的请求中,包括验证信息。HTTP是无状态的,HTTP里有一个Authorization头,HTTP的要求是在每次请求的时候都把验证信息放在里面,服务器每次处理请求前都去验证这个信息。为了安全,我们可以提供一个生成token的Rest API,客户端调用这个API生成token(可以附上用户名/密码来生成token)。在后面的所有请求中都把这个token放在Authentication头中。
实现无状态最大的好处是能够方便的扩展服务器,也即scalability。否则的话,我们要么把Session绑定到具体服务器上,要么用一个共享的空间存储Session。而实现无状态后,我们可以随意增加,减少服务器数量,都不会对当前用户造成影响。
RestAPI的实现的更多相关文章
- 中国Azure媒体服务RESTAPI的Endpoint
Amber Zhao Thu, Feb 26 2015 4:09 AM 由于海外Azure和中国Azure有不同的domain,很多用户在使用媒体服务RESTAPI时,需要指定中国Azure媒体服务 ...
- Flask框架搭建REST-API服务
一.目的 为了能够将测试工具部署成RESTful-API服务,这样就能通过接口的方式提供统一测试工具服务,使用人员就不用构建application而产生的各种环境问题.使用问题. 适合人群:Pytho ...
- [svc]简单理解什么是rpc调用?跟restapi有何区别?
什么是rpc调用 restapi调用方式是对数据的crud. 常见的我们写flash写个api,或者借助django drf写个标准的resetapi,一个url可以借助httpget post pu ...
- 调用kylin的restAPI接口构建cube
调用kylin的restAPI接口构建cube 参考:http://kylin.apache.org/docs/howto/howto_build_cube_with_restapi.html 1. ...
- geoserver源码学习与扩展——restAPI访问
产生这篇文章的想法是在前端通过js调用restAPI时,总是不成功,发送ajax请求时还总是出现类似跨域的问题,后来查找才发现,默认情况下restAPI的访问都需要管理员权限,而通过ajax请求传输用 ...
- ryu的RESTAPI简介——我主要用于下发和查看流表
一.Rest API简介 REST即表述性状态传递(RepreSentational State Transfer),是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性. 表 ...
- Rancher + K8S RestApi使用
1前言 1.1使用的软件及版本 软件 版本号 Rancher 1.6stable Kubernetes 1.8.3 Docker 1.12.6 1.2 Rancher与K8S的RESTAPI差异 ...
- Project Server 2016 RestAPI调用测试
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...
- restapi(0)- 平台数据维护,写在前面
在云计算的推动下,软件系统发展趋于平台化.云平台系统一般都是分布式的集群系统,采用大数据技术.在这方面akka提供了比较完整的开发技术支持.我在上一个系列有关CQRS的博客中按照实际应用的要求对akk ...
- restapi(1)- 文件上传下载服务
上次对restapi开了个头,设计了一个包括了身份验证和使用权限的restful服务开发框架.这是一个通用框架,开发人员只要直接往里面加新功能就行了.虽然这次的restapi是围绕着数据库表的CRUD ...
随机推荐
- atom插件之less-autocompile
less-autocompile package Auto compile LESS file on save. Add the parameters on the first line of the ...
- ASP.NET——真假分页
所谓分页,就是把所有要显示的内容分成n多页来显示.那为什么要用分页而不直接全部显示呢?这就好比一本书,我们可以用一张纸写完全部书的内容,但实际上并不是这么做的.我们把网页分成一页一页的,其实很大程度上 ...
- 轻松精通awk数组企业问题案例
考试题1:处理以下文件内容,将域名取出并根据域名进行计数排序处理:(百度和sohu面试题) oldboy.log http://www.etiantian.org/index.html http:// ...
- (总结)Nginx 502 Bad Gateway错误触发条件与解决方法
一些运行在Nginx上的网站有时候会出现“502 Bad Gateway”错误,有些时候甚至频繁的出现.以下是从Google搜集整理的一些Nginx 502错误的排查方法,供参考: Nginx 502 ...
- CocoaPods 安装与使用
1.如果之前已经安装过的 gem list --local | grep cocoapods 会看到如下输出: cocoapods (1.1.1)cocoapods-deintegrate (1.0. ...
- 【bzoj1270】[BeijingWc2008]雷涛的小猫 dp
题目描述 输入 输出 样例输入 样例输出 8 题解 dp 设f[i][j]表示在第i棵树的j高度时最多吃到的柿子数. 那么只有两种可能能够到达这个位置:滑下来.跳下来. 滑下来直接用f[i][j+ ...
- [洛谷P1903][国家集训队]数颜色
题目大意:有$n$支画笔,有两个操作 $Q\;l\;r:$询问$[l,r]$中有几种颜色 $R\;p\;Col:$把第$p$支画笔的颜色改成$Col$ 题解:带修莫队,分为$n^{\frac{1}{3 ...
- [poj] 1066 Treasure Hunt || 判断直线相交
原题 在金字塔内有一个宝藏p(x,y),现在要取出这个宝藏. 在金字塔内有许多墙,为了进入宝藏所在的房间必须把墙炸开,但是炸墙只能炸每个房间墙的中点. 求将宝藏运出城堡所需要的最小炸墙数. 判断点和直 ...
- POJ 2749 Building roads 2-sat+二分答案
把爱恨和最大距离视为限制条件,可以知道,最大距离和限制条件多少具有单调性 所以可以二分最大距离,加边+check #include<cstdio> #include<algorith ...
- BZOJ4300 绝世好题 【dp】
题目 给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi&bi-1!=0(2<=i<=len). 输入格式 输入文件共2行. 第一行包括一个整数n. 第二行包括n个 ...