搭建前后端之桥

bridge.gif

随着前后端分离,开发的门槛降低了,我们不再要求团队中的每个开发都是全栈工程师,这样更容易找到项目的合适人选。团队也划分成了前端和后端两个团队。前端负责消费 API 并展示页面,后端负责提供 API。这两个团队可以并行开发互不影响,大大提升了效率。虽然前后端分离解决了很多问题,但同时也带来了新的困扰。

前后端分离带来的困扰

沟通成本

前后端成为两个独立团队之后,协作的问题便随之而来。通过什么来协作呢?契约。简单来说,就是预先定义好精准的接口,比如接口的 URL,包含哪些参数、返回值,每个值的类型,是否为空等等。定义好之后,前后端就按照契约进行开发。但是在实际场景中,却经常出现问题。

举个真实的例子。有一次,后端在重构时修改了一个字段名,同时也修改了契约测试,但是却忘了告诉前端。由于这个页面的使用频率不高,前端也工作在别的地方,因此那个页面挂了许久都没有人发现。这些隐藏 Bug 会给我们的应用带来隐患,同时也会增加开发的负担。

在实际开发过程中,保证人人都遵守契约是一个很困难的事情,因为人都可能会犯错。

大量的模板代码

即便团队中所有人都能严格遵循契约,但集成 API 仍旧是个苦力活。我需要定义请求的 URL、Method、参数、参数类型以及返回值类型等等。于是项目中就充斥着下面这样的模板代码:

interface ICreateBookRequestData {
  bookId: string;
  category: string;
  date: string;
  createdBy: string;
}

interface IBook {
  id: string;
  author: string;
  name: string;
  price: string;
  publishDate: string;
  publishVendor?: string;
}

export const createBook= createRequestAction(
"@@books/createBook",
(data) => ({
    url: "/books/book",
    method: "PUT",
    data,
  }),
);

在多人协作时,为了减少冲突,我们通常会按照业务场景,将请求相关的代码存储到不同文件。比如login.api.ts 存放登录相关的请求代码,account.api.ts 中存放账户相关的请求代码。但是这会造成另一个问题,就是类型的重复定义和定义不一致的问题。

在上面的代码中,我们定义了请求响应数据的类型:IBook。对于后端来说,这个数据类型是可复用的,其他接口也可以使用它。但是对于前端来说,很难确定这个数据类型在哪些地方被使用了。因此在不同的文件中,可能会重复定义相同的类型。由于不同的人对同一个数据的类型的理解可能不一致,还会造成定义不一致的问题。比如在 A 文件的 IBook 中我们定义 publishVendor 是一个可选的属性,而在 B 文件中我们可能再次定义 IBook 并把 publishVendor 定义为一个必需的属性。如下所示:

interface IBook {
  id: string;
  author: string;
  name: string;
  price: string;
  publishDate: string;
  publishVendor: string;
}

连接前后端

为了解决上面的问题,我们实现了一个自动化工具,将割裂的前端和后端重新连接起来。简单来说,就是通过 Swagger JSON 自动生成调用 API 所需的代码以及类型定义。

OpenAPI 规范(以前称为 Swagger 规范)为 RESTful API 定义了一个与语言无关的标准接口,允许人和计算机发现和理解服务的功能,而无需访问源代码、文档或开发者工具。Swagger 是一套围绕 OpenAPI 规范构建的开源工具,可以帮助我们生成、描述、调用和可视化 RESTful 风格的服务。

有了自动化工具之后,只需要在终端中执行一行命令,就能立刻生成项目中所有 API 相关的代码以及类型定义。这样就不用再写模板代码了,节省了很多时间。同时,由于所有代码都是通过 Swagger JSON 生成的,当接口发生变动时,我们不用再去查看文档或者询问后端修改了什么,只需要通过命令就能知道哪些接口发生了变化并自动更新对应的前端代码。因为所有代码都是自动生成的,重复定义的问题也就不存在了。对于简单业务场景来说,集成 API 就是一行代码的事:

// getBooksUsingGET 方法由工具自动生成, useTempData 会发起 HTTP 请求并返回响应数据
const [books] = useTempData(getBooksUsingGET, { bookType }, [bookType]);

// 拿到 books 数据,渲染 UI

落地和优化

当我实现完这个工具的第一个可用版本,准备在项目中推行时,却发现还有一些问题亟待解决:

Q: 如何保证生成代码的一致性?
A: 每次运行命令都会从远程服务器上去获取 Swagger JSON,以保证数据来源的一致性。生成新的代码之后覆盖之前的文件即可。这样就能保证每个人生成的代码与当前服务器上的 API 是一一对应的。不过需要注意的是,生成的文件不能手动修改,否则修改最终会被覆盖。

Q: 如何快速得知 API 的变化?
A: 跟package-lock.json 一样,我们会对生成的代码进行排序,以减少生成文件的变化。重新运行命令之后,通过 git diff 就能准确得知 API 的变化。

Q: 如何进行多人协作?
A: 如果后端修改了一个字段名,可能会导致前端所有用到这个字段的地方都发生编译错误。这时如果大家都去修改编译问题,不仅可能产生冲突,还会造成时间的浪费。虽然这个问题在没有自动化工具之前一样存在,但仍然需要解决。好在这个问题发生的频率不高,我们可以和项目成员约定:如果有人正好在做这个功能,那么就由他来修改,否则就由指定的人去协调安排。通过自动化工具,可以更快地完成修改,从而减少阻塞别人工作的时间。

Q: 当后端进度落后于前端时,如何保证先有 Swagger 定义?
A: 由于前后端是两个独立的团队,所以进度也常常不同。后端可能无法先于前端实现好 API,甚至无法和前端同时开始去做一个功能。而这套方案依赖于 Swagger 定义,必须先有 Swagger 定义才能生成代码。如果前端要先于后端完成某个功能,必须先和后端商定好 API Schema 再进行开发。定义好 API Schema 之后,随之更新 Swagger 定义即可(后端无需实现具体功能)。

最后

自动化工具为前后端搭建了一座桥梁,当后端发生变动时,前端也能及时得知并做出相应修改。再也不用担心后端悄悄改接口了!到目前为止,自动化工具已经为我们项目生成了上万行代码。不仅提升了大家的效率,也减少了因为不遵循契约带来的隐藏 Bug。前端终于不用写大量模板代码了,集成 API 也变成了一件很容易的事情。

如果对代码实现感兴趣,可以移步这里:ts-codegen,或者看看我之前写的这篇文章:基于 React 和 Redux 的 API 集成解决方案

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