编写自定义 Mock 响应:动态响应模板与 Handlebars 语法

静态 Mock 响应只能返回固定数据,无法模拟真实 API 根据请求参数、路径、请求头等动态返回不同结果。本章通过引入 Handlebars 模板引擎,实现“请求参数 → 模板变量 → 动态响应”的流水线,让 Mock 服务在开发环境中更贴近真实后端行为。

4.1 核心原理

动态响应模板的工作分为三个阶段:

  1. 注入:将 HTTP 请求中的 query、params、body、headers 等数据提取为模板变量。
  2. 编译:使用 Handlebars 预编译模板字符串,将变量占位符替换为实际值。
  3. 输出:生成最终的 JSON/XML/Text 响应体,返回给客户端。
关键点:Handlebars 是逻辑较少的模板语言,支持 {{变量}}、{{#each}}、{{#if}} 等基础语法,天然适合 JSON 数据驱动。

4.2 实现流程

步骤 操作 说明
1 安装 Handlebars npm install handlebars 或引入 CDN
2 定义模板字符串 在 Mock 配置或独立文件中编写 {{}} 模板
3 编写请求参数映射器 从 req 中提取需要的字段
4 编译并渲染 Handlebars.compile(template)(data)
5 返回响应 将渲染结果以 JSON/其他格式返回
6 调试与缓存 验证模板与参数,优化编译性能

4.3 代码示例:基于 Express 的 Mock 中间件

以下示例演示如何在 Node.js (Express) 中实现动态 Mock 响应:

const Handlebars = require('handlebars');
const express = require('express');
const app = express();

app.use(express.json());

// 预定义模板
const templates = {
  user: Handlebars.compile('{"id":{{id}},"name":"{{name}}","role":"{{role}}"}'),
  list: Handlebars.compile('[{{#each items}}{"id":{{id}},"title":"{{title}}"}{{#unless @last}},{{/unless}}{{/each}}]')
};

app.get('/api/user/:id', (req, res) => {
  // 注入请求参数
  const data = {
    id: req.params.id,
    name: req.query.name || 'default',
    role: req.headers['x-role'] || 'user'
  };
  // 渲染
  const resp = templates.user(data);
  res.set('Content-Type', 'application/json');
  res.send(resp);
});

app.post('/api/list', (req, res) => {
  const items = req.body.items || [{ id: 1, title: 'item1' }];
  const resp = templates.list({ items });
  res.json(JSON.parse(resp));
});

app.listen(3000);
模板变量来源:req.params(路径参数)、req.query(查询参数)、req.body(POST body)、req.headers(请求头)。

4.4 调试技巧

  • 打印原始模板:在编译前输出模板字符串,检查 {{}} 是否匹配。
  • 使用调试中间件:在渲染前后打印 data 对象和渲染结果。
  • Handlebars 已知问题:如果变量不存在不会报错,会输出空字符串。可用 {{! 注释}} 调试。

4.5 常见错误与修复

错误 原因 修复
模板报错 Expecting 'STRING', 'NUMBER', 'BOOLEAN' JSON 中手写逗号、括号不合法 模板字符串本身应为合法 JSON 格式,或用 {{#if}} 控制
变量输出为空 变量名拼错或未从请求中提取 打印 data 对象,确认 key 名称
循环结果不完整 {{#each}} 未正确闭合 确保 {{/each}} 存在且数据为数组
安全注意:不要在模板中直接拼接用户输入(如 req.query.name)作为 Handlebars 部分,防止模板注入。应始终通过数据对象传递,并对字符串做转义。

4.6 性能优化

  • 预编译模板:在服务启动时用 Handlebars.compile() 一次,不要每次请求都编译。
  • 缓存编译结果:如果模板数量多,使用 LRU 缓存;Handlebars 编译 CPU 开销较高。
  • 减少模板中的逻辑:复杂的 {{#if}} 链建议拆分到数据预处理中。
  • 使用 Handlebars.precompile():将模板预编译为 JS 函数,可用于构建时优化。

4.7 内联 SVG 流程图:动态响应模板处理流程

下图展示了从 HTTP 请求到渲染响应的完整路径:

动态响应模板处理流程 HTTP 请求 提取参数 Handlebars 编译 + 渲染 响应输出 模板缓存 说明 1. 请求进入后,提取 query/params/body/headers 生成 data 对象。 2. Handlebars 编译模板(可预编译并缓存),用 data 渲染。 3. 最终返回 JSON 响应。模板缓存可避免重复编译,提升性能。

4.8 部署建议

  • 将模板与数据映射逻辑分离,存入独立 JSON 文件。
  • 使用环境变量控制模板目录路径,便于多环境复用。
  • 在 CI/CD 中加入模板语法校验(如 Handlebars.parse() 检测错误)。
练习:修改上面示例,增加 {{#if}} 分支,使得当 role === 'admin' 时返回额外字段 "admin":true。