从清晰的RESTful接口到漂亮的系统设计
接口是系统与外界交互的窗口,其他系统通过接口可以知道你管理着哪些资源,他能对这些资源干些什么。当然我们不遵守RESTful的建议也可以满足上面的目标,既然如此我们为什么还要按RESTful的规范来设计我们的接口呢?这样的灵魂拷问很现实也很真实,有小朋友会讲,我写接口都是一把梭,快得很,什么RESTful?不存在的。那么我们先来聊一聊为什么要把接口给RESTful了,稍后再聊如何做,有哪些正例反例,
接口是系统与外界交互的窗口,其他系统通过接口可以知道你管理着哪些资源,他能对这些资源干些什么。
当然我们不遵守RESTful的建议也可以满足上面的目标,既然如此我们为什么还要按RESTful的规范来设计我们的接口呢?这样的灵魂拷问很现实也很真实,有小朋友会讲,我写接口都是一把梭,快得很,什么RESTful?不存在的。
那么我们先来聊一聊为什么要把接口给RESTful了,稍后再聊如何做,有哪些正例反例,以及如何以接口为开头来设计出漂亮的 DDD CQRS 的微服务系统。
为什么要写清晰的RESTful接口
- 技术人员也是要面子的,接口文档的交付也是一种技术人员的交手,高手过招在开打前,看你的架势就能大概知道你的水准,设计良好的接口会让对方感觉出你的基本功、设计理念、对技术运用的拿捏、对细节的关注。这就好比武林高手开打前先拉开架势互相观察一样。
- 在同样的规范下容易快速达成共识,减少不必要的解释。大家都遵从一样的规范,一看就明白这个接口是干嘛的了,省去了很多相互之间的误解以及沟通。一个眼神对方就明白你要干嘛了。
- RESTful的接口易于调用,与语言无关,有利于将服务提供给更多消费者。
- 它是面向资源的,能通过接口帮助我们描述我们的资源、资源的结构,能帮助我们去建立领域模型。
- 实现RESTful接口的文档与框架有很多,能为我们提供很多帮助与参考。
- 我们只要花很小的代价就能写好RESTful接口。
当然优点还会有很多,但是上面几个应该已经够了,特别是第一个,面子是最重要的。
RESTful的最佳实践
REST,Representational State Transfer的缩写,表现层状态转换,这个怎么理解呢?
其实这个和我们的程序是一样的,程序是什么?输入一个值,经过逻辑处理改变其状态,返回改变后的状态。
RESTful接口就是把这个值的输入通过http协议传输给到我们的处理逻辑,那么我们的程序如何知道要操作哪个资源,如何操作呢?是读取、创建、更新还是删除呢?
这就引出了另外两个在RESTful接口中很关键的显式要素了:URI和HTTP 请求方式。开始最佳实践前我们讲讲URI和HTTP 请求方式在这个过程中扮演什么角色:
- URI ,指向我们要操作的资源,相当于解答了我是谁;
- HTTP 请求方式,通过GET、POST、PUT、PATCH、DELETE告诉系统,他想对URI指向的资源干什么,解答了要干什么。
上面的文字还是会比较抽象,我们先来看个简单例子,我就用上周一位朋友发我的一个车牌号来举例:
请求方式 | URI | 要干什么 |
---|---|---|
GET | cars | 获取所有的车 |
GET | cars/IPX-177 | 获取车牌号为IPX-177的车 |
POST | cars/IPX-177 | 创建一辆新的IPX-177车,但是在这里会失败,因为已经存在了 |
PUT | cars/IPX-177 | 修改IPX-177这辆车 |
PATCH | cars/IPX-177 | 部分修改车IPX-177 |
DELETE | cars/IPX-177 | 车开完了进入贤者模式,一切索然无味的时候,删除车IPX-177 |
例子举完了,开始讲解最佳实践前,希望大家对着这几个例子观察一下,细品一下,不要用快进模式,之后我们再来分享一些好的实践。
好的实践
- URI是指向资源的,不要带动词,动词由HTTP请求方式来带,我们看第一个例子,这个很简单但是却很不容易做到,回顾我们那些曾经写过的接口,应该很多都在URI里带了动词;
/**
* 获得车牌对应数据
*/
//正例
/cars/ipx-177
//反例
/getCar?id=ipx-177
- 资源的单词用复数,且尽量易懂,这样便于我们对资源做进一步定位,最重要的是便于接口使用者理解和使用,要用做产品的心来做接口,因此接口应该对使用者友好;
//正例
/cars/ipx-177
//反例
/car
- 通过{id}及子资源构成的URI能定位到一条链路上的资源,如我们的例子2;
//正例
/cars/ipx-177
//反例
/car?id=ipx-177
- 在URI中带有大版本号,在header中携带小版本号;
//正例
https://phoenix.force/v1/cars
Accept: json; version=1.1
//反例
https://phoenix.force/v1.1/cars
//正例
https://phoenix.force/cars
- 为集合查询接口提供对筛选、排序、分页、返回字段的支持。只有这样的集合查询接口,才是具备好的扩展性与设计的接口。
//正例
cars?cup>C&fields=type,title,des&sort=-create_time&from=1&size=10
//反例
不支持就是反例了
- 使用Http状态码处理错误,在返回JSON数据中返回具体原因。
- 使用TOKEN进行状态管理,而不是session或cookie。
- 打开gzip压缩。
- 让你的新增、修改、删除接口做好幂等。
- 不要手工维护接口文档,更不要久不更新让文档与实现不同步,使用工具帮你实现代码就是接口文档。
其实常用的建议就这些了,不要贪多,建议不是越多越好,能把上面10条做好已经很不容易了,相信我。
让你的RESTful接口充满恨
我们看了一些好的实践,那么肯定也有不好的,其实这个部分甚至比上面好的实践更重要,为什么呢?
这个就像兵法一样,我们用兵法首先是要干嘛? 首先是让自己活下去,这里也是我们先不追求做得有多好,我们先不要让自己的接口引来仇恨的炮火。
- 用自定义的奇怪格式定义接收或返回数据。
- 新增、修改、删除接口没有做幂等。
- 接口文档与接口实现已经对不上了。
- 返回码乱七八糟,含糊不清,对于问题定位起不到任何帮助。
- 接口没有版本概念,一升级就把调用方全部往死路上逼。
如果你恨一个人,请按以上5点提供接口供对方调用。
未来的一些趋势
这里做一些简单介绍,有兴趣的小伙伴可以先自行搜索查看,我们后续也会陆续进行分享。
学习的一个好途径依然是看官方文档,这个不会错。
- GraphQL
- HATEOAS
- WebFlux
开篇讲的从清晰的RESTful接口到漂亮的系统设计,这里我们是不是还没讲到漂亮的系统设计,DDD、CQRS什么的?别急,这篇我们先到这里,因为漂亮的系统设计其实是一个系统性问题,在这里我们尽量先把涉及到的一些方方面面都简单讲一下,最后一组装,好的系统设计就水到渠成了。不然我们干巴巴的讲系统设计太生硬了,效果不好,这必须结合一些基础知识,业务场景,组织架构一起来讲,要带到环境里场景里。
更多推荐
所有评论(0)