浅淡RESTful api设计规范

目前主流的通讯协议主要有RPC、http/1.1、http/2等,而http中最主流的无疑就是restful了,由于工作的原因,经常需要和不同的外部服务商进行系统集成,给出的文档都说是基于restful规范设计,遗憾的是,在我看来,几乎没有看到过真正可以称之为restful架构的api设计。今天就来谈谈如何设计一个规范、优雅、可读性高的restful api

restful其实本身并不是一个新鲜的东西,最早是在2000年由Roy Thomas Fielding博士在他的博士论文中提出。说起这位老兄,来头可不小,他是http 1.0和1.1版本协议的主要设计者,apache基金会的第一任主席,可以说是现代互联网体系的奠基者。Fielding将他对互联网软件的架构原则,定名为REST,即表述层状态转移(Representational State Transfer)。这是一套在互联网体系下,调用者如何与被调用者(资源实体)进行互动的规范设计。

rest.jpeg

当时的互联网其实还是处于刚萌芽的状态,这个设计思想过于超前,所以早些年一直处于不温不火的状态。直到近年来,互联网业务高速发展,系统架构越来越复杂,移动互联网的兴起,前后端分离架构的流行,人们发现原来这套用于超文本传输的协议是如此适合用于设计基于互联网的api接口,基于http动词以及标准的http status返回信息,能够非常好的描述api的特性,并且可读性非常好。更重要的是,由于http是事实上的互联网通讯标准协议,基于rest设计的api接口,就好像你出国用英语和别人交流,完全不存在沟通障碍。
关于restful设计的最佳实践,这里还是推荐阮一峰老师的RESTful API 设计指南,个人觉得是国内范围里讲的最好的了。rest架构,从个人角度理解,核心做了两件事情

  • 资源定位
  • 资源操作

其实从REST的定义中就能看出来,表述层对应的就是描述资源的位置(资源定位),状态转移就是对资源的状态进行变更操作(增删改查)
下面举个实际的例子:
假设我们数据库里有一张User表,我们根据表建好了领域对象模型User,按照restful规范设计的接口应该是这样的:

  • 新增用户
[POST] /users
  • 修改用户
[PUT] /users/id
  • 删除用户
[DELETE] /users/id
  • 查找全部用户
[GET] /users

看到这里可能有同学就要问了,干嘛非得这么设计,还要用什么http动词,delete、put神马的我都没用过,平时都是get、post走天下,也用的好好的呀。新增用户不能用/addUser吗?删除用户不能用/deleteUser吗?感觉也很清楚啊(事实上很多公司的所谓的restful接口文档都是这么定义的)
好,现在让我们回到前面,复习一下rest的定义,第一条叫做资源定位,如果还不理解,那让我们再想想URL的定义,叫做统一资源定位符,也就是说url是用来表示资源在互联网上的位置的,所以说在url中不应该包含动词,只能包含名词。对资源的操作应该体现在http method上面,如果这样理解还比较抽象的话,这里不妨再打一个比方,比如在jane的网站有一张小汽车的图片,地址是http://jane.com/img/car.jpg,现在jane想设计一个api接口,实现对这张图片的删除操作,这个api应该怎么设计?根据rest的设计规范,很容易得出是

[DELETE] http://jane.com/img/car

非常的清晰明了(这里暂时先不考虑调用方是否有权限删除服务器上的资源)
注意这里为了讲述原理没有加资源的后缀.jpg,引用阮一峰老师的话

严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

在这个例子里,我们可以通过在http header里指定content-type为image/jpeg来申明这个资源是一张jpg格式的图片
接下来,如果要查询(获取)这张图片呢?自然就是

[GET] http://jane.com/img/car

再进一步,我们再改造一下这个api,加上.jpg后缀,这个api就变成了

[GET] http://jane.com/img/car.jpg

看到这里大家应该都很熟悉了,这就是我们每天上网要进行无数次操作的api,就是这么设计出来的

