我将今天想要谈的范围圈定于狭义上的中小规模的业务功能前后端RESTfulAPI接口开发,对于超过范围的内容可能因水平不够而谈的不够准确深刻。主要内容包括我工作中经常遇到的问题和个人的检讨思考。
“API接口可以使不同的软件组件之间进行无缝的通信和交互,从而提高了软件开发的效率和灵活性。”典型的软件组件之间就是前端和后端之间,效率和灵活性是我们要追求的目标之一。
那么我们实际开发的API达到目标了么?如果以是否实现了业务功能为目标,那我相信大部分开发人员都能拍着胸脯保证我写的API绝对能实现功能,而且极度自信绝无可能存在bug。但如果以是否高效和足够灵活来判断,则:
接下来我们来看看下面的几个问题你是否也感同身受?
以上几个问题可能每天都发生在我们的身边,但我们早已习惯麻木,直到AI编程的出现,它将会毫不留情的指出我们的缺点,激进的对我们的代码进行重构,写出具有非常优秀特质的API,最后取代我们的工作干碎我们的饭碗!听起来太可怕了,那么我们还是了解一下什么是优秀API的特质吧:
优秀的API应该具有易用性、可扩展性、安全性、稳定性、兼容性、文档化、可测试性和良好的性能和效率等特质,以满足开发人员和客户的需求。
这些特质我们文字上都看得懂,但很多同学还是不知道要怎么做,那API设计都有哪些原则呢?
API设计应该遵循简洁性、一致性、可预测性、可扩展性、安全性、高性能、可伸缩性、可测试性、可文档化和面向资源等原则,以提供易用、可靠、高效和安全的API服务。
通过这些文字,我们意识到原来我们工作中开发API接口很多都没有遵循这些原则,例如:简洁性、可文档化对应问题1,一致性、可预测性对应问题2,高性能、可伸缩性对应问题3,可扩展性对应问题4。
原则是死的,人是活的,我们还没进化到完全通过AI智能写代码的程度,尽管那是趋势,但我很好奇当AI读到我们的屎山代码的时候,它会发出怎样的感叹!或者当我们用大量的屎山代码库去训练AI时,它会不会写的比我们更好?
接下来我在API接口设计阶段的思路和方法上进行举例和总结,重点用粗体表示,可能有失偏颇的地方还请各位批评指正。
不是随便一个功能就要有个接口,也不是随便一个需求就要加个接口。
每新建一个接口,要有充分的理由和考虑,即这个接口的存在是十分有意义和价值的。无意义的接口不仅增加了维护的难度,更重要是对于程序的可控性的大大降低,接口也会十分臃肿。
设计接口时,分析的角度要统一。否则会造成接口结构的混乱。每个API接口应该只专注一件事,并做好。产品概念简单、关系清楚。功能模棱两可,诸多特殊逻辑的API肯定不是个优雅的API,且会造成功能类似重复的API。
注意:如果API它很难命名,那么这或许是个不好的征兆,好的名称可以驱动开发、并且只需拆分与合并模块即可。
避免:/api/getdata、/api/servicexyz、/api/action1、/api/action2、/api/action3
推荐:get('/api/users/:id/orders')、put('/api/users/:id')、post('/api/users')、delete('/api/users/:id')
功能大而全的API在灵活性、简单性方面肯定捉襟见肘。定义API的粒度之前,建议先将业务分领域、划边界,以此来提取业务对象,然后再根据业务对象用例来设计单一功能的API。
代码举例:
此外,如果将来需要添加更多的选项,可以简单地在单个对象中添加新的属性,而不需要更改函数的签名。这提高了代码的可扩展性。
API的入参、出参所述的对象、属性,一定是按业务特性进行抽象后的实体,而不要误将底层数据模型概念如实的反应到API上。抽象API、抽象对象实体更宏观,具有更好的适用性、兼容性、扩展性。
//错误的代码functioncreateUser(username,password,role_id){//...}//底层数据模型classUser{constructor(username,password,role_id){//...}}在这个示例中,API使用了底层数据模型的概念来定义createUser函数的参数,即username、password和role_id。这使得API难以使用和难以维护,因为调用者可能不知道如何使用这些参数,并且将来更改底层数据模型时,这些参数也需要更改。
//更好的代码functioncreateUser(user){//...}//更好的底层数据模型classUser{constructor(username,password,role){//...}}这样就可以将API的参数定义为一个包含用户信息的对象,这样调用者就可以更容易地使用API,并且在底层数据模型更改时,也可以更轻松地更新API。
API代码应该尽可能减少让读者惊讶甚至惊吓,你是否有过看到一堆或某行奇葩的代码时发出“我艹”惊呼的经历?惊为天人在程序员的世界里是个贬义词。业务API只需根据需求来设计即可,不需要刻意去设计一下复杂无用、华而不实的API,以免弄巧成拙。
下面是ChatGPT聊天的API接口文档,可以看到接口是中规中矩、平平无奇甚至有点简陋到随便一个初级开发就能够写出来,model+messages两个必传参数就已满足需求,但接口背后的技术似乎正在引领一场技术革命。让人惊讶的不是接口本身,而是接口能够提供的能力。
API应该减少对其他业务代码的依赖关系。低耦合往往是完美结构系统和优秀设计的标志。
耦合的种类:
对于API调用者而言,API应该是可被测试且易于被测试的。测试API不需要依赖额外的环境、容器、配置、公共服务等。
对可测试友好的API也是可被有效集成测试的前提。另外单元测试、集成测试和自动化测试工具也很重要,测试用例很可能我们都听过但都没写过,但这都不重要,重要的是接口写完了你总得试着执行一下吧?啥都不管直接丢给使用方/测试方真的合适吗?遇到没充分自测的接口,使用方/测试方的内心活动是:二营长!你他娘的意大利炮呢?给我拉来!
重要的事情说三遍:“一定要接口自测!一定要接口自测!一定要接口自测!”
API要具备统一的命名、统一的入/出参规范、统一的异常规范、统一的错误码规范、统一的版本规范等。
统一规范的API优点:
同志们,任务仍很艰巨,让我们一起来完成统一大业吧!
以上8条不仅适用于前端和后端之间的API接口,也同样适应于前端与前端之间、后端与后端之间。
其实以上提到的这些示例、经验和教训网络上一搜也是一大把,作为信息技术如此发达的时代,想要找到一些关于接口设计方面的内容可以说是轻而易举,但为什么人们就是不喜欢遵循API设计原则来进行API接口开发呢?
有一些可能的原因,导致人们不喜欢遵循API设计原则来进行API接口开发:
总的来说,遵循API设计原则可以提高API的质量和可维护性,但是这需要开发人员有一定的经验和知识,并且需要在公司文化和管理上得到支持和重视。
好吧,如果有人能够耐心的看到结尾这里,一定是一个技术觉悟非常高的人!