释放双手,优雅地调用后端接口💗

简介: 释放双手,优雅地调用后端接口💗

image.png

大家好,我是速冻鱼🐟,一条水系前端💦,喜欢花里胡哨💐,持续沙雕🌲,是隔壁寒草🌿的好兄弟,刚开始写文章。 如果喜欢我的文章,可以关注➕点赞,为我注入能量,与我一同成长吧~


阅读本文🦀



1.如何自动生成基于TypeScript的API模块

2.如何释放业务同学的双手,让其更专注于业务实现

3.您将了解到什么是Swagger、OpenAPIStopLight

4.您将了解到遇到一个完全没接触过的需求,如何快速圆满完成任务

5.如何编写CLI工具,完成自动化操作


前言🌵



您是否在编写前端API请求模块的时候,一边看文档一边编写前端请求接口呢?编写各种TS泛型类型定义,导致开发效率低下,遇到后端接口改变时,又不得不手动去修改请求接口🕶


如何解决这类工程化的问题呢?我们可以利用定义接口的标准API文档,自动生成前端基于TypeScriptAPI调用模块,这样我们甚至可以不用去阅读API接口文档就能直接上手开发业务,下面一起一探究竟吧~


前置知识🐳



  • OpenAPI
    人话就是一个定义API的规范
  • OpenAPI规范是定义REST api结构和语法的标准格式。OpenAPI文档是机器和人类可读的,这使得任何人都可以轻松地确定每个API的工作方式。构建api的工程师可以使用api来规划和设计服务器、生成代码和实现合同测试。
  • Swagger


image.png

  • 人话按照OpenAPI规范生成接口文档的工具
    Swagger是与实现OpenAPI规范的一些最知名、最广泛使用的工具相关的名称。Swagger工具集包括开源、免费和商业工具的组合,可以在API生命周期的不同阶段使用
  • StopLight

image.png

  • 人话就是StopLight是和Swagger一样的按照OpenAPI规范生成接口文档的工具,它需要付费,总之就是更强更好用~~
  • stopLight首先是一个API设计管理工具,可以使用OpenAPI等行业标准来设计、开发、测试和记录HTTP API,并使用Markdown来记录书面形式的文档。
  • API规范文件


image.png

明确需求🐿



  • 编写一个CLI工具让前端只需要执行一行命令就能做到开箱即用的API请求模块
  • 拉取StopLight的规范文件YML或者JSON格式
  • 根据文范文件在node_modules文件夹生成API请求模块,并将生成的文件进行整合,统一导出
  • 可以根据配置文件选择生成不同版本的API请求模块
  • 需要兼容Swagger2.0和OpenAPI3.0规范
  • 编写项目README文档


实现需求🐰



对核心功能进行调研


核心功能就在于如何生成基于TypeScript的API模块?


可以自动动手写模版引擎去生成代码,但是这个工作量太大了,而且我的水平不够,所以只有依赖于第三方库。


通过大量查阅Github和资料,总结出以下的方案


1.使用官方的Swagger-codegen


github.com/swagger-api…


要么生成工具是一个JAR包,要么就是用JS写的库文件很久没人维护了,所以放弃


2.free-swagger


github.com/yeyan1996/f…


国人写的,但是start数太少,生成的代码类型丢失为any,放弃


3.ts-gear


github.com/superwf/ts-…


京东莫大佬写的,但是在我的使用中发现对OpenAPI3支持的不好,放弃


4.swagger-typescript-api


github.com/acacode/swa…


外国友人写的,这个项目有600多start,代码都是使用模版生成的,但是文档写的很烂,但是实现效果还不错,最后还是采用的这个库,不过踩了很多坑

编写CLI入口文件 🍓

指定入口

package.json

{
  //...
  "bin": {
    "heimdall": "bin/heimdall.js"
  }
 //...
}

拉取规范文件 🍎


StopLight会将API接口文档托管在Github,因此我们需要在CLI实现拉取规范文件

以下省略了部分代码


//...
//初始化命令行帮助信息,并获取命令行参数
const options = getCommandOptions()
//生成API入口
if (options.generate) {
    const projectName = getProjectName()
    //1.执行下载文件命令
    await gitCloneProject(projectName)
    //2.生成api文件
    //2.1删除之前下载过的API文件
    await removeDir(path.resolve(cwd(), "node_modules/@imf/heimdall-ts/api"))
    await createApi()
    //3.生成入口文件
    await generateMain()
    //4.删除下载的yml所在文件夹
    await removeDir(path.resolve(cwd(), getProjectName()))
} else if (options.log) {
    const projectName = getProjectName()
    //1.执行下载文件命令
    await gitCloneProject(projectName, true)
    //2.执行打印日志的命令
    await showLog()
    //3.删除下载的文件夹
    await removeDir(path.resolve(cwd(), getProjectName()))
}
//...
/**
 * 克隆项目
 */
