一篇前端同学对后端接口的吐槽
来源:juejin.im/post/5cfbe8c7e51d4556da53d07f
前言
去年的某个时候就想写一篇关于接口的吐槽,当时后端提出了接口方案对于我来说调用起来非常难受,但又说不上为什么,没有论点论据所以也就作罢。最近因为写全栈的缘故,团队内部也遇到了一些关于接口设计的问题,于是开始思考实现接口的最佳实践是什么。在参考了许多资料之后,逐渐对这个问题有了自己的理解。同时回想起过去的经验,终于恍然大悟自己当时的痛点在哪里。
既然是吐槽,那么请原谅我接下来态度的不友善。本文中列举的所有例子都是我个人的亲身经历。
谁应该主导接口的设计
或者更直白一些,应该是接口的消费方还是提供方来决定接口的设计?
当然是接口的消费方
「接口」最吊诡的地方在于提供方大费周章把它实现了,但它自己却(几乎)重来都不使用。于是这极易陷入一种自嗨的境地,因为他更本不知道接口的好坏。就好比一个从来不尝自己做的菜的厨子,你指望他的菜能好到哪里去,他的厨艺能好到哪里去。上面隐含的前提是(我认为)接口是有绝对好坏之分的,坏的接口消费者调用难受,提供者维护难受,还导致产品行为别扭体验变差。
然而接口的好坏与谁来主导设计有什么关系?因为坏接口产生的原因之一是提供方只站在开发者的角度解决问题:
例子一 (Chatty API)
某次需要实现允许用户创建仪表盘页面的功能(如果你对仪表盘页面感到陌生的话,可以想象它是一张集中了不同图表的页面,比如柱状图、折线图、饼图等等。用户可以添加自己想要的图表到页面中,并且手动调整它们的尺寸和位置。仪表盘通常用于总览某个产品或者服务的运行状态)。后端同学的接口初步设计是,当用户填写完基本信息、添加完图表、点击创建按钮之后,我需要连续调用两次接口才能完成一次仪表盘的创建:
- 利用用户填写的基本信息以及图表的尺寸和位置创建一个空的仪表盘
- 再向仪表盘中填充图表的具体信息,比如图表类型,使用的维度和指标等
- LSUD: Large set of unknown developers
- SSKD: Small set of known developers
例子二
articles: [
{
id: ,
author: {},
comments: []
},
{
id:
author: {},
comments: []
}
]
However, 后端的返回结果可能是以实体为单位:
{
articles: [],
authors: [],
comments: []
}
comments 里包含不同文章的 comment,我必须通过类似于 articleId 的字段对它们执行 group by 操作才能分离出属于不同文章的评论。对其他实体做同样的操作,最终手动的拼接成前端代码需要的 articles 数据结构
很明显这又是按照后端库表关系返回的结果,严格来说这并不算是 anti-pattern,在 redux 中也鼓励将数据 normalize。但如果前端用不到原始数据,请不要返回原始数据。例如我需要在页面上展示一个百分比格式的数据,除非用户有动态调整数据格式的需求,例如千分位、小数或者是切换精度等等,否则就直接返回给我百分比的字符串就好了,不要返回给我原始的浮点数据。前端对数据的二次加工还会给问题排查带来干扰,如果任何数据都需要前端进行二次加工,那么所以问题的排查都必须从前端发起,前端确认无误后再进入后端排查流程,这始终会占用两个端的人力,并且 delay 了排查的进度
关于 meta 信息
例子三:
{
meta: {
code: 0,
error: null,
host: "127.0.0.1"
},
result: []
}
在我看来,以上数据结构有两个问题
meta 信息包含独立的状态信息
meta.code
即可
** 但是谁给你们的自信保证后端接口一定是不会挂的?!** 无论后端如何保证接口的坚固,前端仍然需要首先判断 HTTP code 是否为 200,再判断
meta.code
是否与预期的符合一致。这和信任无关,和我程序的健壮有关。
既然无论如何都要对接口判断两次,那为什么不将
meta.code
与 HTTP code 合二为一?更何况我还需要再本地维护一份自定义 code 的枚举值,还需要和后端保证同步。这就涉及到下一个问题了:
meta 信息的存放位置
- 我们真的需要一个平行于返回结果的字段展示 meta 信息吗?
- 每一次请求我们都需要 meta 信息吗?
- meta 信息一定要在 meta 字段里吗?
meta.code
完全可以使用 HTTP code 代替,我看不出始终要保证 200 返回以及自定义 code 的意义在哪里
而至于其它的 meta 信息,可以通过以
X-
开头的自定义 HTTP Header 进行传递。例如
Github API 中关于使用频率限制的信息就放在 HTTP Header 中:
Status: 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999
X-RateLimit-Reset: 1372700873
Design for today
例子四
{
data: [
{
date: "2019-06-08",
result: [
]
}
]
}
虽然需求很明确的指示只会返回某天的查询结果,但是后端还是决定给我返回一个数组。他这么设计的理由是为了防止日后需求发生改变需要返回多日的查询结果。
这看上去是很聪明决策:“看,我预见性的 cover 了一个未来的需求!”,但实际上愚蠢至极:你的确 cover 了一个需求,不过是一个当前并不存在,未来也不见得会发生的需求;而且如果你真的想写 future-proof 的代码,那么还有未来千千万万的需求等待着你实现。
问题在于没有人知道将来是否真的会允许同时查询多日数据,即使某天需要支持同时查询多日数据了,数据结构也不一定非要如此。在数据分析领域我们面临的查询需求并不是线性从单个到多个,在其他业务领域也是这样。
这样导致的后果是你花费多余的时间实现了不需要的代码,并且前端也需要配合这样的数据结构进行实现。并且在将来的维护中,每个看到返回体是数组的人都会纳闷为什么返回的结果明明只有一条,还需要用数组封装,是不是我遗漏了什么?于是不得不投入精力来验证是否真的有可能返回更多的数据。API 和代码应该是精准的,准确表达你想实现的一切而不存在有歧义
有人可能会说不就是多了一层封装吗?实现上也花不了多少的功夫何至于大惊小怪。抱歉我不是针对这一个 case,而是在强调任何场景下无论实现的难易都不应该添加无意义的代码,“勿以恶小而为之” 就是这个道理
“关注当下” 还有另一个维度含义:
例子五
例子六
不仅限于 REST API
结束语
- Web API 的设计与开发
- Designing Web APIs
APIs A Strategy Guide
相关阅读
嵌套事务、挂起事务,Spring 是怎样给事务又实现传播特性的?
为什么SpringBoot 要两次扫描包?一个MyBatis 分页插件异常问题分析
如何给Spring Boot 的嵌入式 Tomcat 部署多个应用?
源码|实战|成长|职场
这里是「Tomcat那些事儿」
请留下你的足迹
我们一起「终身成长」
本文分享自微信公众号 - Tomcat那些事儿(tomcat0000)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
- 前端吐槽的后端接口那些事
- 前端架构之路(2) - 本地化接口模拟、前后端并行开发
- 前后端分离简单架构搭建 Vue-Cli 后端接口Api+Swagger(1) -前端
- 网站开发,从需求到前端到后端到部署到申请第三方各种接口,开发下来的心得
- 前后端分离之让前端开发脱离接口束缚(mock)
- 前后端对接,我后端可以访问到,但是前端访问我接口不行
- 前后端分离 前端报错500 找不到接口,或者有多个方法
- 前端bugger 后端debug 介绍系统内部逻辑 压测新增订单接口 tps上不去 ,oom ,常见性能问题 ,性能分析思路
- 【Express的demo(一)】 使用express写后端接口、前端调用
- 【知识笔记】前端JS、逻辑层接口、后端SQL之间的调用关系
- 在小程序后端中转获取接口数据,绕过前端调用限制
- 前后端分离:前端人员做页面与渲染,后端做接口
- 后端接口返回给前端的数据格式JSON——0526笔记整理
- 经验分享-前端与后端的接口、HTML分离
- 前端和后端的接口类
- 后端-Node(express)连接mongodb到前端-访问接口将数据显示页面(一条龙)
- 后端接口同时接收POJO对象(json)和基本数据类型的情况下,前端如何传值
- 前端和后端采用接口访问时的调用验证机制(基于JWT的前后端验证)(思路探讨)
- 前端通过JS将从后端接口返回的大数求和的简单实现
- 调试接口==(关于前端传递list的json后端如何接收)