通过pont自动化生成接口

1,056 阅读4分钟

前言

在我们日常开发的过程中,后端同学一般会提供Swagger UI 用于帮助前端发开同学查看接口文档编写,在最开始开始写前端项目的过程中(古老项目中),会在项目里面新建用于存放接口的文件,但是前后端文档的编写和维护会花费很大的成本。随着后面对于ts的使用 ,对应的service、interface等ts声明代码。如果后端接口都遵循RESTful 风格,那么我们可以通过一些工具或者自动化脚本,以提高开发者效率和 减少重复的工作。

利用pont自动化生成接口文档

Pont 把 swagger、rap、dip 等多种接口文档平台,转换成 Pont 元数据。Pont 利用接口元数据,可以高度定制化生成前端接口层代码,接口 mock 平台和接口测试平台。

其中 swagger 数据源,Pont 已经完美支持。并在一些大型项目中使用了近两年,各种高度定制化需求都可以满足。

详情查看:developer.aliyun.com/article/787…

快速开始

  • 1、在 vscode 中安装 vscode 插件 pont 以获取 pont 的全部能力。插件使用方法请参考:vscode-pont
  • 2、添加 pont-config.json

pont 一旦检测到有效的 pont-config.json 文件即可启动。pont 支持 pont start 命令快速生成 pont-config.json 配置文件。

全局安装 pont-engine

// npm
npm i -g pont-engine

// 或yarn
yarn global add pont-engine

在你的项目中运行 pont start 命令,按照提示输入配置完成接入。

Tips:

  • 确保服务端使用 Swagger(目前只支持 Swagger V1、V2、V3),提供的数据源接口是免登录的。如果不是,请后端帮忙简单配置一下,或者使用 fetchMethodPath 配置,通过自定义 fetch 方法获取带鉴权的接口的文档。
  • 若需替换默认的请求信息,请参阅pontCore

配置pont-config.json

  • originUrl: 接口平台提供数据源的 open api url(需要免登),目前只支持 Swagger。如 "petstore.swagger.io/v2/swagger.…"
  • outDir: 生成代码的存放路径,使用相对路径即可。如:"./src/api"
  • templatePath: 指定自定义代码生成器的路径(使用相对路径指定)。一旦指定,pont 将即刻生成一份默认的自定义代码生成器。自定义代码生成器是一份 ts 文件,通过覆盖默认的代码生成器,来自定义生成代码。默认的代码生成器包含两个类,一个负责管理目录结构,一个负责管理目录结构每个文件如何生成代码。自定义代码生成器通过继承这两个类(类型完美,可以查看提示和含义),覆盖对应的代码来达到自定义的目的。具体使用方法请参看自定义代码生成器文档
  • prettierConfig: 生成的代码会用 prettier 来美化。此处配置 prettier 的配置项即可,具体可以参考 prettier 文档
  • usingMultipleOrigins: pont 支持一个项目中配置多个 Swagger 来源。此处配置是否启用多数据源
  • origins: 配置每个数据来源
  • mocks:
    • 子字段:
      • 字段名:"enable" 类型:boolean 默认值: true 含义:是否生效
      • 字段名:"basePath" 类型:string 默认值:"" 含义:接口的 basePath
      • 字段名: "port" 类型:string 默认值:8080 含义:mocks 服务的端口号
      • 字段名 "wrapper" 类型:string 默认值:"{"code": 0, "data": {response}, "message": ""}" 含义:接口返回结构,pont 可以计算返回数据类型(比如此处会替换到 {response}),此处可以指定接口返回结构。
  • 等等..

例子:

{
  "usingMultipleOrigins": true,
  "origins": [
    {
      "name": "xxx1",
      "originUrl": "http://xxx1/api-docs"
    },
    {
      "name": "xxx2",
      "originUrl": "http://xxx2/api-docs"
    }
  ],
  "outDir": "./client/pont",
  "prettierConfig": {
    "semi": false,
    "singleQuote": true,
    "tabWidth": 2,
    "trailingComma": "all"
  },
  "mocks": {
    "enable": true,
    "port": 8081
  },
  "templatePath": "./client/pontTemplate"
}

一旦检测到项目中存在有效的 pont-config.json 配置文件,插件便会启动。你将在插件底部看到如下状态栏:

  • sync:重新请求远程接口数据,重新计算本地接口数据和远程接口数据在接口模块和接口返回对象(基类)上的差异。
  • all:将本地所有接口模块、接口返回对象与远程已更新数据保持一致。
  • mod:选择并同步本地接口模块,与远程接口数据保持一致。

配置pontTemplate

import { CodeGenerator, Interface } from 'pont-engine'

export default class MyGenerator extends CodeGenerator {
  /** 获取接口内容的类型定义代码 */
  getInterfaceContentInDeclaration(inter: Interface): string {
    const paramsCode = inter.getParamsCode()
    const bodyParamsCode = inter.getBodyParamsCode()
    const isGetRequest = inter.method.toLocaleLowerCase() === 'get'

    const requestParams = isGetRequest
      ? `params: Params`
      : `params: ${bodyParamsCode}`

    return `
      export ${paramsCode}

      export type Response = ${inter.responseType};
      export const init: Response;
      export function request(${requestParams}): Promise<${inter.responseType}>;
    `
  }
  
  /**
   * 获取所有模块的 index 入口文件  目前对应pont-engine的包以及插件 v1.5.2版本
   */
  getModsIndex() {
    let conclusion = `
      export {
        ${this.dataSource.mods.map(mod => mod.name).join(', \n')}
      };
    `

    // dataSource name means multiple dataSource
    if (this.dataSource.name) {
      conclusion = `
        export const ${this.dataSource.name} = {
          ${this.dataSource.mods.map(mod => mod.name).join(', \n')}
        };
      `
    }

    return `
      ${this.dataSource.mods
        .map(mod => {
          return `import * as ${mod.name} from './${mod.name}';`
        })
        .join('\n')}
 
      ${conclusion}
    `
  }

  /** 获取接口实现内容的代码 */
  getInterfaceContent(inter: Interface): string {
    // 根据内部使用习惯修改
    // 目前只存在 query 或 data 的传参方式,不存在两者并存的情况
    const { path } = inter
    // 增加 auth 服务调用逻辑
    const paramsCode = inter.getParamsCode()
    const isGetRequest = inter.method.toLocaleLowerCase() === 'get'

    const requestParamName = isGetRequest ? 'params' : 'data'

    const requestParams = isGetRequest ? `params: Params` : `data: Params`

    return `
      /**
       * @desc ${inter.description}
       */
      import * as defs from '../../baseClass';
      import axios from '@/services/request';

      export ${paramsCode}
      export const init = ${inter.response.getInitialValue()};
      export async function request(${requestParams}): Promise<any> {
        return axios({
           url: '${inter.getDsName()}${path}',
          ${requestParamName},
          method: '${inter.method}',
        });
      }
    `
  }
}

生成的接口文件

使用

引入使用
import { uploadImage } from '@pont/promote/common/uploadImage'

uploadImage(formData).then((res) => {
  console.log(res)
})
挂载全局使用

在pont/index.ts文件中

import { defs as xxxDefs, xxx } from './xxx'

import { defs as promoteDefs, promote } from './promote'

;(window as any).defs = {
  xxx: xxxDefs,
  promote: promoteDefs,
}
;(window as any).API = {
  xxx,
  promote,
}

在代码中使用

 const data = await API.promote.common.uploadImage.request(formData)

注:pont-engine v1.5.2版本的index.ts生成需要通过 getModsIndex来帮助生成

参考

项目地址:github.com/jianwuG/jw-…