我们继续扩展一下,现在我们要获取的不是静态图片资源了,而是一辆小汽车的相关信息,并且需要对车库里的汽车进行增删改查的维护操作。如果用上面讲的那种一般http的写法,可能会写出类似下面这样的api(只用GET和POST方法)

[POST] http://jane.com/garage/addCar
body:{"brand":"ford","model":"focus","price":"120000"}

[POST] http://jane.com/garage/udpateCar?id=123
body:{"brand":"ford","model":"focus","price":"130000"}

[GET] http://jane.com/garage/queryCarList

[GET] http://jane.com/garage/queryCarSingle?id=123

[GET] http://jane.com/garage/deleteCar?id=123

看出问题来了吗?一个严重的问题是url丢失了资源的位置,更重要的是,你可以叫deleteCar,也可以叫eraseCar,还可以叫removeCar,具体什么含义只有设计这个api的人才能说清楚。而如果用http method,那就肯定是DELETE这个方法,所有看这个api的人都知道你提供的是一个删除这个资源的方法,这就叫做语义化,能用最少的话把一个意思表达清楚,这本身就是一种优雅的设计方式。使用rest设计上述api,结果如下

[POST] http://jane.com/garage/cars
body:{"brand":"ford","model":"focus","price":"120000"}

[PUT] http://jane.com/garage/cars/123
body:{"brand":"ford","model":"focus","price":"130000"}

[GET] http://jane.com/garage/cars

[GET] http://jane.com/garage/cars/123

[DELETE] http://jane.com/garage/cars/123

这里http://jane.com/garage/cars/123代表了id为123的这辆小汽车在网上的唯一位置,本质上和http://jane.com/img/car所代表的含义是一样的。
使用rest能带来的额外的好处,是你可以做很方便的权限控制。因为POSTPUTDELETEGET等都是标准的http方法,你可以很轻松的在nginx这样的7层代理或者防火墙上设置策略,禁止某些资源的修改及删除操作,而这显然是自定义的url所达不到的。
除了HTTP METHOD,rest另外一套重要的规范就是HTTP STATUS,这套状态码规范定义了常规的api操作所可能产生的各种可能结果的描述,遵循这套规范,会使得你的api变得更加可读,同时也便于各种网络、基础设施进行交易状态监控。经常会用到的status code整理如下:

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

事情到了这里似乎一切都很美好,可惜人生不如意十之八九,api设计也不可能一帆风顺。总有一些场景是CRUD所抽象不了的,举个简单的例子,用户登陆,如何去匹配CRUD模型?这里我的建议是,先把你的操作对象或者行为抽象为资源,然后就简单了,无非就是对这个资源的CRUD。
针对用户登陆这个场景,我们可以把用户在远程服务器的会话信息抽象为一个资源,这样的话,登陆其实就是在远程服务器增加了一个会话资源,不难想到,登出就是在远程服务器删除了一个会话资源,所以api可以这样设计

[POST] /login
[DELETE] /logout

如果是发送短信呢?似乎更难。。。这里再次请出阮一峰老师

如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务

这样的话你把发送短信理解成一种服务,api可以这样设计

[POST]  /smsService
body:{"mobile":"13813888888","text":"hello world"}

最后建议大家去看一下github的api文档,可以说是restful架构最完整的实现了,看完后一定会对restful规范有着更深入的理解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • 一、概述: RESTful架构,就是目前最流行的一种互联网软件架构。REST是Representational S...
    极客开发者阅读 628评论 0 0
  • 一、什么是API? API(Application Programming Interface,应用程序编程接口)...
    Fairy_妍阅读 62,617评论 2 42
  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,423评论 0 19
  • ​ REST这个词是2000年Roy Fielding在他的博士论文中提出的,Fielding参与了http协...
    智明书阅读 1,918评论 0 6
  • 有一种美,叫安静,安静的姿态是美的。在安静生命的眼中,一切外在的物质形式,像风中的浮云。 一个安静的生命舍得丢下尘...
    简若素阅读 504评论 0 0