将openapi离线文档转换成可调用api函数,提升开发效率(js版)

197 阅读2分钟

开发的时候常常需要和后端对接接口,虽然可以在实际使用的地方直接通过request(url)方式调用,但是这样的话api常常散落在各个地方,不方便我们对api进行管理,所以我们常常会在一个js文件中统一管理api,但是如果我们一个个的去写请求函数,又显得费时费力,所以这边还是用程序来自动生成

构建步骤

  1. 将离线json导出为文件,也可用request等node包动态获取json数据
  2. 分析json文件后根据openapi的json数据生成apis函数

openapi.json是一个json对象,其中有几个重要属性,tags,paths,definitions

  • tags: 这是所有的模块标签列表,是一个名称列表
  • paths:这是所有的接口列表,一个对象,包含所有接口url
  • definitions:这是接口所用到的所有传参对象,一般在post和put中用到

生成api函数列表的方式就是遍历paths的所有url,由于是对象类型,所以可以用for...in或者是Object.keys进行遍历。restful接口有几种主要的接口调用方式,分别是post,delete,put,get(增删改查),对于post与put来说,一般都是使用对象形式传参的,而get与delete就只需要取paths中对应的parameters列表参数,用concatParams函数来拼接参数,对于parameters中有schema.originalRef(即对象类型参数),就取definitions中的参数做传参

实现代码(以2.0为例)代码完整,可直接使用


const fs = require("fs")
const fs_promise = fs.promises
const path = require("path")

function gen (tags, api_paths, definitions) {
 let files = []
 for (let i = 0; i < tags.length; i++) {

  files.push({
   tag: tags[i].name,
   str: "",
   funcs: [],
  })
 }
 for (let path_key in api_paths) {
  for (let method_key in api_paths[path_key]) {
   const api_obj = api_paths[path_key][method_key]
   const _file = files.find(item => item.tag === api_obj.tags[0])
   const isPostOrPut = ['post', 'put'].includes(method_key)
   if (_file) {
    let zhushi = ''
    if(Array.isArray(api_obj.parameters)) {
     api_obj.parameters.forEach(item3 => {
      if(item3.schema &&    item3.schema.originalRef) {
       if(definitions[item3.schema.originalRef]) {
        const properties = definitions[item3.schema.originalRef].properties
        if(properties) {
         for(let pkey in properties) {
          const item12 =properties[pkey]
          zhushi += '* @' + pkey + ' ' + item12.type + ' ' + item12.description + '\n'
         }
        }
       }
      } else {
       zhushi += '* @' + item3.name + ' ' + item3.type + ' ' + item3.description + '\n'
      }
     })
    }

    _file.str = _file.str + `
    /**     
     *   ${api_obj.summary}     
    ${zhushi}
    */
      export async function ${api_obj.operationId}(data) {
       return Vue.prototype.$u.${method_key}("${path_key}"${isPostOrPut ? ',data' : '+concatParams(data)'})
      }
     `
    _file.funcs.push(api_obj.operationId)
   }
  }
 }

 return files
}

async function exportAll (data, outputdir) {
 try {
  await fs_promise.stat(outputdir)
 } catch (err) {
  await fs_promise.mkdir(outputdir)
 }

 let jsFileString = `
 import Vue from "vue"
 import {concatParams} from "@/utils/url"
`

 const api = data
 const tags = api.tags
 const api_paths = api.paths
 const definitions = api.definitions

 const files = gen(tags, api_paths, definitions)
 let exportStr = ''

files.forEach(item => {
  exportStr += `
  /*****************************************        ${item.tag}           **********************************************************/
  ${item.str}
  `
})

 fs_promise.writeFile(path.resolve(outputdir, "index.js"), `
 ${jsFileString}
 ${exportStr}
 `)
}

exportAll(require("./default_OpenAPI (9).json"), path.resolve(__dirname, "../src/apis"))

在实际调用api的时候会显示请求函数的说明和参数列表 image.png

本函数中的post和put方法 均使用 body方式json类型传参,而get与delete均使用query方式传参

不过,本函数暂时不支持url path参数形式,例如/page/:id形式,这种形式的参数可用正则进行填充,具体可自行尝试

concatParams函数是一个将obj对象转换成'?xxx=xxx&yyy=yyy的函数,如果需要的话可以把函数集成到apis/index.js代码中,代码如下



export const concatParams = params => {
	const tempParams = {}
	for(const key in params) {
		if(params[key] !== undefined) {
			tempParams[key] = params[key]
		}
	}
	const keys = Object.keys(tempParams)
	let paramStr = ''
	if(keys && keys.length > 0) {
		keys.forEach((item, index) => {
			if(index === 0) {
					paramStr += `?${item}=${params[item]}`
			} else {
					paramStr += `&${item}=${params[item]}`
			}
		})
	}
	return paramStr
}