function gitCloneProject(projectName, isLog = false) {
    return new Promise<void>((resolve, reject) => {
        shell.exec(`git clone https://sudongyu:YUANCxzeJwiVhzQio18v@git.stoplight.io/floozy/${projectName}.git`, {
            cwd: `${cwd()}`
        }, () => {
            const versionCode = getPkgMaifest()?.heimdall?.versionCode
            //如果有versionCode,需要回退版本
            if (!isLog && versionCode) {
                shell.exec(`git checkout ${versionCode}`, {
                    cwd: `${path.resolve(cwd(), getProjectName())}`
                }, () => {
                    resolve()
                })
            } else {
                resolve()
            }
        })
    })
}

生成基于TS的API调用模块 🍌


使用 swagger-typescript-api这个库来生成代码,并将其放置在node_modules文件夹下,这样不会影响使用方的代码目录结构


/**
 * 创建api文件
 */
 function createApi() {
     return new Promise<void>(async (resolve, reject) => {
         //V3
         /* NOTE: all fields are optional expect one of `output`, `url`, `spec` */
         const openApi3Array = getOenAPI3YmlFileName(path.resolve(cwd(), `${getProjectName()}`))
         for (let item of openApi3Array) {
             await generateApi({
                 name: `${item.replace('.oas3.yml', '')}Api.ts`,
                 url: null,
                 output: path.resolve(process.cwd(), "node_modules/@imf/heimdall-ts/api"),
                 input: path.resolve(process.cwd(), `${getProjectName()}`, `${item}`),
                 httpClientType: "axios", // or "fetch",
                 unwrapResponseData: true,
                 generateUnionEnums: true,
                 enumNamesAsValues: true,
                 moduleNameFirstTag: false,
                 moduleNameIndex:-1
             })
         }
         //V2
         /* NOTE: all fields are optional expect one of `output`, `url`, `spec` */
         const openApi2Array = getOenAPI2YmlFileName(path.resolve(cwd(), `${getProjectName()}`))
         for (let item of openApi2Array) {
             await generateApi({
                 name: `${item.replace('.oas2.yml', '')}Api.ts`,
                 url: null,
                 output: path.resolve(process.cwd(), "node_modules/@imf/heimdall-ts/api"),
                 input: path.resolve(process.cwd(), `${getProjectName()}`, `${item}`),
                 httpClientType: "axios", // or "fetch",
                 unwrapResponseData: true,//是否包裹response
                 generateUnionEnums: true,
                 enumNamesAsValues: true,
                 moduleNameFirstTag: false,
                 moduleNameIndex:-1 //模块名分割
             })
         }
         if(!openApi3Array.length&&!openApi2Array.length){
             await removeDir(path.resolve(cwd(), getProjectName()))
             reject('no openApi3 or openApi2 resources to generate !!!!!')
         }
            resolve()
     })
}

整合并导出 🥝



由于 swagger-typescript-api这个库生成的文件是单个单个的,我们需要将生成的文件整合起来,利用正则表达式和模版字符串生成库入口文件

/**
 * 生成入口文件index.ts
 */
function generateMain() {
    return new Promise<void>((resolve, reject)=>{
        //获取文件名
        const fileNames = getFileName(path.resolve(cwd(), 'node_modules/@imf/heimdall-ts/api'))
        //转换文件名 eg:  main.ts -> MainGameApi
        const transformedFileNames = fileNames.map(item => {
            return transformToCamel(item)
        })
        //编写要写如的内容content
        const content = `
        ${transformedFileNames.map((item, index) => {
                return `import \{Api as ${item}\} from \'\.\/${fileNames[index]}\'\n`
            }).join('')
        }
   export {
        ${transformedFileNames.map(item => {
            return `${item}\n`
        })}
    }
   `
        //创建index文件
        generateFile(path.resolve(cwd(),'node_modules/@imf/heimdall-ts/api','index.ts'))
        //写入文件
        writeFile(path.resolve(cwd(),'node_modules/@imf/heimdall-ts/api','index.ts'),content).then(()=>{
            resolve()
        })
    })
}

支持版本回退 🍉

本来基本功能都实现了,但是老大又提了新需求,需要支持版本回退,这样调用方就可以方便得管理自己的API模块了

  • 实现查看API文档版本号,这里我的解决方案就是利用git版本管理工具的命令可以查看日志来实现
/**
 * 打印版本stoplight版本信息
 */
function showLog(){
    return new Promise<void>((resolve, reject)=>{
        shell.exec('git log --pretty=" %h %ci %s "', {
                cwd: `${path.resolve(cwd(), getProjectName())}`
            },()=>{
            resolve()
            }
        )
    })
}
  • 执行git checkout命令切换到对应版本的仓库,然后根据这个文档再生成API模块,这样就实现了版本回退


function gitCloneProject(projectName, isLog = false) {
    return new Promise<void>((resolve, reject) => {
        shell.exec(`git clone https://sudongyu:YUANCxzeJwiVhzQio18v@git.stoplight.io/floozy/${projectName}.git`, {
            cwd: `${cwd()}`
        }, () => {
            const versionCode = getPkgMaifest()?.heimdall?.versionCode
            //如果有versionCode,需要回退版本
            if (!isLog && versionCode) {
                shell.exec(`git checkout ${versionCode}`, {
                    cwd: `${path.resolve(cwd(), getProjectName())}`
                }, () => {
                    resolve()
                })
            } else {
                resolve()
            }
        })
    })

