微信小程序使用 Orval + OpenAPI 自动生成接口:Axios 兼容踩坑记录

微信小程序使用 Orval + OpenAPI 自动生成接口:Axios 兼容踩坑记录

最近在做微信小程序时,希望尽量减少手写接口代码。后端本身已经提供了 OpenAPI 文档,所以第一反应就是直接使用 Orval 根据 OpenAPI 自动生成 TypeScript 类型和请求代码。

Orval 本身确实很好用,问题出在它生成的 Axios 请求代码无法直接运行在微信小程序环境里。最后通过 axios-miniprogram 加一层 Axios API 兼容层解决了这个问题。

1. 安装 Orval 和 Axios

先安装 Orval 和 Axios:

复制代码
npm install axios

npm install -D orval

Orval 可以直接根据 OpenAPI Schema 生成 TypeScript 类型以及接口请求代码。官方也支持 Axios Client。

2. 配置 Orval

项目根目录创建:

arduino 复制代码
orval.config.ts

配置如下:

php 复制代码
import { defineConfig } from 'orval';

export default defineConfig({
  api: {
    input: {
      target: './openapi.json',
    },

    output: {
      target: './miniprogram/api/generated/api.ts',
      schemas: './miniprogram/api/generated/models',
      client: 'axios',
      httpClient: 'axios',
    },
  },
});

3. 生成 API 代码

直接执行:

复制代码
npx orval

运行 Orval 后,会生成类似这样的目录:

markdown 复制代码
miniprogram/
└── api/
    └── generated/
        ├── api.ts
        └── models/
            ├── user.ts
            ├── loginRequest.ts
            └── ...

到这里一切都很正常。

接口方法、请求参数、返回值类型全部可以直接根据后端 OpenAPI 自动生成。

这样后端接口发生变化以后,只需要重新获取:

复制代码
openapi.json

然后重新:

arduino 复制代码
npm run api

即可同步客户端代码,不再需要手动维护大量 Interface 和请求函数。

4. 第一个坑:Axios 无法直接运行在微信小程序

真正的问题是在微信开发者工具中编译运行。

Orval 生成的代码依赖:

javascript 复制代码
import axios from 'axios';

但标准 Axios 面向的是浏览器和 Node.js 环境。

微信小程序并不是标准浏览器环境,也没有浏览器中的完整 XMLHttpRequest 等运行环境,请求最终需要走:

scss 复制代码
wx.request()

所以直接把标准 Axios 放进微信小程序里并不能正常工作。

这就导致一个问题:

复制代码
OpenAPI
   ↓
Orval
   ↓
Axios
   ↓
微信小程序
   ✕

Orval 自动生成代码这条路本身没有问题,但是默认 Axios Transport 和微信小程序运行环境对不上。

5. 找到 axios-miniprogram

继续查小程序生态以后,找到了:

axios-miniprogram

项目地址:

github.com/zjxxxxxxxxx...

这个库就是专门面向小程序平台实现的 Promise HTTP 请求库,官方 README 明确列出了微信小程序支持,同时也支持 TypeScript、拦截器、中间件、扩展实例等功能。

安装:

复制代码
npm install axios-miniprogram

然后就可以创建自己的请求实例,例如:

javascript 复制代码
import axios from 'axios-miniprogram';

export const axiosInstance = axios.create({
  baseURL: 'https://example.com',
});

这样底层网络请求的问题解决了:

vbscript 复制代码
微信小程序
   ↓
axios-miniprogram
   ↓
wx.request

但很快又遇到了第二个问题。

6. 第二个坑:axios-miniprogram 和 Axios 的方法参数不一致

axios-miniprogram 虽然 API 命名和 Axios 很像,但它的方法参数设计并不完全一致,这正是 Orval 生成代码无法直接使用它的原因。

GET 请求为例。

标准 Axios 的调用方式是:

arduino 复制代码
axios.get(url, config)

查询参数放在 config.params 中:

php 复制代码
axios.get('/users', {
  params: {
    page: 1,
    size: 20,
  },
})

axios-miniprogram 的调用方式是:

csharp 复制代码
axios.get(url, params, options)

也就是说,它把查询参数单独作为第二个参数:

php 复制代码
axios.get(
  '/users',
  {
    page: 1,
    size: 20,
  },
  {
    timeout: 10000,
  },
)

