接口设计技巧和最佳实践

博客首页文章列表 松花皮蛋me 2019-07-05 13:00
文章首发于公众号 松花皮蛋的黑板报松花皮蛋的黑板报,作者就职于京东,在稳定性保障、敏捷开发、高级JAVA、微服务架构有深入的理解

这篇文章是从人们在设计和实现接口时常见的和常被无视的错误,总结出来的一些技巧和最佳实践

1、严格的数据模型层

你的响应应该是在代码中严格定义的嵌套数据业务模型,不要依赖数据库查询结果映射,或者其他操作

2、无歧义的服务名

记住你的URL应该能充分表达出真实作用,而不是需要翻阅文档才能了解,另外不要不情愿使用版本号命名路由,当然服务版本应该要做到向下兼容

3、数据类型强一致

数值字段应该始终只包括数字,字符类型字体意义始终只包括字符串,同一个字段中不应该混合多种类型数据

4、始终返回所有的字段

不要删除字段属性,即使值为空

5、不要滥用JSON对象

API中的每个JSON对象应该始终在请求之间具有不可变性,具有严格的定义的字段集,下面这种返回就是可怕的做法

正确返回应该是

6、不要滥用JSON数组

当绝对无法避免在同一数组中返回不同类型实体时,尝试返回足够抽象的对象列表,里面包括所有对象,每个对象显示标明类型。比如飞机和汽车不应该出现在同一个返回数组中,但是无法避免时,可以使用下面这种方式

7、不要依赖普通的硬编码错误信息

接口返回错误时,在响应正文中应该包括严格定义的错误对象,对象一般包括内部代码和附加信息

8、不要使用数字枚举

9、不要返回非封装的响应

使用对象作为根响应容器以允许后续添加任意数量的字段而不会导致弃用,比如我们可以使用is_available布尔值标识book的状态,但是它没有表明为啥是不可用状态?什么时候会变得可用?如果将来需要增加其他信息,你将不得不修改根响应

10、使用JSON布尔值

11、尽量让你的接口满足HATEOAS 约束

服务器提供给客户端的表达中包含了动态的链接信息,客户端通过这些链接来发现可以触发状态转换的动作,资源的URI和其他信息都是动态发现的,当服务端发送变化时,客户端并不需要做出修改

12、考虑让你的接口结果可缓存

客户端可以缓存服务器返回的响应结果,服务器可以定义响应结果的缓存时长设置

13、为你的接口实现限流

API确实实施了速率限制的话,请务必通过响应提供其当前状态来告知你的调用者

14、考虑让你的接口返回支持字段过滤

客户端请求可以指定希望服务端在响应中包括哪些字段或者排除哪些字段,这样可以有效处理响应膨胀

15、接口支持高级分页

分页可以减少客户端接收的数据数目,但是当你需要将分页结果与不断接收的新条目结合时,通常的限制limit和偏移offset分页参数是低效的,因为每次当有个新条目在服务端被添加到先前的集合时,先前发送到客户端的偏移offset都变得无效,而且客户端无法得知在两次请求间新增了多少条目。保持客户端同步一个比较好的办法是使用before_id和after_id参数组合,比如客户端将已知的最新条目的id作为after_id请求参数,然后检索之后创建的新条目

16、接口异常显式返回

RPC调用中Exception应该也是返回值的一部分,应该设计成Checked Exception,尽量让调用方能够显式的处理

17、接口使用Specification规格模式

设计者应该避免太多findBy方法和各自的重载,正确的打开方式应该类似组合模式

public interface StudentApi{
     Student findBySpec(StudentSpec spec);
     List findListBySpec(StudentListSpec spec);
     Page findPageBySpec(StudentPageSpec spec);
 }

18、为功能定义接口,不为个别使用方定义接口

定义好统一的路由接口,而非为每一个使用方定义个别处理,如果需要特殊字段,要考虑特殊字段的通用性,如果有通用性,在通用接口上加上字段,其他使用方可维持空,如果没有通用性,作为一个配置字段配置进去

文章翻译摘录自:
Part 1: Introduction and planning
Part 2: Schema suggestions, common mistakes and deprecation
Part 3: Documentation tips and moving beyond the basics