
简介这是一款面向前端开发者与接口调试人员的轻量级 Mock 服务工具针对后端接口尚未就绪、联调受阻的常见痛点提供可视化界面来创建、编辑和管理模拟接口无需搭建传统 Mock 服务器或依赖外部插件一键启动即可进入调试状态。资源包共 33 个文件以 15 个 dll 运行库、10 个 xml 配置说明、2 个 pdf 使用与更新文档、2 个 log 日志、2 个 config 配置及 1 个 exe 主程序为主另含 SQLite 数据库文件整体约 5.36MB结构紧凑、开箱即用。软件基于 Win10 x64 环境采用 Visual Studio 2022 编译依赖 .NET Framework 4.6.2并集成 log4net、Newtonsoft.Json、MQTTnet 等常用组件。目前已有 491 人学习下载。借助它读者可快速配置模拟数据、即时验证接口逻辑省去繁琐的环境搭建环节显著提升前端开发与联调效率。1. Http自动回复请求软件为什么一线开发都在用一键Mock工具联调接口时最怕什么前端页面写完了后端接口还没上线第三方支付回调要测异常分支总不能真去把人家生产环境打挂App 端要验证弱网重试逻辑抓包工具改来改去手都酸了。这时候一个 Http 自动回复请求软件就能救命——它本质上是一个本地 HTTP 服务你提前把「请求长什么样、该回什么」写成规则它收到匹配的请求就自动吐出预设响应整个过程不需要后端配合也不需要网络。一键 Mock 工具解决的正是「依赖方不可控」这个老大难接口契约先冻结前后端并行开发测试用例可复现。适合谁前端、测试、后端、甚至做 IoT 设备对接的嵌入式工程师——只要你在跟 HTTP 协议打交道这套东西迟早用得上。下面我从选型、规则设计、落地实现到踩坑把这条路走一遍。2. 一键Mock工具选型从零写服务还是用现成框架2.1 先想清楚你要 Mock 的是「响应」还是「行为」很多人一上来就问「哪个 Mock 工具最好用」这个问题本身就问错了。你得先分清两类需求第一类是静态响应替换比如GET /api/user/1永远返回同一段 JSON用来让前端先把页面渲染跑通第二类是动态行为模拟比如同一个接口根据请求头里的 token 返回 200 或 401或者第三次调用时故意超时用来验证客户端的重试和降级逻辑。前者用现成工具五分钟搞定后者必须能写脚本或表达式否则你会在「怎么让第三次请求返回 500」这种问题上卡住。我一般会按这个顺序判断如果只是临时给前端挡一下直接用现成 Mock 服务如果这个 Mock 要进 CI、要被测试用例引用、要模拟状态机那就自己写一个轻量服务把规则存成配置文件随代码一起版本管理。别小看这个判断选错了后面全是返工。2.2 现成方案与自建方案的边界现成 Mock 平台的优势是开箱即用界面点一点就能配规则适合非技术同学参与。但它有两个硬伤一是规则存在别人服务器上断网或服务挂了你就抓瞎二是复杂匹配逻辑比如按 JSON body 里某个嵌套字段的值分流往往要付费版才支持。自建方案的核心成本其实不在写服务而在规则描述格式的设计——你得让「匹配条件」和「响应内容」都能被非作者本人看懂并修改。常见做法是用一份 YAML 或 JSON 描述所有规则服务启动时加载进内存收到请求后按优先级逐条匹配。下面是一个最小可用的规则文件结构我用了几个真实项目里验证过的字段# mock_rules.yaml rules: - name: 用户详情-正常返回 priority: 10 match: method: GET path: /api/user/1 headers: {} # 空表示不校验请求头 query: {} # 空表示不校验查询参数 response: status: 200 headers: Content-Type: application/json; charsetutf-8 body: | {id:1,name:张三,role:admin} delay_ms: 0 - name: 用户详情-未授权 priority: 20 # 数字越大越先匹配 match: method: GET path: /api/user/1 headers: Authorization: Bearer expired_token response: status: 401 headers: Content-Type: application/json; charsetutf-8 body: {error:token expired} delay_ms: 200这段配置的逻辑很直白priority决定匹配顺序数字大的先试match里没写的字段就不参与校验response里的delay_ms用来模拟网络延迟。参数上唯一要注意的是Content-Type必须显式写否则很多客户端会按text/plain解析导致 JSON 解析失败——这个坑后面还会细说。2.3 用 Python 起一个最小可用的 Mock 服务选 Python 是因为标准库http.server就够用不需要装任何第三方包拷到任何有 Python 的机器上都能跑。下面这个版本支持加载上面的 YAML 规则、按优先级匹配、返回预设响应# mock_server.py import json import time from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse, parse_qs # 简化版规则加载实际项目可用 PyYAML RULES [ { name: 用户详情-正常返回, priority: 10, match: {method: GET, path: /api/user/1}, response: { status: 200, headers: {Content-Type: application/json; charsetutf-8}, body: {id:1,name:张三,role:admin}, delay_ms: 0, }, }, { name: 用户详情-未授权, priority: 20, match: { method: GET, path: /api/user/1, headers: {Authorization: Bearer expired_token}, }, response: { status: 401, headers: {Content-Type: application/json; charsetutf-8}, body: {error:token expired}, delay_ms: 200, }, }, ] def match_rule(method, path, headers, query): 按优先级从高到低匹配返回第一条命中的规则 for rule in sorted(RULES, keylambda r: -r[priority]): m rule[match] if m.get(method) and m[method] ! method: continue if m.get(path) and m[path] ! path: continue # 请求头匹配规则里写了才校验 ok True for k, v in m.get(headers, {}).items(): if headers.get(k) ! v: ok False break if not ok: continue return rule return None class MockHandler(BaseHTTPRequestHandler): def do_GET(self): parsed urlparse(self.path) rule match_rule(GET, parsed.path, self.headers, parse_qs(parsed.query)) if rule is None: self.send_response(404) self.end_headers() self.wfile.write(b{error:no mock rule matched}) return resp rule[response] if resp.get(delay_ms): time.sleep(resp[delay_ms] / 1000.0) self.send_response(resp[status]) for k, v in resp[headers].items(): self.send_header(k, v) self.end_headers() self.wfile.write(resp[body].encode(utf-8)) def log_message(self, fmt, *args): # 把访问日志打到控制台方便排查哪条规则命中了 print([MOCK] %s - %s % (self.address_string(), fmt % args)) if __name__ __main__: server HTTPServer((0.0.0.0, 8080), MockHandler) print(Mock server running on http://0.0.0.0:8080) server.serve_forever()关键点说明match_rule里对请求头的校验是「规则写了才比」这样你可以只针对某个特殊 header 做分流不用把浏览器自动带的User-Agent、Accept全写进去。log_message重写后会把每条请求打到控制台联调时一眼就能看出是规则没匹配上还是客户端根本没发请求。端口选 8080 是习惯实际用的时候如果和本机其他服务冲突改HTTPServer第二个参数即可。启动命令就一行python3 mock_server.py然后另开一个终端验证curl -i http://127.0.0.1:8080/api/user/1 curl -i -H Authorization: Bearer expired_token http://127.0.0.1:8080/api/user/1第一条应该返回 200 和用户 JSON第二条返回 401。如果第二条也返回了 200说明优先级排序写反了检查sorted里的-r[priority]。3. 请求匹配与响应构造让Mock规则真正可维护3.1 匹配条件的四个维度与优先级设计一个 HTTP 请求能被用来做匹配的维度其实就四个方法、路径、请求头、请求体含查询参数。路径匹配是最常用的但这里有个容易翻车的地方——路径参数。比如/api/user/1和/api/user/2如果各写一条规则用户量一上来规则文件就爆炸了。正确做法是支持路径模板把/api/user/{id}里的{id}提取出来既能匹配又能作为变量注入响应体。请求体匹配要特别小心JSON 的 key 顺序不固定直接字符串比对必然失败。常见做法是先解析成字典再逐字段比对只比对规则里声明的字段其余忽略。查询参数同理parse_qs返回的是列表比对时要注意取第一个值。优先级设计上我的血泪经验是越具体的规则优先级越高。带Authorization头的 401 规则一定比裸路径的 200 规则优先否则永远命中不到 401。用数字显式声明优先级比靠规则书写顺序可靠得多因为后者在多人协作时极易被改乱。3.2 响应构造状态码、Header 与 Body 的联动构造响应时最容易忽略的是Content-Type 和 Body 的匹配。你返回一段 JSON 字符串但 Header 里写的是text/plain前端fetch调.json()就会抛解析异常而错误信息往往只显示「Unexpected token」让人以为是数据问题。所以规则里Content-Type必须和 Body 格式一致JSON 用application/jsonXML 用application/xml纯文本才用text/plain。另一个高频需求是动态响应。比如登录接口要根据用户名返回不同的 token或者列表接口要返回递增的 ID。这时候静态 Body 就不够了需要在规则里支持简单的模板变量。下面是一个用 Python 字符串格式化实现的极简方案# 在 response 里增加 template 字段body 里用 {变量} 占位 def render_body(rule, path_params, query_params): body rule[response][body] # 只替换规则里声明的变量避免误伤 JSON 里的花括号 for k, v in {**path_params, **query_params}.items(): body body.replace({ k }, str(v)) return body参数说明path_params是从路径模板里提取的query_params是从查询串里取的。用replace而不是format是为了避免 JSON 里本身的花括号被当成占位符——这个坑我在一个返回嵌套对象的接口上踩过排查了半小时才发现是format把{a:{b:1}}里的{b当变量了。3.3 把规则文件纳入版本管理Mock 规则不是一次性消耗品它会随着接口契约演进而变化。我习惯把mock_rules.yaml和项目代码放同一个仓库目录结构大致是project/ src/ tests/ mock/ mock_rules.yaml mock_server.py README.md这样前端拉下代码就能起 Mock 服务测试同学也能在 CI 里用同一份规则跑契约测试。README 里只写三件事怎么启动、规则字段含义、新增规则时抄哪条最省事。别写长篇大论没人看。4. 避坑与排查Mock服务上线前必须过的五道坎4.1 现象curl 能通浏览器请求却报 CORS 错误原因浏览器对跨域请求会先发OPTIONS预检你的 Mock 服务没处理OPTIONS方法直接返回 404 或 405预检失败后续请求就不会发。解决在 Handler 里加do_OPTIONS返回 204 并带上Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers三个头。注意Allow-Headers要把客户端实际会带的头都列上比如Authorization, Content-Type。4.2 现象POST 请求的 Body 读出来是空的原因BaseHTTPRequestHandler不会自动读请求体你得根据Content-Length头手动读self.rfile.read(length)。如果客户端用了Transfer-Encoding: chunkedContent-Length不存在读出来就是空。解决先判断有没有Content-Length没有就按 chunked 解析或直接返回 411。大多数 Mock 场景下让客户端别用 chunked 更省事。4.3 现象规则明明写了却匹配不上日志显示 404原因路径带了查询串。self.path拿到的是/api/user/1?debug1直接和规则里的/api/user/1比肯定不等。解决用urlparse拆出path再比对查询参数单独处理。这个坑几乎每个手写 Mock 服务的人都踩过包括我。4.4 现象延迟设置不生效客户端还是秒回原因time.sleep写在send_response之后。HTTP 响应头一旦发出客户端就认为响应开始了后面的 sleep 只影响 Body 传输看起来就像没延迟。解决把 sleep 放在send_response之前确保整个响应都延迟。4.5 现象并发请求时响应错乱A 请求拿到了 B 的响应原因用了全局变量存「当前请求」的状态多线程下互相覆盖。HTTPServer默认是单线程的但如果你换成了ThreadingHTTPServer就必须保证每个请求的处理逻辑不共享可变状态。解决所有匹配和渲染都在函数内部用局部变量完成规则数据只读不写。5. 进阶技巧用连接复用和契约校验把Mock做扎实5.1 开启 HTTP 连接复用让压测数据更真实默认的HTTPServer每个请求处理完就关连接客户端每次都要重新握手。这在功能联调时无所谓但如果你要用 Mock 服务做压测或验证客户端的连接池行为就必须支持HTTP 连接复用Keep-Alive。做法是在响应头里加Connection: keep-alive并确保protocol_version设为HTTP/1.1class MockHandler(BaseHTTPRequestHandler): protocol_version HTTP/1.1 # 默认是 HTTP/1.0不支持长连接 def do_GET(self): # ... 匹配逻辑 ... self.send_response(resp[status]) self.send_header(Content-Type, resp[headers][Content-Type]) self.send_header(Connection, keep-alive) self.send_header(Content-Length, str(len(resp[body].encode(utf-8)))) self.end_headers() self.wfile.write(resp[body].encode(utf-8))注意Content-Length必须显式设置否则客户端不知道 Body 有多长连接复用就无从谈起。设成 HTTP/1.1 后用curl -v能看到Re-using existing connection的提示说明复用生效了。这个技巧在验证客户端连接池配置时特别有用——你能直观看到第 N 个请求是否复用了前面的 TCP 连接。5.2 用契约校验反向检查 Mock 规则是否过期Mock 规则最大的风险是「和真实接口脱节」后端改了字段名Mock 还在返回旧结构前端基于 Mock 开发完一联调就崩。我的习惯是每周跑一次契约校验拿真实接口的响应和 Mock 响应做结构比对只比字段名和类型不比具体值。下面是一个极简的比对脚本import json def compare_schema(real: dict, mock: dict, path): 递归比对两个 JSON 的字段名和类型返回差异列表 diffs [] if type(real) ! type(mock): diffs.append(f{path}: 类型不一致 real{type(real)} mock{type(mock)}) return diffs if isinstance(real, dict): for k in real: if k not in mock: diffs.append(f{path}.{k}: Mock 缺少字段) else: diffs.extend(compare_schema(real[k], mock[k], f{path}.{k})) for k in mock: if k not in real: diffs.append(f{path}.{k}: Mock 多出字段) elif isinstance(real, list) and real and mock: diffs.extend(compare_schema(real[0], mock[0], f{path}[0])) return diffs # 用法分别请求真实接口和 Mock 接口解析 JSON 后比对 # diffs compare_schema(real_json, mock_json) # for d in diffs: print(d)这个脚本只做结构比对不关心值所以可以安全地跑在 CI 里。一旦输出非空就说明 Mock 规则该更新了。参数上唯一要注意的是列表只比第一个元素因为 Mock 数据通常只有一条真实接口可能返回多条比全部会误报。5.3 一个我坚持了三年的习惯每次新建 Mock 规则我一定先写一条「兜底规则」匹配所有未命中的请求返回 501 和一段说明文字而不是默认的 404。原因很简单——404 在客户端看来可能是「资源不存在」的正常业务响应容易被忽略501 明确表示「这个请求没被 Mock 覆盖」联调时一眼就能发现漏配的接口。这个习惯帮我省下了无数次「为什么这个接口返回空」的排查时间。希望帮到你。本文还有配套的精品资源点击获取