后端接口一直还没好?用 Node 搭了个本地Mock服务不耽误前端开发进度

举报
柠檬-夏日清爽 发表于 2026/10/01 19:40:35 2026/10/01
【摘要】 后端接口未就绪时前端只能干等,用 Node 搭了个本地 Mock 服务返回假数据。讲了 OPTIONS 预检必须处理否则请求发不出、要模拟网络延迟才能暴露 loading 与竞态问题、假数据应故意造缺字段和空数组、请求体需自己收流解析、以及 Mock 绝不能混进生产包。

前后端并行开发,前端页面写好了接口还没出,只能干等。或者在代码里写死假数据,联调时又要逐个删掉,容易漏。

做了个本地 Mock 服务:用配置文件描述接口,起一个真的 HTTP 服务返回假数据,前端代码按正常方式请求。联调时只要把接口地址换掉,业务代码不用动。

一、路由定义和匹配

const ROUTES = [
  { method: 'GET', path: '/api/users', handler: () => ({ code: 0, data: makeUsers(10) }) },
  { method: 'GET', path: '/api/users/:id', handler: (p) => ({ code: 0, data: makeUsers(1)[0] }) },
  { method: 'POST', path: '/api/login', handler: (p, body) => ({ code: 0, token: 'mock-token' }) },
];

function matchRoute(method, url) {
  const pathname = url.split('?')[0];
  for (const r of ROUTES) {
    if (r.method !== method) continue;
    const rp = r.path.split('/');
    const up = pathname.split('/');
    if (rp.length !== up.length) continue;
    const params = {};
    let ok = true;
    for (let i = 0; i < rp.length; i++) {
      if (rp[i].startsWith(':')) params[rp[i].slice(1)] = up[i];
      else if (rp[i] !== up[i]) { ok = false; break; }
    }
    if (ok) return { route: r, params };
  }
  return null;
}

按段匹配,:id 这种动态段收集到 params。先比段数再逐段比,段数不同直接跳过,省去不必要的循环。

二、处理请求和 CORS

function createServer({ delay = 200 } = {}) {
  return http.createServer((req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');

    if (req.method === 'OPTIONS') {
      res.statusCode = 204;
      return res.end();
    }

    const hit = matchRoute(req.method, req.url);
    if (!hit) {
      res.statusCode = 404;
      res.setHeader('Content-Type', 'application/json; charset=utf-8');
      return res.end(JSON.stringify({ code: 404, message: 'no mock route' }));
    }

    readBody(req).then((body) => {
      setTimeout(() => {
        const result = hit.route.handler(hit.params, body);
        res.setHeader('Content-Type', 'application/json; charset=utf-8');
        res.end(JSON.stringify(result));
      }, delay);
    });
  });
}

浏览器发非简单请求时先发一次 OPTIONS 预检,不处理的话正式请求根本发不出去。预检直接返回 204,不带响应体。

delay 模拟网络耗时,不然本地太快,加载状态和竞态问题在联调时才暴露——那时候改起来就晚了。

三、读取请求体

function readBody(req) {
  return new Promise((resolve) => {
    if (req.method === 'GET') return resolve(null);
    let raw = '';
    req.on('data', (chunk) => {
      raw += chunk;
      if (raw.length > 1e6) req.destroy();
    });
    req.on('end', () => {
      try {
        resolve(raw ? JSON.parse(raw) : null);
      } catch {
        resolve(null);
      }
    });
  });
}

请求体是流式的,要自己收完再解析。加了大小上限防止超大 body 把内存打满,JSON.parse 失败时返回 null 而不是让进程崩掉。

四、踩过的坑

第一个坑是预检请求没处理。 前面说的,带 Authorization 头或者用 application/json 的请求都会触发 OPTIONS,不返回正确头,浏览器直接拦掉。

第二个坑是没模拟延迟。 本地毫秒级返回,前端没写 loading 或者没处理竞态,联调到真实网络就出问题。加了默认延迟之后这些问题在开发阶段就能发现。

第三个坑是假数据太规整。 每条记录的字段都完整、数组长度固定,前端没处理缺省值,真实数据一来就报错。所以 Mock 里要故意造一些缺字段、空数组、超长文本的数据。

第四个坑是请求体没解析。 用了框架的话这步是自动的,自己写就会漏。

第五个坑是 Mock 数据进了生产。 配置文件里的假数据如果和业务代码混在一起,打包时会一起进去。Mock 应该只在开发环境启用,通过环境变量控制,并且路径跟真实接口分开配置。

第六个坑是改配置要重启。 早期版本改一次路由重启一次服务,很烦。后来加了监听配置文件变化自动重载,开发体验好很多。

五、最后总结

Mock 服务的意义不只是"造数据",更重要的是让前端在早期就能按真实的网络环境开发——有延迟、有错误码、有缺字段。这样联调时改的只是接口地址,不是业务逻辑。

后来加了按场景切换响应。同一个接口有时候要返回成功,有时候要返回失败来测错误处理,支持给路由配置多个响应,通过查询参数或者环境变量切换。

另外支持动态数据。假数据里的 id、时间戳每次生成新的,避免前端把假数据当真的缓存住。

还有个功能是记录请求日志。收到的请求打到控制台,前端说接口没返回时先看 mock 有没有收到,定位快。

最后是配置文件分离。路由和假数据放单独文件,主程序只负责起服务,改数据不用动代码。

还有个功能是状态码模拟。除了正常返回,也支持配置 401、500 这类错误码,用来测前端的错误处理。另外支持按概率失败:每次请求有一定概率出错,能测出前端在偶发错误下的表现,比固定失败更接近真实。最后是约定:mock 只在开发环境启用,构建时不能打进去。
另外支持自定义响应头,有些前端逻辑依赖特定的头部字段,mock 里得能配上。还有个细节是返回体支持直接读文件内容,假数据太多时写在配置文件里更清楚。
还有个细节是支持热重载,改了配置文件不用重启服务,开发时省很多事。另外支持按环境切换 mock 开关,避免忘记关闭导致上线后还在用假数据。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。