两者的方法签名实际上是:

scss 复制代码
// Axios
get(url, config)

// axios-miniprogram
get(url, params, options)

而 Orval 在配置:

vbnet 复制代码
client: 'axios',
httpClient: 'axios',

之后,生成的是标准 Axios 风格代码,例如:

csharp 复制代码
axiosInstance.get('/users', {
  params: {
    page: 1,
  },
})

如果直接把这里的 axiosInstance 替换成 axios-miniprogram,那么:

css 复制代码
{
  params: {
    page: 1,
  },
}

会被 axios-miniprogram 当成第二个参数 params,而不是 Axios 中的 config

于是原本应该发送:

ini 复制代码
?page=1

的数据结构就变了,配置项、查询参数等也无法按照 Orval 生成代码的预期工作。

7. 实现 Axios 兼容层

最终还是需要增加一层兼容层,把 Orval 生成的标准 Axios 调用转换成 axios-miniprogram 能够正确处理的请求格式。

这些方法,那么最简单的办法就是自己实现一层 Adapter,把标准 Axios API 转换成 axios-miniprogram 的:

scss 复制代码
axiosInstance.request()

最终实现如下:

typescript 复制代码
import type {
  AxiosRequestConfig,
  AxiosRequestData,
  AxiosResponse,
  AxiosResponseData,
} from 'axios-miniprogram';

import { axiosInstance } from './request';

type StandardRequestMethod =
  | 'GET'
  | 'DELETE'
  | 'HEAD'
  | 'POST'
  | 'PUT'
  | 'PATCH';

const requestWithConfig = <TData extends AxiosResponseData>(
  method: StandardRequestMethod,
  url: string,
  config?: AxiosRequestConfig,
  data?: AxiosRequestData,
): Promise<AxiosResponse<TData>> => {
  const requestConfig: AxiosRequestConfig = {
    ...config,
    url,
    method,
  };

  if (data !== undefined) {
    requestConfig.data = data;
  }

  return axiosInstance.request<TData>(requestConfig);
};

const compatibleAxiosInstance = axiosInstance.extend({});

compatibleAxiosInstance.get = <TData extends AxiosResponseData>(
  url: string,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('GET', url, config);

compatibleAxiosInstance.delete = <TData extends AxiosResponseData>(
  url: string,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('DELETE', url, config);

compatibleAxiosInstance.head = <TData extends AxiosResponseData>(
  url: string,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('HEAD', url, config);

compatibleAxiosInstance.post = <TData extends AxiosResponseData>(
  url: string,
  data?: AxiosRequestData,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('POST', url, config, data);

compatibleAxiosInstance.put = <TData extends AxiosResponseData>(
  url: string,
  data?: AxiosRequestData,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('PUT', url, config, data);

compatibleAxiosInstance.patch = <TData extends AxiosResponseData>(
  url: string,
  data?: AxiosRequestData,
  config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> =>
  requestWithConfig<TData>('PATCH', url, config, data);

export { compatibleAxiosInstance };

8. 最终编译成功

增加兼容层以后重新编译微信小程序,Orval 自动生成的接口终于可以正常调用。

请求也能够正常发送。

相关推荐
律宏阔16 分钟前
微信小程序集成 TDesign 完整记录:解决 NPM packages not found
前端·微信小程序
律宏阔19 分钟前
微信小程序 ECharts 瘦身实战:分包异步化 + componentPlaceholder 避开主包 2MB 限制
前端·微信小程序
夏天要喝冰可乐26 分钟前
从 Idea 到开源插件:我用 Vibe Coding 做了「文章摆渡」
前端·ai编程·vibecoding
小小小小宇33 分钟前
Pi 手动添加
前端
然我1 小时前
模型不是 Agent:从零实现一个最小 Agent Loop
前端·人工智能·agent
boooooooom1 小时前
手把手做一个图 RAG 烹饪问答系统:Neo4j + Milvus + LLM 的工程实践
前端·javascript·后端
小小善后师1 小时前
HID 设备对接技术解析:基于本地中间服务的 WebSocket 通信模式
前端
黄油面包1 小时前
Codex 额度三天见底后,我重新做了一周预算
前端·人工智能
PedroQue991 小时前
v2.7.1:修复 H5 端返回死循环闪烁问题
前端·uni-app