开发的时候常常需要和后端对接接口,虽然可以在实际使用的地方直接通过request(url)方式调用,但是这样的话api常常散落在各个地方,不方便我们对api进行管理,所以我们常常会在一个js文件中统一管理api,但是如果我们一个个的去写请求函数,又显得费时费力,所以这边还是用程序来自动生成
构建步骤
- 将离线json导出为文件,也可用request等node包动态获取json数据
- 分析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的时候会显示请求函数的说明和参数列表
本函数中的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
}