自动化生成前端API请求代码:基于YApi的高效开发实践

用 TypeScript 实现 API 方法代码生成工具

在现代前端开发中,API 的调用几乎是每个项目中不可或缺的一部分。为了提高开发效率和代码的可维护性,通常需要生成 API 请求的相关代码。然而,手写这些 API 方法可能会变得繁琐且容易出错。为此,我们可以利用工具自动生成 API 方法代码,确保代码的统一性和规范性。

今天,我们就来一起探讨如何通过 TypeScript 和一些流行的工具,快速生成高效的 API 请求代码,特别是在处理大型项目时,这种自动化方法能大大提升工作效率。

github:github.com/yangboxun/y...

背景与需求

在处理 API 接口时,我们通常会面临以下几个常见的需求:

  1. 接口参数动态生成:API 请求的参数往往是动态的,不同的接口可能有不同的请求类型(如 JSON、Form Data 等)。
  2. 代码重复:对于大量的接口方法,手动书写每个 API 的请求代码显得非常冗余,容易出错。
  3. 接口数据变化: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 自动化生成的理解。欢迎留言分享你的想法与经验!

相关推荐
代码CC5 分钟前
Vue.js+Element UI 登录界面开发详解【附源码】
前端·vue.js·ui·elementui
无名之逆7 分钟前
Hyperlane:Rust 语言打造的 Web 后端框架新标杆
开发语言·前端·网络·网络协议·rust·github·ssl
冰夏之夜影12 分钟前
【css酷炫效果】纯CSS实现悬浮弹性按钮
前端·css
shadouqi20 分钟前
4.angular 服务
前端·javascript·angular.js
JaxNext21 分钟前
成为谷歌开发者专家,也成为儿时心中的侠客
前端·程序员·开源
一个处女座的程序猿O(∩_∩)O32 分钟前
Webpack vs Rollup vs Parcel:构建工具深度对比
前端·webpack·devops
小鱼冻干1 小时前
express中间件
前端·mysql·node.js
范哥来了1 小时前
python web开发flask库安装与使用
前端·python·flask
顾林海1 小时前
Flutter Dart 泛型详解
android·前端·flutter
百慕大三角2 小时前
如何用AI工具设计出令人惊艳的页面(附截图)
前端·trae·ai 编程