likes
comments
collection
share

我是如何用Apifox来Mock接口的

作者站长头像
站长
· 阅读数 14

1. 本地Mock

在没有使用接触Apifox的时候,使用的是本地Mock的方案,需要在本地写一大堆的代码。

大概讲一下使用的方案吧。

1.1. 安装

首先安装vite-plugin-mockmockjs这两个库。

pnpm install mockjs vite-plugin-mock -D

1.2. 配置插件

vite.config.js中配置vite-plugin-mock插件,开启Mock:

import { viteMockServe } from 'vite-plugin-mock'

export default defineConfig((config) => {
  const { command, mode } = config
  const env = loadEnv(mode, process.cwd())

  return {
    plugins: [
      vue(),
      viteMockServe({
        // 只在开发阶段开启 mock 服务,mock和后端服务器接口能共存,可以通过配置来区分
        localEnabled: command === 'serve',
      }),
     	...
    ],
    ...
  }
})

这里localEnabled设置了在开发环境开启。

1.3. Mock数据

Mock数据就很简单了,在根目录下新建一个Mock目录,然后我们创建一下关于User的接口:

export default [
  // 用户登录
  {
    url: '/dev-api/mock/admin/acl/index/login',
    method: 'post',
    response: ({ body }) => {
      const { username, password } = body
      const checkUser = createUserList().find(
        (item) => item.username === username && item.password === password,
      )
      if (!checkUser) {
        return resultError('Incorrect username or password!')
      }
      const { token } = checkUser
      return resultSuccess({
        token,
      })
    },
  },
  // 获取用户信息
  {
    url: '/mock/user/info',
    method: 'get',
    response: (request) => {
      const token = getRequestToken(request)
      const checkUser = createUserList().find((item) => item.token === token)
      if (!checkUser) {
        return resultError(
          'The corresponding user information was not obtained!',
        )
      }
      return resultSuccess(checkUser)
    },
  },
  // 一个失败的请求
  {
    url: '/dev-api/error',
    method: 'get',
    response: () => {
      return {
        code: 1,
        message: '密码错误',
        data: null,
      }
    },
  },
]

这里就简单的对登录和用户信息作了一下Mock。

这里有个getRequestToken方法解释一下,它能从header中取出token,然后验证用户登录信息:

export function getRequestToken({
  headers,
}: requestParams): string | undefined {
  return headers?.authorization
}

它是从request中拿到的。

然后成功和失败的响应结构我也做了一层封装,方便复用:

import { ResultEnum } from '../src/enums/httpEnums'

/**
 * @description: 错误响应结构
 * @returns {*}
 */
export function resultError(
  message = 'Request failed',
  { code = ResultEnum.ERROR, data = null } = {},
) {
  return {
    code,
    data,
    message,
    type: 'error',
  }
}

/**
 * @description: 成功响应结构
 * @returns {*}
 */
export function resultSuccess<T>(data: T, { message = 'ok' } = {}) {
  return {
    code: ResultEnum.SUCCESS,
    data,
    message,
    type: 'success',
  }
}

export interface requestParams {
  method: string
  body: any
  headers?: { authorization?: string }
  query: any
}

/**
 * @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
 * @return token
 */
export function getRequestToken({
  headers,
}: requestParams): string | undefined {
  return headers?.authorization
}

以上就是对Mock数据的封装,使用起来就跟正常的接口请求一样,然后请求路径跟Mock接口中的一致就可以了:

/dev-api/mock/admin/acl/index/login

以上代码可以参考:github.com/xiumubai/vi…

2. Apifox

偶尔接触Apifox还是因为PostMan打开太慢了,后来我发现了这个工具,来测试后端接口。慢慢发现功能很强大啊,竟然可以云端Mock。我就研究一下。直接在我的项目中用上了。

下面我们来个详细讲解。

2.1. 基础mock

我们先来创建一个基础的Mock接口。

比如登录接口。

我是如何用Apifox来Mock接口的

我是如何用Apifox来Mock接口的

直接点击添加接口即可。然后输入接口的配置:

  1. 请求方式:post
  2. 请求路径:/admin/acl/login
  3. 请求参数:Body
  4. 返回响应

在响应的数据这里,我们需要把每个返回数据的字段填写上,这样返回的数据字段就会根据我们设置的字段返回。

最后点击保存,我们就成功了Mock了一个接口了。写完了这个接口,如何在我们的项目中测试呢?就需要我们的云端Mock功能了。

2.2. 云端Mock

云端Mock可以为我们提供一个云端服务器,就跟后端一样,会给我们返回Mock的数据。

我是如何用Apifox来Mock接口的

如果所示:apifox会给我们提供一个云端Mock的url地址,这个就是接口网管地址,加上我们写的接口请求路径,就可以完成的请求一个接口了。

云端Mock未开启的请点击开关按钮开启。

开放访问:任何人都能访问。

Token鉴权:需要在接口地址中携带一个Token才能访问这个接口。

有个这个Mock的url以后,回到前我们创建的登录接口,切换右上角未云端Mock即可:

我是如何用Apifox来Mock接口的

然后我们点击发送,可以成功请求到Mock结果了。

2.3. 期望Mock

情景:当我们登录的时候,会涉及到不同的登录判断情况,比如密码错误了,账户名错误了等等。

基于以上情景,我们需要使用高级Mock中期望Mock功能。

点击高级Mock中的新建期望

我是如何用Apifox来Mock接口的

然后新增一个期望

我是如何用Apifox来Mock接口的

这里我们可以根据参数来做判断,当不满足或满足某种情况,返回某些特定结果。

