如何在 Apifox 中返回条件 Mock 数据(自定义规则与 Mock 脚本)

智能Mock不够用?本文带你实操Apidog高级Mock规则,通过具体示例教你根据不同请求动态返回200或401响应,轻松应对各种复杂前端联调场景。

用 Apifox,节省研发团队的每一分钟

如何在 Apifox 中返回条件 Mock 数据(自定义规则与 Mock 脚本)

免费使用 Apifox

相关推荐

最新文章

API

一体化协作平台

API 设计

API 文档

API 调试

自动化测试

API Mock

API Hub

立即体验 Apifox
目录

智能 mock 让你在几秒钟内即可获取一个模拟的 API。它通过读取你的接口数据模型,返回看似合理的数据:如格式正确的电子邮件、合理的 timestamp,以及不再是像 xJ8kQ 这样的随机名称。对于大多数前端开发工作而言,这足以解决你的燃眉之急。

接着你遇到了智能 mock 无法处理的场景。例如,你希望 /login 接口在遇到已知用户时返回 200,否则返回 401。你希望 /orders/{id} 接口针对某个特定的 ID 返回“已发货”状态的订单,而针对另一个 ID 返回“已取消”状态的订单。你可能还想根据需要强制返回 500 错误,以便在代码上线到生产环境之前测试错误处理逻辑。由于智能 mock 对每个接口只能返回一种响应结构,无法根据请求进行分支处理。而这正是本指南要帮您解决的问题。

Apifox 通过两个功能来解决这一问题:用于基于规则条件响应的 mock 期望,以及用于处理规则无法表达的复杂逻辑的 mock 脚本。本教程将通过具体的示例向您演示这两者,并解释它们的优先级顺序,以确保您的自定义规则始终能够覆盖智能 mock。如果您还不熟悉基础知识,“API mocking 概览”是一个很好的预热,而 Apifox 是我们全程使用的工具。OpenAPI 规范(OpenAPI Initiative)则定义了使这一切成为可能的设计优先(contract-first)工作流。

什么是条件 mock

条件 mock 实际上就是一条规则:当传入的请求符合 这种特征 时,就返回 对应的响应。Apifox 从两个层面上构建这些规则。

第一层是接口数据模型内部的字段级自定义。你可以将某个字段固定为特定值,也可以附加一个动态的 Faker.js 表达式,使其在每次调用时发生变化。这控制了 字段包含什么内容,但该接口返回的响应结构仍然只有一种。

第二层则是完整的响应 mock 期望。一个期望是一个具有名称的规则,它包含可选的触发条件以及专有的响应 body、状态码和 header。没有设置条件的期望会无条件地返回固定数据。而设置了条件的期望只有在请求匹配条件时才会返回其数据。通过叠加多个期望,你可以实现真正的分支路由:当请求满足条件 A 时返回响应 B,在缺失 header 时返回错误 body,或者针对不同的 path 参数返回不同的 payload。

这第二层使得根据需求模拟错误状态和根据请求生成不同的 body 成为可能。本指南接下来的内容将重点围绕这一层展开。

首先了解字段级动态值

在介绍分支逻辑之前,先了解单个字段是如何获取其值的会有所帮助,因为你的条件响应将复用相同的语法。

在接口的数据模型中,任何字符串字段都可以包含写作 {{$category.method}} 的 Faker.js 表达式。Apifox 会在每次 mock 调用时根据你的 JSON Schema 定义中声明的字段类型重新解析该表达式。

{ "id": "{{$number.int(min=1000,max=9999)}}", "customer": "{{$person.fullName}}", "email": "{{$internet.email}}", "product": "{{$commerce.productName}}", "shippingAddress": "{{$location.streetAddress}}, {{$location.city}}", "orderedAt": "{{$date.between(from='2024-01-01',to='2024-12-31',format='yyyy-MM-dd')}}" }

参数化方法是可行的,因此 {{$number.int(min=1000,max=9999)}} 限制了数值范围,而 {{$date.between(...)}} 则限定了日期范围和格式。您可以在一个字段中拼接静态文本和多个表达式,上面的地址就是通过这种方式构建出来的。如果您需要特定地区的数据,Apifox 支持自定义 mock 语言环境(locales),以便您的姓名、地址和电话号码与特定的语言或国家相匹配。Apifox 中的 Faker.js 参考文档涵盖了完整的方法目录。

这属于智能 mock(Smart mock)的范畴。它是动态的,但不是条件性的。要根据请求进行分支处理,您需要使用 mock 期望。

实战演练:一个返回 200 或 401 的登录接口

经典案例:POST /login 接收包含 usernamepassword 的 JSON body。已知用户应获得返回 token 的 200 响应,其他所有用户都应获得 401 响应。

