六步实现Rest风格的API

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的规范来保证这种统一性。

创建并保存一个博客:

[plain] view plaincopy

  1. POST /blog/save

这个请求需要返回博客的保存后的结果,其中包括博客的标识(ID)。 获取一个已经保存的博客,并更新它:

[plain] view plaincopy

  1. GET /blog/get/345
  2. //更新它
  3. POST /blog/update/345

这个博客的标识是345。获取博客的某个回复:

[plain] view plaincopy

  1. GET /blog/get/345/reply/456

对待子资源,通常的做法就是和这个例子一样,是一级一级的往下找。我们还可以有其他方法,比如remove用来删除,merge用来部分更新,list用来查找。

有三种方式可以将参数传给API操作:

第一种是通过URL的地址传递,如前面的例子中把标识放在URL里;

第二种是通过URL的参数,比如,对于一个查找请求,可以把查找的过滤条件放在参数里:

[plain] view plaincopy

  1. 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又能简化:

[plain] view plaincopy

  1. PUT   /blog           //创建保存一个新的博客
  2. GET   /blog/345    //获取博客345内容
  3. PUT   /blog/345    //更新博客345
  4. GET   /blog/345/reply/456     //获取博客345的回复456
  5. POST /blog/345    //更新博客345的部分内容
  6. 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。而实现无状态后,我们可以随意增加,减少服务器数量,都不会对当前用户造成影响。

关键字: Rest, Resful, 实现Rest

时间: 2024-11-13 09:14:46

六步实现Rest风格的API的相关文章

六步轻松搞定,自建APP不求人

随着互联网浪潮的席卷,越来越多的传统企业开始涉足互联网领域.无论是出于企业转型升级考虑,还是受市场整体环境的驱动,很多企业凭借某一领域的绝对优势,浩浩荡荡进军移动互联网领域,通过自建APP的方式,推出属于自己的移动端应用产品. 不少企业在探寻如何自建APP的过程中,遇到的第一个决策性难题就是:自建APP开发团队还是外包APP建设?其实对于大多数企业而言,由于企业内部组织架构设置问题,尚没有配备完善的自建APP开发团队,同时财政预算有限,难以承担整体自建APP工作.对于这类中小型企业而言,如何能够

程序设计入门学习六步曲

初学者遇到最多的困惑是:上课也能听懂,书上的例题也能看明白,可是到自己动手做编程时,却不知道如何下手.发生这种现象的原因有三个: 一.所谓的看懂听明白,只是很肤浅的语法知识,而我们编写的程序或软件是要根据要解决问题的实际需要控制程序的流程,如果你没有深刻地理解C语言的语句的执行过程(或流程),你怎么会编写程序解决这些实际问题呢? 二.用C语言编程解决实际问题,所需要的不仅仅是C语言的编程知识,还需要相关的专业知识.例如,如果你不知道长方形的面积公式,即使C语言学得再好你也编不出求长方形的面积的程

测开大佬告诉你:如何5分钟快速创建restful风格的API接口-使用django restframework框架

一.思考?? 1.创建API接口难吗? 软件测试工程师: 只测过API接口, 从没创建过 应该需要掌握一门后端开发语言和后端开发框架吧!? 脑容量有限,想想就可怕 2.如何创建API接口呢? 使用Django restframework能快速创建符合restful风格的API接口 通过Django restframework框架提供的序列化器.视图集可轻轻松松实现 简单高效,学习成本低 二.搭建项目工程???? 1.创建项目 打开pycharm 创建项目 使用豆瓣源安装Django Django

PHP实现RESTful风格的API实例(一)

(关于Yii2的RESTful教程请看: Yii2框架RESTful API教程) 最近看了一些关于RESTful的资料,自己动手也写了一个RESTful实例,以下是源码 目录详情: restful/ Request.php 数据操作类 Response.php 输出类 index.php 入口文件 .htaccess 重写url Request.php :包含一个Request类,即数据操作类.接收到URL的数据后,根据请求URL的方式(GET|POST|PUT|PATCH|DELETE)对数

PHP实现RESTful风格的API实例(二)

接前一篇:PHP实现RESTful风格的API实例(一) Response.php :包含一个Request类,即输出类.根据接收到的Content-Type,将Request类返回的数组拼接成对应的格式,加上header后输出 <?php /** * 输出类 */ class Response { const HTTP_VERSION = "HTTP/1.1"; //返回结果 public static function sendResponse($data) { //获取数据

PHP实现RESTful风格的API实例(三)

接前一篇:PHP实现RESTful风格的API实例(二) .htaccess :重写URL,使URL以 /restful/class/1 形式访问文件 Options +FollowSymlinks RewriteEngine on # 重写规则 RewriteRule ^class$ index.php?class=all [nc,qsa] RewriteRule ^class/(\d+)$ index.php?class=$1 [nc,qsa] 在Apache的web目录下新建一个restf

PHP实现Restful风格的API(转)

Restful是一种设计风格而不是标准,比如一个接口原本是这样的: http://www1.qixoo.com/user/view/id/1表示获取id为1的用户信息,如果使用Restful风格,可以变成这样: http://www1.qixoo.com/user/1可以很明显的看出这样做的好处: 1.更简洁的URL,对程序员友好 2.不暴露内部代码结构,更安全 那么,如何实现这个接口呢?首先,我们需要接收到/user/1部分. $path = $_SERVER['PATH_INFO'];$ar

快速入门git第六步

远程仓库的使用远程仓库是依托在网上的管理的项目,有些只能读,或可以写git remote 命令列出了该项目所有的远程仓库的简短名(它对应的是仓库的地址即url,如origin实际上是一个url),克隆或提交项目需要该名,默认是origin的远程库git remote -v 列出了简短名和url添加一个远程仓库: git remote add 简短名 url 如果是ssh协议则url 是用户名@ip:文件路径, 如果是github拉取项目,则是git://url.git远程仓库建好后,git pu

没有拿到软考证书,你肯定还差这六步!

计算机技术与软件专业技术资格水平考试简称为计算机软件考试,也就是广大技术人所说的软考.考试难度也是有的,"有证"."没证"对职业的发展还是有一定的影响,今天小编就为大家带来软考中高项必备的六步走,首先让我们来看一下软考都有哪些分类: 软考六步走: