使用Koa+node.js配合后端swagger一键生成API的js文件

简介

因为后端会把接口用swagger去生成一个可视化接口页面,前端每次去调用的时候都要去手写接口的api文件,所以本文将介绍如何使用nodejs去通过swagger生成的可视化页面自动创建接口api文件

1、首先使用Koa和koa-router去创建一个服务

createApi-server.js

// 因为我们需要去创建文件所以引入fs
const fs = require('fs')
const Koa = require('koa')
const Router = require('koa-router')
// 因为会需要调用接口 所以引入axios
const axios = require('axios')

const app = new Koa()
const router = new Router()
const port = 369

app.use(router.routes()).use(router.allowenMethods())
app.listen(port, async () => {
  console.log(`http://localhost:${port}/ is running server`)
})

2、创建一个文件夹用来展示调用swagger接口之后的结果 element-plus和axios、vue3。这3个是我直接拿的框架源码

createApi
│
└───page
│   │   index.html
│   │   axios.min.js
│   │   element-plus.css
│   │   element-plus.js
│   │   index.js
│   │   vue3.js
│   
└───util
    │   index.js

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="./vue3.js"></script>
    <link rel="stylesheet" href="./element-plus.css" />
    <script src="./element-plus.js"></script>
    <script src="./axios.min.js"></script>
    <title>一键创建api文件</title>
    <style>
      html,
      body {
        position: relative;
        overflow: hidden;
        margin: 0;
        width: 100vw;
        height: 100vh;
      }
      body {
        display: flex;
        justify-content: center;
        align-items: center;
        font-family: 'Helvetica';
        color: hsla(0, 0%, 0%, 0.618);
      }
      #app {
        display: flex;
        flex-direction: column;
        width: 520px;
        height: 520px;
        /* background: #eee; */
      }
      .header {
        margin-bottom: 30px;
      }
      .header .select {
        margin-right: 20px;
        width: 250px;
      }
      #app .content {
        position: relative;
        box-sizing: border-box;
        flex: 1;
        padding-bottom: 30px;
      }
      .group {
        padding: 0 12px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="header">
        <el-select
          class="select"
          placeholder="请选择你需要创建的接口名称"
          filterable
          v-model="checkTag"
        >
          <el-option
            v-for="item in tags"
            :label="item.name"
            :value="item.name"
          ></el-option>
        </el-select>
        <el-button type="primary" size="mini" @click="creatFile"
          >创建文件</el-button
        >
      </div>
      <div class="content">
        <el-checkbox
          v-for="item in pathList"
          :key="item.path"
          v-model="checkPaths"
          :label="item.path"
          >{{
          `${item.method}&nbsp;&nbsp;${item.path}&nbsp;&nbsp;${item.summary}`
          }}</el-checkbox
        >
      </div>
    </div>
    <script type="module">
      import './index.js'
    </script>
  </body>
</html>

index.js

;(async ({ Vue, ElementPlus, axios }) => {
  // console.log(window);
  const App = {
    setup() {
      const { toRefs, shallowReactive, onMounted, watch } = Vue
      const { ElLoading, ElMessage } = ElementPlus
      const state = shallowReactive({
        // 接口属性
        paths: {},
        // 下拉框
        tags: [],
        // 选中的tag
        checkTag: '',
      })
      // 获取api-doc文档
      const getApiDocs = async () => {
        const loadingInstance = ElLoading.service({
          fullscreen: true,
          text: '正在获取数据...',
          background: 'rgba(255, 255, 255, .1)',
        })
        const url = '/api'
        try {
          const { data } = await axios.get(url)
          state.tags = data.tags
          state.paths = data.paths
        } catch (e) {
          ElMessage.error({
            message: '服务中断或发生异常!',
          })
        }
        loadingInstance.close()
      }
      // 获取Path
      watch(
        () => state.checkTag,
        async (v) => {
          state.pathList = await getCheckTag()
        }
      )
      const creatFile = () => {
        // 创建文件方法      
      }
      onMounted(async () => {
        getApiDocs()
      })
      return {
        ...toRefs(state),
        creatFile
      }
    },
  }
  const app = Vue.createApp(App)
  app.use(ElementPlus)
  app.mount('#app')
  window.App = App
})(window)

3、使用koa-static配置静态目录为刚刚写好的index.html,并且创建api接口

createApi-server.js

const KoaStatic = require('koa-static')
...
const port = 369
app.use(koaStatic(`${process.cwd()}/createApi/page`))

router.get('/api', async (ctx, next) => {
  // api文档地址
  const url = 'swagger的api-docs的url'
  const res = await axios(url)
  ctx.body = res.data
  await next()
})
...

4、接下来起一下服务先看看页面效果

由于我是直接在vuecli项目写的这个功能,所以我直接在package.json里面加了一个命令 "createApi": "nodemon createApi-server.js"

效果图

至于为什么要用多选框,我是为了以后加功能便捷而创建的,这个可以忽视

5、页面 接口都联调好了,现在就是开始创建文件了,首先打开createApi/util/index.js里面写创建文件的方法

/util/index.js

const fs = require('fs')
const path = require('path')

// 创建文件之前 首先需要判断 该路径是否存在此文件
/**
 * @desc 判断该路径是否存在此文件
 * @param {string} dirname 路径
 * @author deng
 */
function hasFile(dirname) {
  return new Promise(function (resolve, reject) {
    fs.access(dirname, (err) => {
      if (err) {
        // 没有文件
        resolve(false)
      } else {
        // 有该文件
        resolve(true)
      }
    })
  })
}

// 我这边使用了比较暴力的手法,如果有该文件,直接替换掉

/**
 * @desc 创建文件
 * @param {string} dirname 文件名
 * @param {*} data 文件内容
 * @author deng
 */
async function creatFile(dirname, data) {
  // 先删除同名文件
  if (await hasFile(dirname)) {
    console.log('删除原文件')
    await fs.rmSync(dirname)
  }
  fs.writeFile(dirname, data, (err) => {
    if (err) {
      console.error('创建失败', err)
    }
    console.log(`创建成功-${dirname}`)
  })
}

/**
 * @desc 写入api文件
 * @param {Array} pathList 接口API的集合
 * @author deng
 */
function createApiFile(pathList) {
  // 路径名
  const dirname = `./src/api/${pathList[0].path.split('/')[1]}.js`
  // 这是我自己在项目封装的axios方法
  let file = `import request from '@/utils/request' 
  `
  pathList.forEach((v) => {
    const { path, method, summary } = v
    // 方法名
    const name = path.split('/').join('')
    fucList.push(name)
    // 只有get请求才会使用params参数
    const data = method === 'get' ? 'params' : 'data'
    file += `
export function ${name}(${data}){
  return request({
    url: '${path}',
    method: '${method}',
    desc: '${summary}',
    ${data}
  })
}
`
  })
  creatFile(dirname, file)
}

module.exports = {
  creatFile,
  createApiFile
}

创建文件的方法已经封装好了, 那就直接在createApi-server.js去写接口吧
使用koa-bodyparser插件来接收参数

createApi-server.js

const bodyParser = require('koa-bodyparser')

const { createApiFile } = require('./createApi/util')
app.use(bodyParser())

// 创建api
router.post('/writeAPIFile', async (ctx, next) => {
  const { pathList } = ctx.request.body
  await createApiFile(pathList)
  ctx.body = { code: 0, msg: '创建成功!' }
  await next()
})

再去page/index.js里面调用创建文件的api

/page/index.js

// 之前创建的createFile方法里面调用接口
const creatFile = async () => {
  const { pathList } = state
  try {
    const res = await axios.post('/writeAPIFile', {
      pathList,
    })
    if (res.code === 0) {
      ElMessage.success({
        message: '创建成功!',
      })
    }
  } catch (e) {
    ElMessage.error({
      message: '文件异常!',
    })
  }
}

启动服务 试一下创建是否成功

创建成功

就这样就ok了 如果还想一起创建其他的文件也可以按照案例写,但是如果需要同时创建的话 我建议使用async await或者promise来做这样的话不会出问题

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

推荐阅读更多精彩内容