打开正确的标签页

在何处进行配置取决于您的工作模式:

  • 在调试模式(Request-first)下,打开该接口并点击 Mock 标签页。
  • 在文档模式(Design-first)下,打开该接口并点击 高级 mock 标签页。

两者都会引导至相同的 mock 期望列表。如果您想跟着步骤操作但尚未安装应用程序,可以先下载 Apifox,然后导入或创建一个 /login 接口。

添加成功时的 mock 期望

点击 新建期望。给它起一个 期望名称,例如 login-success。现在添加一个条件。因为 username 存在于 JSON 请求 body 中,所以您可以将其作为 body parameter 进行匹配:在名称字段中填入目标属性的 JSON 路径 username,并将条件设置为等于 alice@example.com

Body-parameter 条件仅支持 JSON,并且通过名称字段中的 JSON 路径进行匹配,因此嵌套属性使用点路径(例如 user.email)。在 响应数据 中填入成功时的 Payload:

json { "token": "mock-jwt-{{$string.uuid}}", "user": { "id": 4821, "username": "alice@example.com", "role": "member" } }

保存。默认的 HTTP 状态码200,因此对于正常流程,您无需修改任何其他内容。

添加失败时的 mock 期望

再次点击 新建期望。将其命名为 login-failure,并保持其条件为空,以便它作为兜底规则(catch-all)。将其 响应数据 设置为错误 body:

json { "error": "invalid_credentials", "message": "Username or password is incorrect." }

此期望需要一个非默认的状态。打开该期望的 More 标签页,将 HTTP Status Code 设置为 401。顺便提一下,More 标签页也是设置以毫秒为单位的 Response Delay(默认值为 0)以及任何自定义响应 header 的地方。设置 400ms 的延迟是一种非常简便的方法,可以确保你的加载动画(loading spinner)能够真正渲染出来。

顺序至关重要

mock 期望是从上到下依次评估的,第一个匹配的规则生效。因此,login-success 必须位于 login-failure 之上。当请求的 username 等于 alice@example.com 时,会匹配第一条规则并返回 Token。其他任何情况都会落入无条件失败规则,并返回 401。如果你颠倒了顺序,无条件(空白条件)规则将匹配所有内容,而你的成功用例将永远不会被触发。

从接口中复制 mock URL 并测试这两个路径:

# 已知用户 -> 返回 200 及 Token
curl -X POST https://<your-mock-host>/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice@example.com","password":"whatever"}'

# 其他任何人 -> 返回 401
curl -X POST https://<your-mock-host>/login \
  -H "Content-Type: application/json" \
  -d '{"username":"stranger@example.com","password":"whatever"}'

实战演练:根据状态为 /orders/{id} 返回不同的 body

第二个常见案例是根据 path 参数进行分支。你希望 /orders/{id} 针对某一个 ID 返回已发货的订单,而针对另一个 ID 返回已取消的订单,这样你的 UI 就可以在没有真实后端的情况下渲染每种状态。

为每种状态创建一个 mock 期望。为每个期望添加针对 path 参数 id 的条件,然后填写相应的 Response data

mock 期望 order-shipped,条件:path 参数 id 等于 5001

{
  "id": 5001,
  "status": "shipped",
  "total": 129.90,
  "trackingNumber": "1Z{{$string.alphanumeric(length=16)}}",
  "shippedAt": "{{$date.recent(days=3,format='yyyy-MM-dd')}}"
}

mock 期望 order-cancelled,条件:path 参数 id 等于 5002

{
  "id": 5002,
  "status": "cancelled",
  "total": 0,
  "cancelledAt": "{{$date.recent(days=1,format='yyyy-MM-dd')}}",
  "refundIssued": true
}

添加最后一个不带任何条件的 mock 期望,返回一个通用的待处理订单,这样任何其他 ID 仍能获得有效的响应,而不是落空。将具体的规则排在兜底规则之上,保存后,你就拥有了一个可以按需渲染各种订单状态的 mock。混合条件同样有效:在 path 条件旁边添加一个 header 条件,两者必须同时满足,因为 Apifox 使用与(AND)逻辑组合多个条件(用文档的话说,就是条件的交集)。

条件并不局限于 body 和 path。你可以匹配 query 参数、header 参数、cookie 参数,甚至 IP 地址,这允许你在测试期间将响应限制在特定的客户端。

按需强制触发错误状态

你不需要一个崩溃的后端来测试异常的响应。一个 mock 期望配合 More 标签页就能为你提供所需的任何状态。

要强制返回 500,可以添加一个 mock 期望,其触发条件由客户端控制,例如 header X-Mock-Scenario 等于 server-error。在 More 标签页中将其 Response data 设置为真实的错误 body,并将 HTTP Status Code 设置为 500