如果你的返回情况判断比较多,你可以写多种:

我是如何用Apifox来Mock接口的

最后记得把你的按钮打开。关闭则不生效。

2.4. 脚本Mock

情景:当我们Mock一个分页列表的时候,需要根据分页参数来返回数据,期望Mock的方式已经不能再满足了。这时候就需要使用脚本Mock的方式了。

在高级Mock选择脚本,开启Mock

我是如何用Apifox来Mock接口的

注意:脚本Mock的优先级比期望Mock低,需要先把期望Mock关闭。

然后在自定义脚本中输入代码:

var MockJs = require('mockjs');

var pageNum = Number(fox.mockRequest.getParam('pageNum'));
var pageSize = Number(fox.mockRequest.getParam('pageSize'));

// 生成中国手机号断前三位
function generateChinaMobilePrefix() {
  const prefixes = [
    '134', '135', '136', '137', '138', '139', '150', '151', '152', '157',
    '158', '159', '165', '1703', '1705', '1706', '178', '182', '183', '184',
    '187', '188', '195', '197', '198'
  ];
  const randomIndex = Math.floor(Math.random() * prefixes.length);
  return prefixes[randomIndex];
}

// 扩展手机号
MockJs.Random.extend({
  phone: function () {
    return generateChinaMobilePrefix() + MockJs.mock(/\d{8}/)
  }
});

// 生成分页列表数据
function generatePageList(pageNum, pageSize, total) {
  const results = [];
  for (let i = 0; i < pageSize; i++) {
    const id = (pageNum - 1) * pageSize + i + 1;
    results.push({ 
      id,
      "username": MockJs.mock('@first'),
      "nickName": MockJs.mock('@cname'),
      "roleName": '系统管理员 游客',
      "createTime": MockJs.mock('@datetime'),
      "updateTime": MockJs.mock('@datetime'),
      "phone": MockJs.mock("@phone")
    });
  }

  return {
    code: 200,
    message: "成功",
    data: {
        list: results,
        pageNum,
        pageSize,
        total,
    },
    ok: true
  };
}

fox.mockResponse.setBody(generatePageList(pageNum, pageSize, 100))

fox.mockRequest.getParam()会获取包括 Path 参数、Body 参数、Query 参数

fox.mockResponse.setBody()会把最终生成的数据返回。

其他的逻辑就是需要自己去判断和添加了。跟我们的js一样的。

2.5. Mock优先级

在apixfox的Mock中,默认Mock<脚本Mock<期望Mock。

2.6. Mock语法扩展

有时候我们想使用Mock中不存在的语法,比如生成手机号@phone,我们可以扩展:

var MockJs = require('mockjs');

// 扩展手机号
MockJs.Random.extend({
  phone: function () {
    var phonePrefixs = ['132', '135', '189']
    return this.pick(phonePrefixs) + MockJs.mock(/\d{8}/)
  }
});

// 使用
"phone": MockJs.mock("@phone")

2.7. Mock奇门遁甲

2.7.1. mock头像

"avatar": MockJs.mock(`@image('100x100', @color, @cname)`),

我是如何用Apifox来Mock接口的

2.7.2. Mock枚举值

...MockJs.mock({"status|1": [0, 1]})
// 或者
status: Mockjs.mock('@integer(0, 1)')

以上就是Apifox的基本玩法。

大家可以参考我这份API文档:y3vpryby6w.apifox.cn

接口创建好了以后,我们就可以轻松的在项目中使用。不用安装任何Mock依赖。更正常写法一样请求就OK了。

下面我再说一下项目中我是怎么做请求的。

3. 如何使用

一般我都是使用Proxy配置个带来,然后采用环境变量的形式来区分不服的服务接口。

配置环境变量,一般分为这三个:

我是如何用Apifox来Mock接口的

# .env.development
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '后台管理系统'
VITE_APP_PORT = 3007
VITE_APP_BASE_API = '/dev-api'
# .env.production
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
# NODE_ENV = 'production'
VITE_APP_TITLE = '后台管理系统'
VITE_APP_PORT = 3007
VITE_APP_BASE_API = 'https://mock.apifox.cn/m1/2979784-0-default'
# .env.test
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'test'
VITE_APP_TITLE = '尚品汇商城后台管理系统'
VITE_APP_PORT = 3007
VITE_APP_BASE_API = 'https://mock.apifox.cn/m1/2979784-0-default'

可以看到,development环境下,配置的VITE_APP_BASE_API='/dev-api',这时候走的就是代理,也就是Proxy,配置如下:

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig((config) => {
  const { command, mode } = config
  const env = loadEnv(mode, process.cwd())

  return {
    base: './',
    ...
    server: {
      host: 'localhost',
      port: Number(env.VITE_APP_PORT),
      proxy: {
        [env.VITE_APP_BASE_API]: {
          target: 'https://mock.apifox.cn/m1/2979784-0-default',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^/dev-api/, ''),
        },
      },
    },
  }
})

在封装axios的时候,baseURL也是根据环境变量来配置:

const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: ResultEnum.TIMEOUT as number,
})

这样,我们写接口的时候这样写就OK了。

/**
 * 登录
 */
export function login(data: LoginData) {
  return http.post<string>('/admin/acl/login', data)
}

/**
 * 获取登录用户信息
 */
export function getUserInfo() {
  return http.get<UserRes>('/admin/acl/info')
}

以上,就是关于如何Mock数据,我正在使用的方案。

欢迎大家关注我的专栏# vue3后台管理系统教程,持续更新中。