用 TypeScript 实现 API 方法代码生成工具
在现代前端开发中,API 的调用几乎是每个项目中不可或缺的一部分。为了提高开发效率和代码的可维护性,通常需要生成 API 请求的相关代码。然而,手写这些 API 方法可能会变得繁琐且容易出错。为此,我们可以利用工具自动生成 API 方法代码,确保代码的统一性和规范性。
今天,我们就来一起探讨如何通过 TypeScript 和一些流行的工具,快速生成高效的 API 请求代码,特别是在处理大型项目时,这种自动化方法能大大提升工作效率。
github:github.com/yangboxun/y...
背景与需求
在处理 API 接口时,我们通常会面临以下几个常见的需求:
- 接口参数动态生成:API 请求的参数往往是动态的,不同的接口可能有不同的请求类型(如 JSON、Form Data 等)。
- 代码重复:对于大量的接口方法,手动书写每个 API 的请求代码显得非常冗余,容易出错。
- 接口数据变化:API 接口可能会随时发生变化,如何保证 API 请求代码与接口定义的同步?
解决方案:API 代码生成工具
我们可以创建一个 API 代码生成工具,根据从 API 文档中获取的接口信息,自动生成对应的 API 请求方法。以下是实现过程的详细分析。
1. 工具结构和配置
首先,我们需要定义一个配置对象 ApiGeneratorOptions
,包括 API 的基本信息、请求 token、目标语言等。
csharp
interface ApiGeneratorOptions {
baseUrl: string, // API 基础 URL
token: string, // API token
targetLanguage: string, // 目标编程语言,支持 JavaScript 和 TypeScript
apiTemplate?: string // 自定义 API 模板
}
2. 获取 API 列表
通过发送 GET 请求从远程 API 获取接口列表,并以 JSON 格式返回。我们利用 axios
来处理 HTTP 请求。
javascript
async fetchApiList() {
try {
const { data } = await axios.get(`${this.opts.baseUrl}/api/plugin/export`, {
params: {
type: 'json',
status: 'all',
isWiki: 'false',
token: this.opts.token
},
})
return data || []
} catch (error) {
console.error('获取接口数据时出错:', error)
throw error;
}
}
3. 生成 API 方法代码
通过 ejs
模板引擎,我们可以动态生成 API 请求代码。在这里,我们为每个接口生成一个 API 请求方法,包括请求类型、路径、请求参数和响应类型等。
ini
// 生成 API 方法代码
async generateApiMethods(apiList: ApiInterface[]) {
let apiMethods = [],
tsTypes = []
let defaultApiTemplate =
`
/**
* <%= title %>
* @method <%= method %>
* @path <%= path %>
*/
export async function <%= methodName %>_<%= method.toLowerCase() %>(params){
return request({
url: '<%= path %>',
method: '<%= method.toLowerCase() %>',
data: params
})
}
`
if(this.opts.targetLanguage === 'typescript') {
defaultApiTemplate = `
/**
* <%= title %>
* @method <%= method %>
* @path <%= path %>
*/
<%= requestTypeDef %>
<%= responseTypeDef %>
export async function <%= methodName %>_<%= method.toLowerCase() %>(params:<%= requestType %>){
return request<<%= responseType %>>({
url: '<%= path %>',
method: '<%= method.toLowerCase() %>',
data: params
})
}
`
}
for(let api of apiList) {
let requestJsonSchema:any
const { path, method, title, req_body_other, res_body, req_body_type, req_body_form, req_query,req_params } = api;
const methodName = path
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_+/g, '_')
.replace(/^_|_$/g, '');
if(isPostMethod(method)) {
switch(req_body_type) {
case 'form':
requestJsonSchema = convertToJsonSchema(req_body_form)
break
case 'json':
if (req_body_other) {
requestJsonSchema = JSON.parse(req_body_other)
if (requestJsonSchema.type === 'object') {
// 将 additionalProperties 设为 false
requestJsonSchema.additionalProperties = false
}
}
break
default:
break
}
}
// 处理查询数据
if(req_query && req_query.length>0) {
const queryJsonSchema = convertToJsonSchema(req_query)
if(requestJsonSchema) {
requestJsonSchema.properties = {
...requestJsonSchema.properties,
...queryJsonSchema.properties,
}
requestJsonSchema.required = [
...(Array.isArray(requestJsonSchema.required) ? requestJsonSchema.required : []),
...(Array.isArray(queryJsonSchema.required)
? queryJsonSchema.required
: []),
]
} else {
requestJsonSchema = queryJsonSchema
}
}
// 处理路径参数
if (req_params && req_params.length) {
const paramsJsonSchema = convertToJsonSchema(req_params)
if(requestJsonSchema) {
requestJsonSchema.properties = {
...requestJsonSchema.properties,
...paramsJsonSchema.properties,
}
requestJsonSchema.required = [
...(Array.isArray(requestJsonSchema.required) ? requestJsonSchema.required : []),
...(Array.isArray(paramsJsonSchema.required)
? paramsJsonSchema.required
: []),
]
} else {
requestJsonSchema = paramsJsonSchema
}
}
const requestType = changeCase.pascalCase(`Request_${methodName}`);
const responseType = changeCase.pascalCase(`Response_${methodName}`);
const requestTypeDef = await jsonSchemaToTypes(JSON.stringify(requestJsonSchema), requestType);
const responseTypeDef = await jsonSchemaToTypes(res_body, responseType);
const apiCode = ejs.render(this.opts?.apiTemplate || defaultApiTemplate, {
title,
method,
path,
methodName,
requestTypeDef,
responseTypeDef,
requestType,
responseType,
})
const tsTypesCode = `
${requestTypeDef}
${responseTypeDef}
`
apiMethods.push(apiCode)
if(this.opts.targetLanguage === 'typescript') {
tsTypes.push(
tsTypesCode
);
}
}
const apiFormatCode = await formatCode(apiMethods.join('\n'))
const typesFormatCode = await formatCode(tsTypes.join('\n'))
return {
codes: apiFormatCode,
tsTypes: typesFormatCode
}
}
4. 生成最终代码
最后,我们通过调用 generateCode
方法来整合所有 API 接口,并生成最终的 API 请求代码。
javascript
async generateCode() {
const apiList: Category[] = await this.fetchApiList()
if (apiList.length === 0) {
console.error('未找到 API 数据')
return;
}
for (let apis of apiList) {
apis._apiCodes = await this.generateApiMethods(apis.list)
}
return apiList
}
5. 代码格式化
我们利用 formatCode
方法对生成的代码进行格式化,以保持代码风格的一致性。这个方法可以保证生成的代码符合团队的规范要求。
csharp
// 格式化生成的代码
export function formatCode(code: string): Promise<string> {
const options = {
parser: 'typescript',
singleQuote: true,
semi: true,
};
return prettier.format(code, options);
}
const apiFormatCode = await formatCode(apiMethods.join('\n'))
const typesFormatCode = await formatCode(tsTypes.join('\n'))
最终效果
经过上述步骤,我们可以得到按需生成的 API 请求方法代码。例如,生成的一个 API 方法如下:
php
/**
* 获取用户信息
* @method GET
* @path /user/{userId}
*/
export async function get_user(params: { userId: string }) {
return request({
url: '/user/' + params.userId,
method: 'get',
data: params
})
}
同时,我们也会生成与之相关的 TypeScript 类型定义:
typescript
export interface Request_get_user {
userId: string;
}
export interface Response_get_user {
id: string;
name: string;
email: string;
}
总结
通过本工具,我们可以实现 API 请求代码的自动化生成,极大地减少了手动编写的工作量,也能确保代码的一致性和准确性。特别是在面对多个接口和复杂的 API 请求时,这种自动化生成工具可以显著提高开发效率,降低出错率。
如果你正面临相似的需求,不妨尝试将这种自动化生成方法引入到你的开发流程中,相信会让你的工作变得更加高效!
希望这篇文章对你有所帮助,提升了你对 API 自动化生成的理解。欢迎留言分享你的想法与经验!