{
  "error": "internal_error",
  "requestId": "{{$string.uuid}}",
  "message": "Something went wrong on our end. Please retry."
}

现在,同一个接口默认会返回正常的 200,而一旦你发送了该 header,它就会返回 500。你也可以对 404429(在 More 标签页中设置 Retry-After header)或 503 进行相同的操作。你的前端错误处理终于有内容可以捕获了。如果你在自动化检查中对这些响应进行断言,API 断言指南会与此配置非常契合。

共享项目的一个细节:在 mock 期望列表中,可以针对本地和云端 mock 环境独立开启或关闭每个 mock 期望。因此,你可以在本地保持 500 规则处于激活状态,而在你团队成员访问的云端 mock 中保持关闭。

当规则不够用时:mock 脚本

mock 期望是声明式的。它们进行匹配并返回结果,但无法进行计算。当你需要一个源自请求的字段、计算商品明细的总额,或者需要一个根据多个输入同时改变结构的 body 时,你就需要使用 mock 脚本。

mock 脚本是针对 mock 响应运行的 JavaScript。它位于 Mock 标签页底部的 Mock Script 区域,可以通过开关启用。该脚本暴露了两个全局变量:

  • $$.mockRequest:通过 getParam(key) 读取传入的请求,以及 headerscookiesbodyformdataurlencoded
  • $$.mockResponse:通过 setBody()setCode()setDelay()json() 以及 headerscode 属性来构建传出的响应。

以下是一个根据提交的商品明细计算订单总额,并回显调用者货币 header 的脚本:

const body = $$.mockRequest.body;
const items = body.items || [];

const subtotal = items.reduce((sum, item) => {
  return sum + item.price * item.quantity;
}, 0);

const currency = $$.mockRequest.headers["x-currency"] || "USD";

$$.mockResponse.setCode(201);
$$.mockResponse.setBody({
  orderId: Math.floor(Math.random() * 90000) + 10000,
  currency: currency,
  subtotal: subtotal,
  tax: Number((subtotal * 0.08).toFixed(2)),
  total: Number((subtotal * 1.08).toFixed(2))
});

其运行流程是:智能 mock 生成初始响应,你的脚本读取 $$.mockRequest 和当前的 $$.mockResponse,应用其逻辑,调用 $$.mockResponse.setBody()(以及根据需要的 setCodesetDelayheaders),最后引擎返回最终结果。如果你想通过数组方法或日期计算来进一步扩展逻辑,MDN JavaScript 参考文档会是一个很好的帮手。

一个容易让人绊倒的规则

Mock 脚本仅适用于 Smart mock。它们不适用于 mock 期望或响应示例。这是最需要牢记的一点:你无法将 mock 脚本与基于期望的响应结合使用。如果某个期望匹配了请求,脚本就绝不会运行。因此,请为每个接口明确选择一种方式。当你想根据固定条件进行分支并返回预设的 body 时,请使用期望。当你需要基于 Smart mock 生成的基础数据生成计算输出时,请使用 mock 脚本。

优先级顺序的解析机制

综合来看,以下是 Apifox 处理任何 mock 请求时的执行顺序:

  1. 它会自上而下地检查你的期望。第一个所有条件均匹配的期望将生效,并返回其响应。这就是为什么自定义规则优于 Smart mock 的原因:一旦匹配到期望,就会直接中断并跳过其下方的所有内容。
  2. 如果没有匹配的期望,Apifox 会回退到你在项目设置 - 功能设置 - Mock 设置中设置的 Mock 方式优先级。在这一层级中,Smart mock(以及附加到它的任何 mock 脚本)会生成响应。

心智模型很简单:特定规则优先,生成数据次之。将你的期望按照从最具体到最泛化的顺序排列,如果你想确保必定匹配,可以在最下方保留一个无条件的兜底项,并让 Smart mock 处理其他所有情况。如需深入了解何时使用各层级,API mock 使用场景指南将常见场景与功能进行了对应。

发布前需要注意的易错点

了解以下几点限制可以避免让你陷入混乱的调试中:

  • Parameter 条件不支持 {{variables}}。Apifox 项目变量和环境变量在 mock 期望中不可用,因此请在条件中写入字面量值。
  • Body-parameter 条件仅支持 JSON,不支持 XML,且必须通过名称字段中的 JSON path 进行匹配。
  • 条件中的请求 body 格式必须与接口定义相匹配。对于 form-data 接口,必须使用 form-data 的方式进行 mock,而不能使用 JSON。
  • 在 mock 脚本内部,没有日志函数,pm 对象不可用(它与测试脚本的执行环境不同),并且无法使用 Apifox 变量。请保持脚本逻辑的自包含性。

