简介这是一款面向前端开发者与接口调试人员的HTTP自动回复请求软件即一键Mock工具主要解决后端接口尚未完成时前端开发受阻、传统Mock服务器搭建繁琐的问题。软件提供直观界面可快速创建、编辑和管理Mock接口依据接口文档配置模拟数据并支持一键启动服务无需复杂安装或外部插件适用于接口联调、模拟数据测试等日常开发场景。资源包共33个文件以15个dll动态库、10个xml配置说明、2个pdf使用与更新说明、2个log日志、2个config配置文件及1个db数据库和1个exe主程序为主压缩包约5.36MB运行于Win10 x64依赖.NET Framework 4.6.2。目前已有490人学习下载。借助该工具开发者可省去搭建Mock服务的繁琐流程快速进入开发状态提升接口调试效率与项目质量。1. Http自动回复请求软件一键Mock工具到底在解决什么问题联调时最怕的不是接口报错而是接口根本还没写好。前端页面已经画完后端同学还在改数据库字段测试同学催着要验收这时候一个能接管 HTTP 请求、按规则自动吐响应的 Mock 工具就是救命稻草。Http自动回复请求软件一键Mock工具本质上是一个本地 HTTP 服务它拦截你发出去的请求根据 URL、方法、请求头甚至请求体匹配预设规则然后返回你提前写好的响应。它解决的核心问题是让调用方在不依赖真实服务端的前提下拿到结构正确、状态可控的返回数据。适合前端联调、自动化测试、第三方接口模拟、异常场景复现这几类人。很多人第一次听到 Mock 会以为只是前端框架里的拦截其实 HTTP 层的 Mock 更彻底——它连真实网络请求都能接管浏览器、Postman、curl、甚至 C 写的客户端都骗得过。2. 一键Mock工具的匹配引擎与响应规则怎么设计2.1 请求匹配的四个维度方法、路径、头、体一个能用的 Mock 工具匹配逻辑必须比“路径相等”更细。真实项目里同一个路径可能因为请求头不同返回不同结构比如Accept: application/json和Accept: application/xml要吐两种格式。我一般把匹配拆成四层HTTP 方法、路径支持路径参数和通配、请求头键值对、请求体关键字或 JSONPath。优先级从高到低先匹配最具体的规则匹配不到再走默认兜底。下面是一个用 Python 标准库写的极简匹配核心不依赖任何框架方便你直接嵌进自己的工具里。import re import json class MockRule: def __init__(self, method, path_pattern, headersNone, body_containsNone, responseNone, status200): self.method method.upper() self.path_re re.compile(path_pattern) # 路径支持正则如 /api/user/\d self.headers headers or {} # 需要匹配的请求头空表示不校验 self.body_contains body_contains # 请求体包含的字符串或 dict self.response response or {} self.status status def match(self, method, path, headers, body): if method.upper() ! self.method: return False if not self.path_re.fullmatch(path): return False for k, v in self.headers.items(): if headers.get(k) ! v: return False if self.body_contains: if isinstance(self.body_contains, dict): try: body_json json.loads(body) for bk, bv in self.body_contains.items(): if body_json.get(bk) ! bv: return False except json.JSONDecodeError: return False elif self.body_contains not in body: return False return True这段代码里path_re用fullmatch而不是search是为了避免/api/user误匹配/api/user/123。headers只校验你关心的键不要求全量相等否则浏览器自动带的User-Agent会让规则永远匹配不上。body_contains支持字符串和字典两种写法字典走 JSON 解析适合校验{action: submit}这类关键字段。参数status单独拎出来是因为 Mock 异常场景时你要返回 500、404、403不能只改 body。2.2 响应模板与动态变量让 Mock 数据不再写死写死的响应只能骗过第一次调用一旦调用方需要 ID 关联、时间戳递增、分页游标静态 JSON 就露馅了。常见做法是在响应体里埋占位符返回前做一次替换。我一般支持三类变量{{uuid}}、{{timestamp}}、{{randomInt(1,100)}}再加一个{{req.body.xxx}}把请求里的字段回显到响应里。下面这个渲染函数直接接在上面的规则类后面用。import uuid import time import random import json def render_response(template, req_body): if isinstance(template, dict): template json.dumps(template) # 替换内置变量 template template.replace({{uuid}}, str(uuid.uuid4())) template template.replace({{timestamp}}, str(int(time.time() * 1000))) # 替换 randomInt def rand_repl(m): low, high int(m.group(1)), int(m.group(2)) return str(random.randint(low, high)) template re.sub(r\{\{randomInt\((\d),(\d)\)\}\}, rand_repl, template) # 回显请求体字段 try: req_json json.loads(req_body) for key, val in req_json.items(): template template.replace({{req.body.%s}} % key, str(val)) except Exception: pass return json.loads(template)render_response先统一转成字符串再做替换避免嵌套字典里漏掉占位符。randomInt用正则捕获两个数字参数支持任意范围。req.body回显只处理一层深层嵌套可以递归展开但大多数联调场景一层够用。注意json.loads在最后执行如果模板本身不是合法 JSON这里会抛异常所以规则配置阶段就要做一次校验别等到请求进来才翻车。2.3 一键启动把匹配和响应串成 HTTP 服务有了规则类和渲染函数剩下就是起一个 HTTP 服务把请求接进来。用 Python 的http.server足够轻不需要装 Flask。下面这段是完整可跑的入口监听 127.0.0.1:15721这个端口在热搜里出现过很多人本地调试时被 502 卡住其实就是服务没起或者端口被占。from http.server import BaseHTTPRequestHandler, HTTPServer RULES [ MockRule( methodGET, path_patternr/api/user/\d, response{code: 0, data: {id: {{randomInt(1000,9999)}}, name: mock_user, ts: {{timestamp}}}} ), MockRule( methodPOST, path_patternr/api/login, body_contains{username: admin}, response{code: 0, token: {{uuid}}}, status200 ), MockRule( methodGET, path_patternr/api/error, response{code: 500, msg: internal error}, status500 ), ] class MockHandler(BaseHTTPRequestHandler): def _handle(self): length int(self.headers.get(Content-Length, 0)) body self.rfile.read(length).decode(utf-8) if length else for rule in RULES: if rule.match(self.command, self.path, self.headers, body): resp render_response(rule.response, body) payload json.dumps(resp).encode(utf-8) self.send_response(rule.status) self.send_header(Content-Type, application/json) self.send_header(Content-Length, str(len(payload))) self.end_headers() self.wfile.write(payload) return self.send_response(404) self.end_headers() self.wfile.write(b{code:404,msg:no mock rule matched}) do_GET _handle do_POST _handle do_PUT _handle do_DELETE _handle if __name__ __main__: server HTTPServer((127.0.0.1, 15721), MockHandler) print(Mock server running on http://127.0.0.1:15721) server.serve_forever()RULES列表就是你的“一键”配置加一条规则就多一个接口。_handle里先读Content-Length再读 body不读干净会导致连接复用出问题热搜里那个http连接复用的坑多半就是这里没处理。do_GET _handle这种写法让所有方法走同一套逻辑省去重复代码。404 兜底返回 JSON 而不是空 body方便调用方判断是 Mock 没匹配还是服务挂了。启动后直接用 curl 验证curl -s http://127.0.0.1:15721/api/user/123 # {code: 0, data: {id: 4821, name: mock_user, ts: 1710000000000}} curl -s -X POST http://127.0.0.1:15721/api/login -d {username:admin,pwd:x} # {code: 0, token: 550e8400-e29b-41d4-a716-446655440000}如果返回 404先看路径正则是不是写成了search能匹配但fullmatch不匹配比如/api/user/\d对/api/user/123/就匹配不上。如果 curl 卡住不返回检查Content-Length是否和实际 body 长度一致不一致时rfile.read会一直等。3. 把 Mock 工具接进真实联调链路代理、录制与回放3.1 正向代理模式不改调用方一行代码直接让调用方改 baseURL 指向 Mock 服务在已有项目里往往要动配置甚至重新打包。更省事的做法是把 Mock 工具做成正向代理调用方仍然请求真实域名但 DNS 或 hosts 把域名指到 Mock 服务Mock 服务根据 Host 头区分是转发还是拦截。下面这段在原有 Handler 上加一层转发逻辑匹配不到规则时把请求原样转发给真实后端。import urllib.request REAL_BACKEND http://127.0.0.1:8080 # 真实后端地址 def forward(self, body): url REAL_BACKEND self.path req urllib.request.Request(url, databody.encode(utf-8) if body else None, methodself.command) for k, v in self.headers.items(): if k.lower() not in (host, content-length): req.add_header(k, v) try: with urllib.request.urlopen(req, timeout5) as resp: data resp.read() self.send_response(resp.status) for k, v in resp.headers.items(): if k.lower() ! transfer-encoding: self.send_header(k, v) self.end_headers() self.wfile.write(data) except Exception as e: self.send_response(502) self.end_headers() self.wfile.write(json.dumps({code: 502, msg: str(e)}).encode())在_handle的 404 分支里调用self.forward(body)即可。转发时过滤掉Host和Content-Length因为目标地址变了这两个头必须重算。transfer-encoding也要过滤否则分块传输会冲突。热搜里unexpected status 502 bad gateway出现在127.0.0.1:15721大概率就是转发目标没起或者超时设太短。timeout5对本地联调够用跨机房要调到 15 以上。3.2 录制回放先跑一遍真实接口再离线 Mock手写规则多了也累更高效的方式是让工具先以代理模式跑一遍真实业务流程把请求和响应成对录下来之后直接回放。录制时要注意过滤掉Date、Set-Cookie、ETag这类每次都变的头否则回放时匹配不上。下面是一个简单的录制存储结构用 JSON 文件按天分片避免单文件过大。import os import datetime RECORD_DIR ./records def save_record(method, path, req_headers, req_body, status, resp_headers, resp_body): os.makedirs(RECORD_DIR, exist_okTrue) day datetime.date.today().isoformat() filepath os.path.join(RECORD_DIR, f{day}.jsonl) record { method: method, path: path, req_headers: {k: v for k, v in req_headers.items() if k.lower() not in (date, cookie)}, req_body: req_body, status: status, resp_headers: {k: v for k, v in resp_headers.items() if k.lower() not in (date, set-cookie, etag)}, resp_body: resp_body, } with open(filepath, a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n)回放时按method path查最近一条记录如果请求体有关键字段差异可以再加一层 body 相似度匹配。录制文件用 JSONL 而不是单个 JSON 数组是为了追加写入不用读全量几千条记录也不卡。过滤cookie是因为登录态每次不同回放时应该用录制时的固定 token 或者干脆不校验。3.3 在浏览器 F12 里直接 Mock 接口返回不是所有场景都值得起一个本地服务。如果只是前端页面调接口F12 的 Network 面板就能做轻量 Mock。Chrome 的 Local Overrides 功能可以把你请求到的真实响应保存到本地改完刷新页面就生效不用装任何插件。操作路径F12 → Network → 右键某个请求 → Save for overrides → 修改本地文件 → 刷新。这个方式适合临时改几个字段验证 UI但没法模拟 500、超时、网络中断。要模拟异常状态还是得用上面的本地 Mock 服务或者用 Charles 的 Map Local 配合断点。热搜里怎么在f12工具里mock接口返回参数问的就是这个记住 Local Overrides 只对已成功请求过的接口生效第一次请求必须先走通。4. 避坑与排查Mock 工具最容易翻车的五个地方4.1 现象请求返回 502日志显示连接被拒绝原因Mock 服务监听的地址是127.0.0.1但调用方在容器或虚拟机里127.0.0.1指向的是容器自身而不是宿主机。解决把监听地址改成0.0.0.0调用方用宿主机 IP 访问。如果必须限制访问用防火墙规则而不是绑回环地址。4.2 现象POST 请求 body 读不到规则匹配永远走默认分支原因BaseHTTPRequestHandler不会自动读 body必须根据Content-Length手动rfile.read。如果Content-Length缺失比如 chunked 传输读出来就是空。解决先判断Transfer-Encoding: chunked是的话按 chunk 解析否则读Content-Length。更省事的做法是要求调用方发请求时带上Content-Length大多数 HTTP 客户端默认会带。4.3 现象同一个接口第一次返回正常第二次开始 404原因规则里的路径正则用了re.search而不是fullmatch或者路径带了查询字符串?a1导致匹配失败。解决匹配前用urlparse把 path 和 query 拆开path 只保留路径部分。正则统一用fullmatch需要前缀匹配就显式写.*。4.4 现象响应里的中文变成乱码原因send_header(Content-Type, application/json)没带charsetutf-8浏览器按 ISO-8859-1 解析。解决写成application/json; charsetutf-8并且json.dumps时加ensure_asciiFalse最后.encode(utf-8)。三步缺一不可。4.5 现象并发请求时响应串包A 请求拿到 B 的返回原因BaseHTTPRequestHandler默认单线程但如果你用了ThreadingHTTPServer而规则对象里有可变状态比如计数器就会串。解决规则对象只读动态变量在render_response里现场生成不要往规则实例上写状态。需要计数就用threading.local或者外部原子计数器。5. 进阶用规则热加载和请求回显把 Mock 工具变成调试黑匣子规则写死在代码里每加一个接口就要重启服务联调节奏一快就烦。我后来改成从rules.json热加载文件一改下次请求自动生效不用重启。实现方式很简单在_handle开头检查文件 mtime变了就重新解析规则列表。下面这段是热加载的核心逻辑接在MockHandler里。import os import threading _rules_lock threading.Lock() _rules_mtime 0 _rules_cache [] def load_rules_if_changed(): global _rules_mtime, _rules_cache path ./rules.json if not os.path.exists(path): return _rules_cache mtime os.path.getmtime(path) if mtime _rules_mtime: return _rules_cache with _rules_lock: if mtime _rules_mtime: return _rules_cache with open(path, r, encodingutf-8) as f: raw json.load(f) new_rules [] for item in raw: new_rules.append(MockRule( methoditem[method], path_patternitem[path], headersitem.get(headers), body_containsitem.get(body_contains), responseitem.get(response), statusitem.get(status, 200), )) _rules_cache new_rules _rules_mtime mtime return _rules_cacherules.json的格式就是规则类的字段平铺比如[ { method: GET, path: /api/order/\\d, response: {code: 0, orderId: {{randomInt(10000,99999)}}, status: paid}, status: 200 }, { method: POST, path: /api/pay, body_contains: {orderId: 12345}, response: {code: 0, payUrl: https://mock.pay/{{uuid}}}, status: 200 } ]热加载用双检锁避免每次请求都读文件mtime不变直接返回缓存。rules.json里路径正则的\d在 JSON 字符串里要写成\\d这是最容易写错的地方写完先用json.load校验一遍。有了热加载联调时你改规则、调用方刷新页面中间不用等任何人重启服务。再进一步把每个请求的入参和出参都记一份到debug.log格式化成一行 JSON出问题时直接tail -f看最近几十条比在调用方加日志快得多。我习惯在_handle的返回前加一句log_request(self.command, self.path, body, rule.status)日志里带上时间戳和匹配到的规则索引。这样当调用方说“返回不对”时你能立刻判断是规则没匹配上、还是匹配上了但响应模板渲染错了。这个习惯帮我省过很多次来回扯皮也算是一点血泪经验。最后说一个我自己的教训Mock 工具再方便也别让它悄悄接管了本该走真实后端的请求。我一般会在响应头里加一个X-Mock: true调用方在日志里看到这个头就知道当前数据是假的避免把 Mock 数据当成真实结果写进测试报告。这个头不影响业务逻辑但能在排查时省下大量“为什么数据对不上”的时间。希望帮到你。本文还有配套的精品资源点击获取