浅谈RESTful接口设计和开发

一、由来和简介

现在我们团队内说开发服务端接口,一般不做特别强调,都指的是 RESTful API。那RESTful API是什么,怎么设计和开发呢?虽然网络上已经有大量相关的资料,但我们在做招聘面试时,发现有不少同学不是很理解,或是在使用时不会变通、受其束缚,所以这个话题需要继续聊聊,介绍基本概念的同时分享一下实践经验。

软件开发从单机软件到互联网化,从互联网单体应用到系统拆分、前后端分离、服务化,接着到后来移动互联网兴起,一度有“mobile first”的策略,再到现在的微服务。可以说软件架构越来越庞大复杂,相关联的各部分需要划分得更细、职责更专一,这样各部分之间的交互就很频繁。RESTful作为一种架构风格、一种接口设计规范,简单、标准、易扩展让其得到了越来越多的应用。

REST 全称 “Representational State Transfer”,乍看很晦涩的名字,翻译为“表现层状态转化”,全称应叫“资源表现层状态转化””。“资源”指服务中的实体或信息,“表现层”指信息的展现形式,如json、xml、html等,“状态转化”指接口访问过程中数据和状态的变化,作为http无状态服务,业务的实现必然要涉及到数据和状态的变化。

二、资源

在RESTful API中,“资源” 使用名词形式的URL表示,且一般为复数,如“/users”,使用具有“ID”同等作用的数据限定单体,如 “/users/666”或”/users/zhangsan”,使用URL连接表示所属或关联关系 如 ”/users/666/projects”表示ID为666用户的所有项目。

接口的版本号,一般也放在URL中,简单方便,易于监控, 如”/v1/users”。 也有将版本号放在http header中,或者有的将主版本号放URL中,小版本好放httpheader中。

三、表现层

“表现层”一般采用JSON字符串,作为接口数据传输,相对于XML,JSON虽然在表现力上若一些,但可读性、数据简洁性上都好很多,且和前端Javascript天生就是一对。接口返回最好采用统一的格式(如:{code: 200, msg:”OK”,data:{}}),方便使用者。其中“code”可以表示特殊业务相关的状态,如定义code为600时表示添加的用户名称重复,在无特殊含义时建议和http status保持一致以减少认知上的负担;”msg”给出必要的简短描述,在某些场景考虑到安全性,可以简化甚至将“msg”留空,如一个未授权的用户访问资源,接口返回http status为401, “msg”不要再做出过于详细的原因说明;“data”可以是对象或列表,若是分页的列表,采用统一的分页数据格式说明分页信息——如当前页码、总页数、总条数。

四、状态转移

“状态转移”可以使用基本的httpmethod和上面的URL形成一个动宾结构,如“POST /v1/users”表示创建一个用户,在http body中提交用户详细信息,成功后接口返回 {code:200, msg:”OK”,data:{id:666, name:”zhangsan”}} 。 一般常用的有GET、POSTPUTDELETE*,分别表示获取、创建、更新、删除,而HEAD表示获取接口元数据。对于GET、PUT、DELETE、HEAD要实现接口的 幂等性,而POST请求根据业务需要也可以实现其幂等性,根据系统实现复杂度,POST请求实现幂等性难度会更大些,特别是在微服务场景下。

五、RESTful规范带来的一些 “困境”

采用一套规范的同时往往也意味着有限制,会对接口设计造成一些困境。

http method有限的几个动词,有时不足以表达接口含义,如“订阅一个项目”(在项目更新时给订阅者发送通知),对资源”/v1/users/666/projects/1”加任何一个httpmethod都不足以表达。此时可以采用变通的方法:一般可以在资源路径末尾加动词或将操作动作抽象为一种实体对象,抽象出来的实体对象尽量采用常规的名称,要易于理解,避免异想天开造轮子。如POST /v1/users/666/projects/1/subscribe 表示订阅用户666的ID为1的项目,POST /v1/users/666/projects/1/star 则将“收藏项目”这个动作抽象为“给项目加一颗星”。