编写文档 📃


一个库代码逻辑写好后,最重要的还有文档编写啦😁,写上好看易懂的文档,这个库就算大功告成了


image.png

项目效果展示 🪖


自动生成的代码

image.png

从自动生成的API模块库中导入要使用的API(智能的提示)

image.png

  • 使用API模块发送请求


由于生成的TS,所以有很好的代码提示,能直接点出来想调用的接口

image.png

所有数据都拥有对应的TS类型

image.png

收获🍁


编写这个库最大的收获其实是遇到一个完全不懂的需求,如果去解决问题呢?当使用的第三方库不能满足你的需求怎么办呢。我学到的就是先从结果出发,理清思路,一步步去实现每个步骤,当遇到阻碍的时候,多去看看第三方库的Github上的issue或者阅读源码,或许就能很好的解决问题。当然还学习到了如何优雅地调用后端接口,帮助业务同学提高开发效率😂~

相关文章
|
4月前
|
SQL JSON Java
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
本文为Spring Boot增删改查接口的小白入门教程,介绍了项目的构建、配置YML文件、代码编写(包括实体类、Mapper接口、Mapper.xml、Service和Controller)以及使用Postman进行接口测试的方法。同时提供了SQL代码和完整代码的下载链接。
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
|
5月前
|
Java 程序员 编译器
作为后端开发,感受下接口带来的魅力!🔥🔥
在一场大厂面试中,小南被问及何时选用接口,何时采用抽象类。一位拥有24K粉丝的博主给出解答:选择取决于需求——接口定义多个类需遵守的契约,而抽象类则用于创建共享实现的基类。有时两者结合使用,通过抽象类实现接口以确保契约并提供通用功能。此外,小南还分享了关于抽象类的深入探讨,包括子类如何正确调用父类构造函数、访问父类成员以及父类不可访问的方法等细节。最后,他提出一个关于接口与抽象类差异的小测验,并邀请读者关注他的开源项目《Java学习进阶指南》,旨在帮助Java开发者更好地掌握核心知识和面试要点。
191 3
作为后端开发,感受下接口带来的魅力!🔥🔥
|
5月前
|
小程序 JavaScript Java
微信小程序+SpringBoot接入后台服务,接口数据来自后端
这篇文章介绍了如何将微信小程序与SpringBoot后端服务进行数据交互,包括后端接口的编写、小程序获取接口数据的方法,以及数据在小程序中的展示。同时,还涉及到了使用Vue搭建后台管理系统,方便数据的查看和管理。
微信小程序+SpringBoot接入后台服务,接口数据来自后端
|
6月前
|
存储 开发框架 前端开发
循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合
循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合
|
5月前
|
存储 缓存 关系型数据库
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
Django后端架构开发:缓存机制,接口缓存、文件缓存、数据库缓存与Memcached缓存
94 0
|
6月前
|
运维 前端开发 API
运维,如何快速查找后端的接口
运维,如何快速查找后端的接口
|
6月前
|
前端开发 应用服务中间件 nginx
网页设计,若依项目修改(It must be done)01----若依打包位置,nginx代理前端静态资源和后端接口,就是怎样设置转载,访问固定端口,让他访问其他资料的配置文件,访问/,给你那些
网页设计,若依项目修改(It must be done)01----若依打包位置,nginx代理前端静态资源和后端接口,就是怎样设置转载,访问固定端口,让他访问其他资料的配置文件,访问/,给你那些
|
5月前
|
开发框架 前端开发 API
使用代码生成工具快速开发应用-结合后端Web API提供接口和前端页面快速生成,实现通用的业务编码规则管理
使用代码生成工具快速开发应用-结合后端Web API提供接口和前端页面快速生成,实现通用的业务编码规则管理
|
7月前
|
JSON 前端开发 Java
一文读Web开发 之接口后端接口、类与前端请求、拦截器编写
一文读Web开发 之接口后端接口、类与前端请求、拦截器编写
234 6
|
6月前
|
开发框架 前端开发 关系型数据库
使用egg.js开发后端API接口系统 什么是Egg.js
使用egg.js开发后端API接口系统 什么是Egg.js

热门文章

最新文章