文档中并未指出这些功能受到套餐方案的限制。本地与云端之间唯一的区别仅在于功能上:如前所述,每个环境拥有独立的开关,而不是付费墙。

使用 Apifox CLI 实现工作流自动化

Apifox 中的 mock 功能是一项 GUI 和云端能力。mock 引擎通过本地和云端 mock URL 提供接口服务,并且没有可以直接启动运行中 mock 服务端的 CLI 命令。Apifox CLI 真正增加的是对构建这些 mock 的资源进行控制的能力。

Mock 响应是根据接口数据模型生成的,因此 mock 的准确性取决于接口规范的准确性。CLI 以及驱动它的 AI 编码助手(Cursor、Claude Code、Trae、Codex)可以创建和更新项目中的接口和数据模型。在代码中修改契约并进行同步,即可保持 mock 输出的正确性,无需任何人重新打开应用。

一旦 mock 解决了前端开发的阻塞问题,同一个项目的测试场景就可以在 CI 中以无头(headless)模式运行,从而根据 mock 描述的契约来校验真实的后端:

apifox run -t <scenario_id> -e <env_id> -r cli

这一条命令即可执行您的测试场景并报告结果,从而使 mock 和验证共享同一个单一事实源。Apifox CLI 安装指南介绍了如何进行设置,而 GitHub Actions 中的 Apifox CLI 演练则指导如何将其接入流水线。

FAQ

为什么即使条件看起来正确,我的 mock 期望仍然被忽略了? 几乎总是由于顺序或格式不匹配引起的。Mock 期望是自上而下进行评估的,并且以第一个匹配项为准。因此,位于具体规则之上的宽泛且无条件的规则会直接吞掉该请求。另外,请确认 body 格式与接口规范相匹配(例如 JSON body 对应 JSON path,表单接口对应 form-data 布局)。如果您想重新检查,可以参考 API mock 概览,其中介绍了基础设置。

我可以在同一个响应上同时使用 mock 脚本和 mock 期望吗? 不能。Mock 脚本仅在智能 mock(Smart mock)下运行。它们会被 mock 期望和响应示例忽略。如果某个 mock 期望匹配成功,脚本将永远不会执行。因此,请为每个接口选择一种方法:使用 mock 期望进行基于规则的分支处理,使用脚本进行计算输出。

如何在不破坏默认 200 响应的情况下返回 401 或 500? 添加一个专属的 mock 期望,并设置一个可由客户端控制的条件(使用 header 非常有效),然后打开它的“更多”标签页并设置 HTTP 状态码(HTTP Status Code)。默认响应仍将保持 200;只有在条件匹配时才会触发错误响应。

条件中可以使用我的环境变量吗? 不能。Apifox 的 {{variable}} 变量值在 mock 期望中不可用,并且 parameter 条件也不支持 {{variables}}。请在条件中使用字面量值。

当没有任何 mock 期望匹配时会发生什么? Apifox 将回退到“项目设置 - 功能设置 - Mock 设置”中的“Mock 方式优先级”,此时智能 mock 会根据您的数据模型生成响应。相比之下,添加一个空白条件的兜底 mock 期望是保证特定回退行为的有效方法。

总结

Smart mock 处理常见情况,而 mock 期望则处理所有包含 if 条件的情况:例如已知用户返回 200,否则返回 401;根据不同状态返回不同的订单 body;或者按需返回 500。只有在需要规则无法表达的计算输出时,才需要使用 mock 脚本,并且请记住它仅针对 Smart mock 运行。牢记优先级顺序(特定期望优先,生成的数据次之),您的 mock 将完全像真实的 API 一样进行分支流转。下载 Apifox 以构建您的第一个条件 mock,免费且无需信用卡。

开发必备:API 全流程管理神器 Apifox

介绍完上文的内容,我想额外介绍一个对开发者同样重要的效率工具 —— Apifox。作为一个集 API 文档、调试、设计、测试、Mock、自动化测试于一体的工具,Apifox 是目前提升研发效率的首选。

如果你正在开发项目,不妨试试其极其友好的界面设计,它完全兼容 Postman 和 Swagger 数据格式,导入数据非常方便,,即使是新手也能很快上手,点击这里即可注册使用

Apifox

值得一提的是,除了个人和常规团队使用,针对有高安全合规要求、或需要在内网环境协作的企业,Apifox 还提供了深度定制的私有化部署方案

获取专属报价与部署方案

icon 详细的私有化部署系统架构与安全白皮书
icon 针对您公司规模的专属报价单
icon 免费的 1v1 专属产品演示 (Demo) 机会
获取部署方案
* 提交后,我们的客户经理将在 1 个工作日内与您联系
林俊锋 企业微信
@Apifox 专属顾问
扫码备注: 私有化 + 公司名