再比如对“/projects”有复杂的查询需求,URL用任何一个“资源”路径都不好表达,且查询参数还比较多——此时就不要再固守“查询只能用GET”,可以设计为POST /v1/projects/search,查询参数太多不适合当作queryparameter 放在URL后面则可以放到http body中。

考虑到很多监控系统,不会去读取httpPOST请求的body内容,在很需要做监控的接口上,甚至可以考虑将POST请求的body部分改为在URL的query参数,如 POST /tasks?name=xxx&template=yyyy 。对于固守规范的同学会很诧异既然用了POST 怎么还将参数放到URL后面——在实际需要很大时,是可以牺牲一些设计上的“完美”的。

一般来说,每个接口职责要清晰明确,业务方根据需要去组合编排这些接口来实现业务。在某些必要的场景也可以将多个简单接口的功能组合到一个大的接口中,以减少接口访问在时间和性能上的损耗。但这样的做法不能太多,否则接口容易交叉、混乱、职责不清。

总之,不要死抱着规范不放,在必要的地方要做变通,多看看一些好的示例学习,如github的接口。

六、领域模型分析和RESTful API设计的关系

在得到一个业务系统需求后,在开发服务接口之前,我们会有一步是对其做领域模型分析,提炼出该业务系统涉及的各种实的或需的领域对象,并识别它们之间的关系。对某些业务系统来说,完成这些领域对象的CRUD操作及它们之间关系的组合变化,就能支撑大部分的业务需求。那这和RESTful API 有什么关系呢?相信到这里你不难发现,RESTful 规范中URL表示的“资源”、“资源”间的关联关系,以及“状态转移”部分说到的http method + URL的动宾组合,都能比较好的继承我们领域模型分析的结果,很好的过度到RESTful API的设计。当然,不表示领域模型里的所有“实体”都会对应到RESTful API中的URL,在接口设计中结合实际情况肯定会有相应的取舍和变化。比如领域模型中分析出来的某个实体可能只是体现在数据存储层,没到接口层,或是这个所谓“实体”其实是多个概念的综合表现,体现为多个小的实体及其关系。

通过领域模型分析,结合DB数据层技术上的考量,可以将需要落库的对象进一步分析得到DB的ER图设计。至此,至下往上从数据层、之上往下从接口层都清晰了后,中间层的代码就容易组织得清晰,而一个新来的同学也比较容易在这个框架内去理解和扩展。

七、RESTful API设计对开发的影响

在一个团队中,如果没有清晰的系统设计、接口设计,特别是新人,在拿到一个业务需求时,容易仅仅面向当前需求来编写代码,缺少一个通用的、长远的考虑。如要提供一个接口给xxx系统“创建任务”,接口名甚至可能被定义为/createXxxTask,接口内部逻辑实现上自然也会加上针对这个业务的一堆if else。可以想象在接入的业务需求多了后,会有很多同质化的接口,也会存在不少大同小异的代码重复。

转换一个角度,理解要实现的业务需求后,将目光由外转到系统内。根据以上提到的领域模型分析、RESTful API设计,我们对当前系统有哪些“资源”,他们组合关系是怎样的,进而得出系统目前能提供哪些能力,还欠缺什么,然后再在这上面去扩展。尽量将这个需求本质上要操作哪些资源和关系提炼出来,设置更加通用和可扩展的接口。

相对于天马行空无章法地接口设计,RESTful接口设计规范在给设计着开发者戴上一副脚镣的同时也让其舞得更美。

感兴趣的可以进一步了解 接口身份认证、HATEOAS**、各种对数据的序列化方式以及RPC 方案和RESTfulAPI 方案在技术选型上的取舍

接口设计过程中可以使用一些工具、文档来沉淀设计成果,同时用于加强在设计、开发过程中团队之间的交流协作 —— 这部分内容留待下次再聊。

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

推荐阅读更多精彩内容