# WebCurl
**Repository Path**: o8oo8o/WebCurl
## Basic Information
- **Project Name**: WebCurl
- **Description**: 极简网页版API调试神器
- **Primary Language**: Go
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 6
- **Forks**: 5
- **Created**: 2025-07-12
- **Last Updated**: 2026-02-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 🚀 WebCurl - 极简网页版API调试及Mock神器
> **⚡ 一个文件搞定所有API请求和Mock服务需求 | 🎯 轻量级选择 | 🔒 数据本地化,安全无忧**
[](https://golang.org)
[](LICENSE)
[]()
> **联系我: QQ 774309635**
## ✨ 为什么选择 WebCurl?
还在为API调试工具而烦恼吗?传统工具太臃肿?curl命令行太复杂?试试 **WebCurl** 吧!
🎉 **一个8MB的二进制文件 = 完整的API测试和Mock解决方案**
💡 **源码极简**:仅几个文件实现完整功能
### 🌟 核心优势对比
| 特性 | WebCurl | PostXXX | curl |
|------|---------|---------|------|
| **安装复杂度** | ⭐ 一个文件 | ⭐⭐⭐ 需要安装 | ⭐⭐ 命令行 |
| **跨域支持** | ✅ 完美解决 | ✅ 原生支持 | ✅ 原生支持 |
| **文件大小** | 8MB | 200MB+ | 系统自带 |
| **离线使用** | ✅ 完全离线 | ❌ 需要登录 | ✅ 完全离线 |
| **数据安全** | ✅ 本地存储 | ❌ 云端同步 | ✅ 本地存储 |
| **信创兼容** | ✅ 完美支持 | ❌ 有限支持 | ✅ 完美支持 |
| **IPv6支持** | ✅ 完美支持 | ✅ 支持 | ✅ 原生支持 |
| **源码简洁** | ✅ 仅4个文件 | ❌ 复杂项目 | ❌ 复杂项目 |
| **实时通信** | ✅ WebSocket+SSE | ❌ 仅HTTP | ❌ 仅HTTP |
| **调试接口** | ✅ 内置EchoServer | ❌ 需额外工具 | ❌ 需额外工具 |
| **Mock服务** | ✅ 支持自定义 | ❌ 需额外工具 | ❌ 需额外工具 |
## 📖 简介
本项目是一个极致轻量、跨平台、无依赖的 HTTP 请求转发与调试工具,**本质上就是一个网页版的API测试及Mock调试工具**,适合接口开发、调试、测试等多种场景。
- **前端**:纯原生 HTML+JS+CSS,无任何第三方库或依赖,开箱即用,加载速度极快。
- **后端**:仅使用 Golang 标准库,无任何第三方依赖,安全可靠。
- **源码极简**:整个项目仅包含4个文件,代码结构清晰,易于理解和维护。
- **产物**:编译后仅有一个约 8M 的单一二进制文件(含前端页面),无需安装、无需环境、无需依赖,直接运行。
- **平台支持**:支持 Windows、Linux、MacOS、ARM、x86_64、信创(国产芯片/操作系统)等主流及国产平台,真正做到"一次编译,到处运行"。
- **网络支持**:完美支持 IPv4 和 IPv6 网络协议,适应各种网络环境。
- **实时通信**:原生支持 WebSocket 和 SSE(Server-Sent Events),满足实时数据推送需求。
- **内置调试服务**:集成强大的EchoServer,提供完整的请求回显和响应控制功能。
- **适用场景**:接口联调、API 测试、前端跨域调试、信创环境接口测试、离线/内网环境接口调试等。
- **数据本地化存储,保障安全**:所有接口信息、历史记录、变量、全局头等均仅存储于本地浏览器(localStorage),不会同步到云端或外部服务器,保障接口数据的私密性与安全性,适合企业内网、敏感环境使用。
**主要用途**:API 测试与调试,替代某些需要登录才能使用工具(xxxxMan,xxxFox)
- 支持多种请求体格式(form-data、x-www-form-urlencoded、json、text、xml、binary)
- 支持文件上传、下载
- 支持请求重试、超时、SSL 验证、重定向等高级选项
- 支持 WebSocket 和 SSE(Server-Sent Events)实时通信
- 内置美观易用的前端页面,支持接口历史、变量、全局头、接口集合管理
- 支持命令行参数自定义监听端口、静态目录、日志、SSL 等
- **内置EchoServer调试服务**:提供完整的请求回显、响应控制、流式通信功能
---

---

---

---
### 打赏我:
* **每一个开源项目的背后,都有一群默默付出、充满激情的开发者。他们用自己的业余时间,不断地优化代码、修复bug、撰写文档,只为让项目变得更好。如果您觉得我的项目对您有所帮助,如果您认可我的努力和付出,那么请考虑给予我一点小小的打赏,友情提示:打赏不退,怕被媳妇查到大额支出🤡,如果需要技术支持,需要收费哦**

[项目推荐: https://github.com/o8oo8o/WebSSH 一个网页版的SSH管理工具](https://github.com/o8oo8o/WebSSH "一个网页版的SSH管理工具")
## 🛠️ 功能特性
### 🔥 核心功能
- **🌐 网页版 体验**:无需安装客户端,浏览器即用,界面美观,功能丰富。
- **🔄 HTTP 请求转发**:前端通过 `/api/forward` 接口将请求参数提交给后端,后端代为转发并返回结果,突破浏览器跨域限制。
- **支持 HTTP CONNECT 代理隧道**:支持 HTTP CONNECT 方法,可作为 HTTPS/SSH 等协议的代理隧道,适用于 curl、ssh、ncat 等工具的代理转发。
- **📁 多种请求体支持**:支持 `form-data`(含多文件上传)、`x-www-form-urlencoded`、`json`、`text`、`xml`、`binary`。
- **🔧 请求头自定义**:支持自定义任意请求头。
- **📤 文件上传/下载**:支持多文件上传,响应内容可直接下载。
- **🔄 请求重试与超时**:可配置重试次数、重试间隔、超时时间。
- **🔒 SSL 验证与重定向**:可选择是否校验 SSL 证书、是否自动跟随重定向。
- **📚 前端功能丰富**:接口历史、接口集合、变量替换、全局请求头、导入导出等。
- **⚙️ 命令行灵活配置**:支持自定义监听地址、端口、静态目录、日志、SSL 证书等。
- **⚡ 极致轻量**:单一二进制文件,体积仅约 10M,部署、迁移、分发极其方便。
- **💾 无依赖、易运维**:无需数据库、无需外部依赖,直接运行。
- **🖥️ 跨平台/信创兼容**:支持主流操作系统及国产软硬件平台,适合信创环境、内网、离线等特殊场景。
- **🌐 网络协议支持**:完美支持 IPv4 和 IPv6 网络协议,适应各种网络环境。
- **🔌 实时通信支持**:原生支持 WebSocket 和 SSE(Server-Sent Events),满足实时数据推送需求。
### 🎯 EchoServer 调试服务
- **🔄 智能请求回显**:自动解析并回显请求的URL、方法、请求头、请求体(文本、表单、文件、二进制等)。
- **🎛️ 灵活响应控制**:支持通过自定义请求头或URL参数灵活控制响应内容和行为。
- **📊 多种响应格式**:支持JSON、XML、Text等多种响应格式。
- **⏱️ 响应延迟控制**:可自定义响应延迟时间,模拟网络延迟场景。
- **📥 下载响应控制**:支持将响应内容作为文件下载。
- **🔌 流式通信支持**:SSE和WebSocket接口支持流式数据推送。
- **🎯 自定义数据队列**:支持预设响应数据,实现自定义流式推送。
- **🛡️ 健壮性保障**:内置panic恢复机制,防止服务崩溃。
### 🌐 静态文件服务器
- **📁 完整文件服务**:类似Nginx的静态文件服务器功能,支持所有常见文件类型。
- **🎨 丰富MIME支持**:自动识别HTML、CSS、JS、图片、音频、视频、字体等文件类型。
- **🔒 安全防护**:防止路径遍历攻击,确保文件访问安全。
- **⚡ 高性能**:支持大文件传输,内置缓存控制。
- **🌍 CORS支持**:内置跨域资源共享支持,适合前端开发。
- **📱 移动友好**:支持移动设备访问,响应式设计。
### 🌍 Mock服务
- **支持 Web 配置**:内置 Admin 控制台(默认 `/mock/`),在浏览器中拉取、编辑、格式化 `mock.json`,无需改本地文件即可改配置。
- **支持 Web 热重载**:在 Admin 中提交配置后,服务将新 JSON 写入文件并热重启 Mock 服务,无需手动重启进程。
- **支持 Web 启停控制**:通过 Admin 的 Start / Stop / Restart 控制 Mock 服务生命周期,Admin 自身常驻,Mock 停止后仍可管理。
- **纯 JSON 配置**:所有 Mock 行为由一份 `mock.json` 定义,无代码即可增删路由、改响应、挂静态资源与 WebSocket/SSE。
- **多端口多协议监听**:`listen` 支持多组 host/port,每组可配 `http` 与 `https`,并支持 `cert_file`/`key_file` 配置 TLS。
- **REST 路径参数**:路由 path 支持 gorilla/mux 风格(如 `/api/user/{id}`),路径参数自动注入为 `param.*` 供模板与条件使用。
- **请求预匹配(match)**:可按 headers / query / body 配置 match 条件(headers/query 支持正则,body 支持点路径与正则),全部满足才命中路由,否则 404。
- **路由级与响应级 when**:路由可配 `when` 不满足则 403;响应可配多条 `when` 分支,按顺序匹配选一条返回,实现条件分流。
- **when 表达式**:when 的 value 支持等值或操作符前缀:`=`、`!=`、`>`、`<`、`~`(正则)、`contains`,可写如 `"query.age": ">18"`、`"header.X-Role": "~^admin$"`。
- **变量提取(extract)**:从 body/query/header 按点路径提取字段到 `extract.*`,供 when、响应 body/headers/cookies、模板统一使用。
- **多响应分支**:单路由下可配置多条 response,每条可有自己的 when、status、headers、cookies、body、file、delay_ms,按 when 顺序命中即返回。
- **模板变量**:响应 body、headers、cookies、file 路径、template 内容中支持 `{{param.xxx}}`、`{{query.xxx}}`、`{{header.xxx}}`、`{{body.xxx}}`、`{{form.xxx}}`、`{{extract.xxx}}` 等命名空间。
- **强类型占位符**:支持 `{{@int:key}}`、`{{@float:key}}`、`{{@bool:key}}`,在 JSON 响应中输出数字或布尔类型而非字符串。
- **模板文件(@filename)**:body 或 template 可写 `@payloads/xxx.json`,从本地文件读取内容并做变量替换后返回,便于维护大块响应。
- **响应直接回文件**:`responses[].file` 可指定本地文件路径(可含模板变量),直接将文件内容作为 HTTP 响应体返回。
- **响应延迟(delay_ms)**:可为单条 response 配置毫秒级延迟,用于模拟慢接口或超时场景。
- **文件下载限速(speed_kbps)**:可为文件下载配置速率限制(单位 KB/s),模拟慢速网络环境。
- **缓冲式下载(buffered)**:支持缓冲式下载模式,先读取整个文件到内存再发送,适用于需要完整响应体的场景。
- **响应重定向**:通过 `status: 301/302` + `headers.Location` 实现重定向,模拟页面跳转场景。
- **响应压缩(compress)**:支持 gzip/deflate 压缩响应,可配置 `auto` 根据客户端 Accept-Encoding 自动选择。
- **分块传输(chunks)**:支持配置分块发送响应体,每块可单独设置延迟,模拟流式 API。
- **计数器模式(counter)**:支持按请求次数返回不同响应,模拟重试场景、服务恢复场景。
- **概率响应(probability)**:支持按概率权重随机返回响应,模拟不稳定服务、A/B 测试。
- **请求日志持久化(log)**:支持将请求日志持久化到文件,支持日志轮转和格式配置。
- **响应 Cookie**:支持在 response 中配置 cookies 数组(name/value/path/domain/expires/max_age/secure/http_only/same_site),值支持模板变量。
- **静态资源托管**:`static` 可将 URL 前缀映射到本地目录,支持自定义 headers、allow_methods、download 模式(Content-Disposition: attachment)。
- **CORS 预检兜底**:对 OPTIONS 请求做统一 CORS 头兜底,便于前端跨域调用 Mock。
- **表单与文件上传**:自动解析 `multipart/form-data` 与 `application/x-www-form-urlencoded`,注入 `form.xxx` 及 `form.xxx.filename`/`form.xxx.size`,可在 when 与模板中使用。
- **WebSocket Mock**:可配置 websockets 数组,path 支持路径参数,按 script 剧本顺序执行 send、await(正则或 JSON 等值)、delay_ms、timeout_ms、close,消息内容支持模板变量。
- **SSE(Server-Sent Events)**:可配置 sse 数组,支持 path/method/match/headers/status/cookies,events 可配 id/event/data/retry/delay_ms,data 支持模板,可选 repeat 循环推送。
- **无配置文件启动**:若 `mock.json` 不存在,以空配置 `{}` 启动,仅开放 Admin,便于首次在 Web 中编辑并保存配置。
### 🧰 常用工具(WebTools 工具箱)
访问 `http://localhost:4444/tool` 即可使用以下工具:
#### 📝 文本/编码类
- **JWT解析**:一键解析 JWT Token,查看 Header 和 Payload 信息
- **UUID生成**:批量生成标准 UUID,支持一键复制
- **时间戳转换**:时间戳与日期时间互转,支持秒/毫秒
- **Base64编解码**:文本编码/解码,支持文件转Base64、Base64转文件下载
- **URL编解码**:URL编码/解码,支持URL参数解析
- **正则表达式测试器**:实时匹配测试,支持 g/i/m/s 标志,显示捕获组
#### 🔐 加密/安全类
- **Hash计算**:支持文本和文件的 SHA-256/SHA-1/SHA-384/SHA-512 哈希计算
- **对称加密/解密**:
- 支持 AES-GCM/CBC/CTR 三种模式
- 支持 128/192/256 位密钥长度
- 支持文本和文件加密/解密
- **RSA 密钥生成**:生成 2048/4096 位公私钥对(PEM格式)
- **RSA-OAEP 加密/解密**:公钥加密、私钥解密,支持文本和小文件
- **HMAC 消息认证码**:支持 SHA-256/384/512,生成和验证消息认证码
- **数字签名**:
- 支持 RSA-PSS、RSASSA-PKCS1-v1_5、ECDSA、Ed25519
- 生成密钥对、签名、验证
- **密钥交换**:ECDH P-256/384/521、X25519,双方协商共享密钥
- **密钥派生**:PBKDF2/HKDF,从密码派生加密密钥
- **Token生成器**:自定义长度和字符类型,生成随机 Token
#### 📊 数据格式类
- **JSON格式化**:JSON美化、压缩、转义/反转义
#### 🎨 图像/多媒体类
- **图片处理**:
- 图片压缩(可调质量,支持 JPEG/PNG/WebP)
- 尺寸调整(支持保持宽高比)
- 图片裁剪(自定义区域)
- **录屏录像**:
- 屏幕录制(支持系统音频)
- 摄像头录像(支持选择设备)
- 录制列表管理(播放/下载/删除)
#### 🎨 其他工具
- **颜色转换器**:HEX/RGB/HSL 颜色互转
- **客户端信息**:
- 浏览器信息(UA、平台、语言、硬件并发数等)
- 屏幕信息(分辨率、色深、像素比)
- 网络状态(网络类型、下行速度、RTT延迟)
- 媒体设备(摄像头、麦克风、扬声器)
- 电池状态(电量、充电状态)
- **服务端信息**:
- 时间信息(服务器时间、客户端时间、时间差异)
- 操作系统(类型、架构、家族)
- Go运行时(版本、Goroutines、GC状态)
- CPU信息(核心数、字节序)
- 系统内存(总内存、可用内存、使用率)
- 网络接口(所有网卡信息、IP地址)
- 进程信息(PID、工作目录、执行路径)
- 用户信息(UID、GID、用户名)
- 环境变量(所有环境变量列表)
---
## 🚀 快速开始
### 1️⃣ 编译 & 运行(30秒搞定)
**源码结构极简**:
```
WebCurl/
├── index.html # 前端界面 WebCurl(纯原生HTML+JS+CSS)
├── main.go # 后端服务 WebCurl
├── mock.html # 前端界面 WebMock(纯原生HTML+JS+CSS)
├── mock.go # 后端服务 WebMock
├── tool.html # 前端界面 WebTools(纯原生HTML+JS+CSS)
├── tool.go # 后端服务 WebTools
├── ws.go # 后端服务 WebSocket
└── mux.go # 后端服务 Mux路由
```
```bash
# 编译
go build
# 也可以使用 sh build.sh 命令
# 运行(默认 0.0.0.0:4444,内嵌前端页面)
./WebCurl
# 浏览器访问
http://localhost:4444
```
> **注** :构建所有平台可执行文件,请使用 `sh build.sh all` 命令,构建完成后在build目录下查找对应平台可执行文件。
### 2️⃣ 命令行参数
| 参数 | 说明 | 默认值 |
|---------------------|----------------------------------------|-----------------------|
| `--host` | 监听地址 | 0.0.0.0 |
| `--port` | 监听端口 | 4444 |
| `--webroot` | 静态文件根目录(为空用内嵌 index.html)| 空 |
| `--daemon` | 后台运行(支持 Windows/Linux/Mac) | false |
| `--echo-server` | 是否开启EchoServer调试服务 | true |
| `--log-level` | 日志级别(error, info, debug) | error |
| `--log-file` | 日志文件路径 | post_api.log |
| `--log-size` | 日志文件大小限制(K/M/G) | 100M |
| `--ssl-cert` | SSL 证书文件路径 | ssl_cert.pem |
| `--ssl-cert-key` | SSL 证书密钥路径 | ssl_cert.key |
| `--upload-dir` | form-data上传文件保存目录(为空仅透传) | 空 |
| `--stdout-log` | 是否在控制台打印日志,true为同时输出到控制台和文件,false仅输出到文件 | true |
#### 启动示例
```bash
# 默认(0.0.0.0:4444,内嵌index.html,开启EchoServer)
./WebCurl
# 指定端口和host
./WebCurl --host 127.0.0.1 --port 8888
# 指定静态目录
./WebCurl --webroot /tmp/www
# 控制日志是否输出到控制台
./WebCurl --stdout-log=false
# 开启静态文件服务器模式(指定目录)
./WebCurl --webroot /mnt/webroot
# 关闭EchoServer调试服务
./WebCurl --echo-server=false
# 后台运行(Linux/MacOS/Windows)
./WebCurl --daemon
# 组合
./WebCurl --host 0.0.0.0 --port 9000 --webroot /tmp/www --daemon --stdout-log=false
```
### 🐳 容器化部署 · 极速上云
### 🚀 一键 Docker 部署
WebCurl 天生适合容器化,支持 Docker/Kubernetes 等主流云原生环境,轻松实现弹性扩展与自动化运维!
#### 1️⃣ Docker 镜像构建与运行
```bash
# 构建镜像
docker build -t webcurl:2.2 .
# 运行容器(默认 0.0.0.0:4444)
docker run -d -p:4444:4444 --name webcurl webcurl:2.2
# 指定数据/静态目录挂载
docker run -d --name webcurl -p 4444:4444 -v /usr/share/nginx/html/:/usr/local/WebCurl/webroot webcurl:2.2 /usr/local/WebCurl/WebCurl --webroot=/usr/local/WebCurl/webroot
```
#### 2️⃣ Kubernetes 极速部署
WebCurl 完美兼容 K8S,支持无状态部署、弹性伸缩、健康检查等企业级需求。
**示例 Deployment 配置:**
```yaml
######################################
apiVersion: apps/v1
kind: Deployment
metadata:
name: webcurl
spec:
replicas: 1
selector:
matchLabels:
app: webcurl
template:
metadata:
labels:
app: webcurl
spec:
containers:
- name: webcurl
image: webcurl:2.2
ports:
- containerPort: 4444
# args: ["/usr/local/WebCurl/WebCurl","--echo-server=true","--port=4444"]
---
apiVersion: v1
kind: Service
metadata:
name: webcurl
spec:
type: NodePort
ports:
- port: 4444
targetPort: 4444
nodePort: 30444
selector:
app: webcurl
######################################
```
> 只需 `kubectl apply -f webcurl.yaml`,即可在 K8s 集群中弹性部署 WebCurl!
### 🌈 容器化优势
- **极致轻量**:单一二进制+极简镜像,启动快、资源占用低
- **云原生友好**:无状态设计,天然适配 K8S、Docker、OpenShift 等平台
- **弹性扩展**:支持副本横向扩展,轻松应对高并发
- **自动化运维**:支持健康检查、日志挂载、配置注入
- **一键迁移**:镜像即服务,随时随地部署到任意云/集群/本地
---
**WebCurl,让 API 调试与测试像部署静态网站一样简单,轻松上云,随时随地,安全高效!**
---
## 🎯 适用场景
### 💼 企业级应用
- **内网环境**:数据不出内网,安全可控
- **信创环境**:完美支持国产芯片和操作系统
- **离线部署**:无网络环境也能正常使用
- **团队协作**:配置可导出分享,便于团队统一
### 👨💻 开发者日常
- **接口联调**:前后端接口调试必备
- **API测试**:自动化测试前的接口验证
- **跨域调试**:完美解决前端跨域问题
- **文件上传测试**:支持多文件上传测试
- **接口调试**:EchoServer提供完整的请求回显和响应控制
### 🔧 运维测试
- **接口监控**:定期测试关键接口状态
- **性能测试**:支持重试和超时配置
- **SSL测试**:SSL证书验证测试
- **重定向测试**:自动跟随重定向测试
- **实时通信测试**:WebSocket连接和SSE事件流测试
- **网络延迟模拟**:EchoServer支持响应延迟控制
### 🌐 静态文件服务
- **网站托管**:快速部署静态网站,支持HTML、CSS、JS等
- **文件分享**:企业内部文件分享和下载服务
- **开发环境**:前端开发时的本地文件服务器
- **文档服务**:API文档、技术文档的在线访问
- **资源托管**:图片、视频、音频等多媒体资源托管
- **CDN替代**:小型项目的CDN服务替代方案
---
## 📖 前端使用说明
### 1. 访问页面
启动后,浏览器访问 `http://localhost:4444`,即可进入 WebCurl 风格的调试页面。
启动后,浏览器访问 `http://localhost:4444/tool`,即可进入 WebTools 页面, 提供一些常用的工具。
启动后,浏览器访问 `http://localhost:4444/mock`,即可进入 Mock 服务器json配置页面,详细配置见下文。
启动后,浏览器访问 `http://localhost:4444/doc`,即可下载 WebCurl 文档readme.md文件。
### 2. 请求模式自动切换
- 前端会自动请求 `/api/mode`,如返回 `{ "mode": "proxy" }`,则所有请求将通过后端 `/api/forward` 转发,解决跨域问题。
- 否则,前端直接用 fetch 发起请求。
### 3. 发送请求(代理模式)
- 支持 GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS 等方法
- 支持多种请求体格式、文件上传、请求头自定义
- 支持变量替换、全局头、接口集合、历史记录等
- 支持请求参数、重试、超时、SSL 验证、重定向等高级选项
---
## 🔧 后端接口说明
### 1. `/api/forward`(POST)
用于前端通过 form-data 方式提交请求参数,由后端转发到目标接口。
#### 支持的 form-data 字段
| 字段名 | 类型/说明 | 示例/说明 |
|----------------|------------------|------------------------------------------------|
| url | string | 目标接口地址 |
| time_out | int | 超时时间(秒),0为不超时 |
| retry_count | int | 重试次数,0为不重试 |
| retry_delay | int | 重试间隔(秒),0为无间隔 |
| method | string | 请求方法,默认 GET |
| body_type | string | 请求体类型,见下表 |
| headers | json字符串 | `[{"name":"X-Token","value":"abc"}]` |
| file_info | json字符串 | `[{"field_name":"files","file_name":"a.txt"}]` |
| files | 文件 | 支持多文件上传 |
| body | string | 请求体内容(json/xml/text等) |
| verify_ssl | Y/N | 是否校验SSL,默认Y |
| follow_redirect| Y/N | 是否自动跟随重定向,默认Y |
| stream | boolean | 是否流式响应,默认false |
#### body_type 支持
- `form-data`:多文件上传,表单参数
- `x-www-form-urlencoded`:标准表单
- `json`:application/json
- `text`:text/plain
- `xml`:application/xml
- `binary`:二进制文件上传
- `none`:无请求体
### 2. `/api/mode`(GET)
返回当前后端模式,前端据此判断是否需要通过后端转发。
**返回示例**
```json
{ "mode": "proxy" }
```
### 3. EchoServer 调试接口
#### 3.1 `/api/echo`(所有HTTP方法)
**功能**:智能请求回显,支持灵活的响应控制。
**特性**:
- 支持所有HTTP方法(GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS等)
- 自动解析并回显请求的URL、方法、请求头、请求体
- 支持文本、表单、文件上传、二进制等多种请求体类型
- 支持通过自定义请求头或URL参数灵活控制响应
**响应控制参数**:
| 请求头 | 作用 | 示例值 |
|-------------------------|---------------------------------------------------|--------------------------------|
| X-Response-Status-Code | 控制HTTP响应状态码 | 201、404、500 |
| X-Response-Location | 设置响应Location头 | https://www.xx.com |
| X-Response-Headers | 批量设置响应头(JSON字符串,键值对) | {"X-Foo":"Bar"} |
| X-Response-Type | 控制响应体格式(json/xml/text) | json、xml、text |
| X-Response-Sleep | 控制响应延迟(单位:毫秒) | 200、1000 |
| X-Response-Body | 指定Base64编码的响应body内容(优先级最高) | `eyJtc2ciOiJoZWxsbyJ9` |
| X-Response-Download | 控制响应为下载,指定下载文件名 | data.txt、result.json |
**参数传递方式**:
- 支持请求头方式:`curl -H "X-Response-Status-Code: 202" http://localhost:4444/api/echo`
- 支持URL参数方式:`curl "http://localhost:4444/api/echo?X-Response-Status-Code=202"`
- **请求头优先级高于URL参数**
**基本用法示例**:
```bash
# 普通请求
curl -X GET http://localhost:4444/api/echo
curl -X POST -d 'Hello World' http://localhost:4444/api/echo
# 表单与文件上传
curl -X POST -F 'text=Hello' -F 'file=@/path/to/file' http://localhost:4444/api/echo
# 控制响应状态码
curl -H "X-Response-Status-Code: 202" http://localhost:4444/api/echo
# 控制响应类型
curl -H "X-Response-Type: xml" http://localhost:4444/api/echo
curl -H "X-Response-Type: text" -d "a simple text body" http://localhost:4444/api/echo
# 控制响应延迟
curl -H "X-Response-Sleep: 500" http://localhost:4444/api/echo
# 自定义响应体(Base64编码)
curl -H "X-Response-Body: eyJtc2ciOiJoZWxsbyJ9" http://localhost:4444/api/echo
# 下载响应内容
curl -H "X-Response-Download: data.txt" http://localhost:4444/api/echo -OJ
```
**响应结构**:
```json
{
"method": "POST",
"url": "http://localhost:4444/api/echo",
"headers": [
{"key": "Content-Type", "value": "application/json"},
...
],
"body": "Hello World"
}
```
#### 3.2 `/api/sse/echo`(SSE流式接口)
**功能**:Server-Sent Events流式回显,适合前端流式消费。
**特性**:
- 支持所有HTTP方法
- 返回SSE流,每条消息为JSON
- 支持自定义响应参数
- 支持流式数据推送
**额外参数**:
- `X-Response-Sse-Count`:SSE消息条数,默认100
- `X-Response-Sleep`:每条SSE消息间隔(毫秒),默认500
**用法示例**:
```bash
# 基本SSE请求
curl http://localhost:4444/api/sse/echo
# POST带body
curl -X POST -d 'Hello SSE' http://localhost:4444/api/sse/echo
# 控制SSE消息条数和间隔
curl -H "X-Response-Sse-Count: 5" -H "X-Response-Sleep: 1000" http://localhost:4444/api/sse/echo
# SSE上传文件
curl -X POST -F 'file=@/path/to/file' http://localhost:4444/api/sse/echo
```
#### 3.3 `/api/ws/echo`(WebSocket接口)
**功能**:WebSocket流式回显,适合前端WebSocket流式消费。
**特性**:
- 支持WebSocket协议,升级连接后推送多条消息
- 支持所有HTTP方法(WebSocket仅升级GET,其他方法通过header传递)
- 支持自定义响应参数
**额外参数**:
- `X-Response-Websocket-Count`:WebSocket消息条数,默认100
- `X-Response-Sleep`:每条消息间隔(毫秒),默认500
**用法示例**:
```bash
# WebSocket基本请求(需WebSocket客户端)
wscat -c ws://localhost:4444/api/ws/echo
# 控制消息条数和间隔
wscat -c "ws://localhost:4444/api/ws/echo?X-Response-Websocket-Count=5&X-Response-Sleep=1000"
# WebSocket自定义响应内容(Base64)
wscat -c "ws://localhost:4444/api/ws/echo?X-Response-Body=eyJtc2ciOiJoZWxsbyB3cyJ9"
```
#### 3.4 自定义数据队列接口
**`POST /api/sse/set`**:设置SSE响应的消息队列
**`POST /api/ws/set`**:设置WebSocket响应的消息队列
**功能**:预设响应数据,实现自定义流式推送。
**请求体格式**:
```json
[
{"value": {"v": 1, "data": "mydata"}},
{"value": {"v": 2, "data": 123}},
{"value": "mydata"},
{"value": null},
{"value": 123}
]
```
#### 3.5 响应模式控制
通过请求头 `X-Response-Mode` 控制SSE/WS响应行为:
- `default`(默认):原有回显逻辑,自动回显请求内容
- `user`:从预设队列中依次取出value作为响应体
- `react`:连接建立后等待用户推送数据(WebSocket专用)
**用法示例**:
```bash
# 设置SSE队列
curl -X POST -H "Content-Type: application/json" \
-d '[{"value":{"v":1,"data":"mydata"}}, {"value":{"v":2,"data":123}}]' \
http://localhost:4444/api/sse/set
# SSE user模式消费
curl -H "X-Response-Mode: user" http://localhost:4444/api/sse/echo
# 设置WS队列
curl -X POST -H "Content-Type: application/json" \
-d '[{"value":{"v":1,"data":"mydata"}}, {"value":{"v":2,"data":123}}]' \
http://localhost:4444/api/ws/set
# WS user模式消费
wscat -c "ws://localhost:4444/api/ws/echo?X-Response-Mode=user"
```
### 4. HTTP CONNECT 代理隧道支持
**功能**:支持 HTTP CONNECT 方法,可作为 HTTPS/SSH 等协议的代理隧道。
- 可直接作为 curl、ssh、ncat 等工具的 HTTP 代理服务器。
- 通过 CONNECT 建立 TCP 隧道,支持 HTTPS、WebSocket、SSH 等协议的转发。
- 适合企业内网、开发测试等需要代理隧道的场景。
**用法示例**:
```bash
# curl 通过 HTTP 代理访问 HTTPS 站点
curl -k -x http://localhost:4444 https://www.example.com
# ssh 通过 HTTP 代理
ssh -o "ProxyCommand ncat --proxy 127.0.0.1:4444 --proxy-type http %h %p" user@host
```
- 该功能无需额外配置,服务启动后自动支持。
- 日志中会记录 CONNECT 隧道的建立与关闭。
---
## 🎨 前端高级功能
- **📊 接口历史**:自动保存最近 50 条请求历史,支持一键加载、导入导出、清空
- **📚 接口集合**:支持多集合管理,接口保存、导入导出、删除
- **🔧 变量管理**:支持变量定义与替换,便于环境切换
- **🎛️ 全局请求头**:支持全局头配置,自动合并到每次请求
- **⚙️ 请求配置**:支持 SSL 验证、重定向、超时、重试、缓存、mode、credentials、referrerPolicy 等高级 fetch 选项
- **💾 导入导出**:支持全部配置一键导入导出,便于迁移和备份
- **🪝 Hook脚本**:支持前置脚本和后置脚本,可在请求发送前和响应返回后执行自定义逻辑
---
## 🪝 Hook 脚本功能
Hook 脚本功能允许你在请求发送前(Pre-Request)和响应返回后(Post-Response)执行自定义 JavaScript 代码,实现动态修改请求参数、提取响应数据、自动保存变量等高级功能。
### 📍 入口位置
在请求区域点击 **"脚本"** 标签页,可以看到:
- **Pre-Request Hook(前置脚本)**:请求发送前执行
- **Post-Response Hook(后置脚本)**:响应返回后执行
- **控制台输出**:显示脚本执行日志
### 🔧 Pre-Request Hook(前置脚本)
前置脚本在请求发送前执行,可以动态修改请求参数。
#### 函数签名
```javascript
function preRequest(request) {
// request 包含以下属性:
// - url: 请求URL(可修改)
// - method: 请求方法(可修改)
// - headers: 请求头对象(可修改)
// - body: 请求体(可修改)
// - bodyType: 请求体类型
// - settings: 请求设置
// - variables: 当前所有变量(只读)
// - utils: 工具函数集
return request; // 必须返回 request 对象
}
```
#### request 对象详解
| 属性 | 类型 | 可修改 | 说明 |
|------|------|--------|------|
| `url` | string | ✅ | 请求的完整URL |
| `method` | string | ✅ | HTTP方法:GET/POST/PUT/DELETE/PATCH等 |
| `headers` | object | ✅ | 请求头键值对对象 |
| `body` | string | ✅ | 请求体内容 |
| `bodyType` | string | ❌ | 请求体类型:none/json/text/xml/form/formdata/binary |
| `settings` | object | ✅ | 请求配置:timeout、retry_count等 |
| `variables` | object | ❌ | 当前所有启用的变量 |
| `utils` | object | ❌ | 工具函数集(详见下文) |
#### 使用示例
**示例1:动态添加时间戳签名**
```javascript
function preRequest(request) {
const timestamp = Date.now();
const nonce = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
// 添加签名相关请求头
request.headers['X-Timestamp'] = timestamp;
request.headers['X-Nonce'] = nonce;
request.headers['X-Signature'] = 'your_sign_algorithm_here';
request.utils.log('签名已生成');
return request;
}
```
**示例2:动态Token注入**
```javascript
function preRequest(request) {
// 从变量中获取token
const token = request.utils.getVariable('auth_token');
if (token) {
request.headers['Authorization'] = 'Bearer ' + token;
request.utils.log('已注入Token:', token.substring(0, 20) + '...');
} else {
request.utils.warn('未找到auth_token变量,请先登录');
}
return request;
}
```
**示例3:环境切换**
```javascript
function preRequest(request) {
const env = request.utils.getVariable('current_env') || 'dev';
const baseUrls = {
dev: 'http://localhost:8080',
test: 'http://test.example.com',
prod: 'https://api.example.com'
};
const baseUrl = baseUrls[env] || baseUrls.dev;
// 替换URL中的占位符
request.url = request.url.replace('{{base_url}}', baseUrl);
request.utils.log('当前环境:', env, 'BaseURL:', baseUrl);
return request;
}
```
**示例4:修改请求体**
```javascript
function preRequest(request) {
if (request.body && request.bodyType === 'json') {
try {
const body = JSON.parse(request.body);
body.timestamp = Date.now();
body.clientId = 'web-app';
request.body = JSON.stringify(body);
request.utils.log('请求体已修改');
} catch (e) {
request.utils.error('JSON解析失败:', e.message);
}
}
return request;
}
```
---
### 📤 Post-Response Hook(后置脚本)
后置脚本在响应返回后执行,可以处理响应数据、提取变量等。
#### 函数签名
```javascript
function postResponse(response) {
// response 包含以下属性:
// - status: HTTP状态码(只读)
// - statusText: 状态文本(只读)
// - headers: 响应头对象(只读)
// - body: 响应体字符串(可修改显示内容)
// - duration: 响应耗时ms(只读)
// - contentType: 内容类型(只读)
// - json: JSON解析后的对象(只读,仅JSON响应时可用)
// - utils: 工具函数集
return response; // 必须返回 response 对象
}
```
#### response 对象详解
| 属性 | 类型 | 可修改 | 说明 |
|------|------|--------|------|
| `status` | number | ❌ | HTTP状态码:200、404、500等 |
| `statusText` | string | ❌ | 状态文本:OK、Not Found等 |
| `headers` | object | ❌ | 响应头键值对对象 |
| `body` | string | ✅ | 响应体内容 |
| `duration` | number | ❌ | 响应耗时(毫秒) |
| `contentType` | string | ❌ | Content-Type响应头值 |
| `json` | object | ❌ | JSON解析后的对象(仅当响应是JSON时) |
| `utils` | object | ❌ | 工具函数集(详见下文) |
#### 使用示例
**示例1:自动提取并保存Token**
```javascript
function postResponse(response) {
const utils = response.utils;
// 检查响应是否成功
if (response.status === 200 && response.json) {
// 尝试从不同位置提取token
const token = response.json.token ||
response.json.data?.token ||
response.json.access_token ||
response.json.result?.accessToken;
if (token) {
utils.setVariable('auth_token', token);
utils.success('Token已提取并保存');
}
}
// 检测token过期
if (response.status === 401) {
utils.warn('Token可能已过期,请重新登录');
utils.setVariable('token_expired', 'true');
}
return response;
}
```
**示例2:响应日志记录**
```javascript
function postResponse(response) {
const utils = response.utils;
utils.info('=== 响应信息 ===');
utils.info('状态码:', response.status);
utils.info('状态文本:', response.statusText);
utils.info('响应耗时:', response.duration, 'ms');
utils.info('内容类型:', response.contentType);
if (response.json) {
utils.info('JSON数据:', JSON.stringify(response.json, null, 2));
} else if (response.body) {
utils.info('响应体长度:', response.body.length, '字符');
}
return response;
}
```
**示例3:自动保存响应字段**
```javascript
function postResponse(response) {
const utils = response.utils;
if (response.json && response.json.data) {
const data = response.json.data;
// 自动保存常用字段到变量
const fieldsToSave = ['id', 'userId', 'userName', 'email', 'roleId'];
fieldsToSave.forEach(field => {
if (data[field] !== undefined) {
utils.setVariable('last_' + field, data[field]);
}
});
utils.success('已自动保存响应字段到变量');
}
return response;
}
```
**示例4:错误处理与重试标记**
```javascript
function postResponse(response) {
const utils = response.utils;
if (response.status >= 400) {
utils.error('请求失败:', response.status);
// 保存错误信息
utils.setVariable('last_error_status', response.status);
utils.setVariable('last_error_time', new Date().toISOString());
if (response.json && response.json.message) {
utils.error('错误信息:', response.json.message);
utils.setVariable('last_error_msg', response.json.message);
}
} else {
utils.success('请求成功');
utils.setVariable('last_error_status', '');
}
return response;
}
```
---
### 🛠️ 工具函数集(utils)
Hook 脚本中可通过 `request.utils` 或 `response.utils` 访问工具函数集。
#### 日志函数
| 函数 | 说明 | 示例 |
|------|------|------|
| `log(...args)` | 输出普通日志(蓝色) | `utils.log('消息')` |
| `info(...args)` | 输出信息日志(蓝色) | `utils.info('信息')` |
| `success(...args)` | 输出成功日志(绿色) | `utils.success('成功!')` |
| `warn(...args)` | 输出警告日志(黄色) | `utils.warn('警告')` |
| `error(...args)` | 输出错误日志(红色) | `utils.error('错误')` |
#### 变量操作函数
| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `setVariable(name, value)` | name: 变量名, value: 变量值 | void | 设置变量(自动刷新变量表格) |
| `getVariable(name)` | name: 变量名 | string\|undefined | 获取变量值(仅启用的变量) |
| `getAllVariables()` | 无 | object | 获取所有启用的变量 |
**变量操作示例:**
```javascript
// 设置变量
utils.setVariable('api_key', 'sk-xxx');
utils.setVariable('user_id', '12345');
// 获取变量
const apiKey = utils.getVariable('api_key');
const userId = utils.getVariable('user_id');
// 获取所有变量
const allVars = utils.getAllVariables();
// 返回: { api_key: 'sk-xxx', user_id: '12345', ... }
```
#### 全局请求头操作函数
| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `setGlobalHeader(name, value)` | name: 请求头名, value: 请求头值 | void | 设置全局请求头(自动刷新请求头表格) |
| `getGlobalHeader(name)` | name: 请求头名 | string\|undefined | 获取全局请求头值(仅启用的) |
| `getAllGlobalHeaders()` | 无 | object | 获取所有启用的全局请求头 |
**全局请求头操作示例:**
```javascript
// 设置全局请求头
utils.setGlobalHeader('Authorization', 'Bearer token_xxx');
utils.setGlobalHeader('X-API-Key', 'key_xxx');
// 获取全局请求头
const auth = utils.getGlobalHeader('Authorization');
// 获取所有全局请求头
const allHeaders = utils.getAllGlobalHeaders();
// 返回: { Authorization: 'Bearer token_xxx', ... }
```
---
### 📝 完整使用示例
#### 场景1:OAuth2 自动化认证
```javascript
// === Pre-Request Hook ===
function preRequest(request) {
const utils = request.utils;
const token = utils.getVariable('oauth_token');
const tokenExpire = utils.getVariable('token_expire_time');
const now = Date.now();
// 检查token是否存在且未过期
if (token && tokenExpire && now < parseInt(tokenExpire)) {
request.headers['Authorization'] = 'Bearer ' + token;
utils.log('使用缓存的Token');
} else {
utils.warn('Token不存在或已过期,需要重新获取');
// 可以设置标记,让后续流程处理
utils.setVariable('need_refresh_token', 'true');
}
return request;
}
// === Post-Response Hook ===
function postResponse(response) {
const utils = response.utils;
// 如果是登录接口,自动保存token
if (response.json && response.json.access_token) {
const token = response.json.access_token;
const expiresIn = response.json.expires_in || 3600;
utils.setVariable('oauth_token', token);
utils.setVariable('token_expire_time', Date.now() + expiresIn * 1000);
utils.success('Token已保存,有效期:', expiresIn, '秒');
}
// 如果返回401,清除token
if (response.status === 401) {
utils.setVariable('oauth_token', '');
utils.setVariable('token_expire_time', '');
utils.warn('Token已失效,请重新登录');
}
return response;
}
```
#### 场景2:API签名认证
```javascript
// === Pre-Request Hook ===
function preRequest(request) {
const utils = request.utils;
// 获取API密钥
const appId = utils.getVariable('app_id') || 'your_app_id';
const appSecret = utils.getVariable('app_secret') || 'your_app_secret';
// 生成签名参数
const timestamp = Date.now().toString();
const nonce = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
// 简单签名示例(实际使用时建议使用更安全的算法)
const signStr = appId + timestamp + nonce + appSecret;
// 设置签名请求头
request.headers['X-App-Id'] = appId;
request.headers['X-Timestamp'] = timestamp;
request.headers['X-Nonce'] = nonce;
request.headers['X-Signature'] = signStr; // 实际使用时应该用MD5/SHA256等
utils.log('签名参数已生成');
utils.log('AppId:', appId);
utils.log('Timestamp:', timestamp);
utils.log('Nonce:', nonce);
return request;
}
```
#### 场景3:接口测试断言
```javascript
// === Post-Response Hook ===
function postResponse(response) {
const utils = response.utils;
let passCount = 0;
let failCount = 0;
// 断言函数
function assert(condition, message) {
if (condition) {
utils.success('✓ ' + message);
passCount++;
} else {
utils.error('✗ ' + message);
failCount++;
}
}
utils.info('===== 开始断言测试 =====');
// 状态码断言
assert(response.status === 200, `状态码应为200,实际为${response.status}`);
// 响应时间断言
assert(response.duration < 1000, `响应时间应小于1000ms,实际为${response.duration}ms`);
// JSON结构断言
if (response.json) {
assert(response.json.code !== undefined, '响应应包含code字段');
assert(response.json.code === 0, `业务码应为0,实际为${response.json.code}`);
assert(response.json.data !== undefined, '响应应包含data字段');
// 数据字段断言
if (response.json.data) {
assert(response.json.data.id !== undefined, 'data应包含id字段');
assert(typeof response.json.data.id === 'string', 'id应为字符串类型');
}
}
utils.info('===== 断言测试完成 =====');
utils.info(`通过: ${passCount}, 失败: ${failCount}`);
// 保存测试结果
utils.setVariable('test_pass_count', passCount.toString());
utils.setVariable('test_fail_count', failCount.toString());
return response;
}
```
---
### 💾 数据持久化
Hook 脚本会随接口一起保存:
1. **历史记录**:发送请求后,Hook 脚本会保存到历史记录中
2. **接口集合**:保存接口到集合时,Hook 脚本会一起保存
3. **导入导出**:导出配置时会包含所有 Hook 脚本
---
### ⚠️ 注意事项
1. **必须返回对象**:`preRequest` 必须返回 `request`,`postResponse` 必须返回 `response`
2. **异步支持**:脚本支持 async/await,可以使用 Promise
3. **错误处理**:脚本执行出错不会影响请求,错误信息会显示在控制台
4. **安全限制**:脚本在浏览器沙箱中执行,无法访问敏感 API
5. **变量作用域**:`setVariable` 设置的变量可在后续请求中使用 `{{变量名}}` 引用
---
# Mock 服务器
**Mock** 是一个基于 Go 的 API Mock 服务:通过一份 JSON 配置即可提供 REST 接口、静态资源、WebSocket、SSE 等能力,并支持路径参数、条件匹配、模板变量、热重载与内置 Admin 控制台。
## 配置文件顶层结构(mock.json)
```jsonc
{
"listen": [ /* 服务监听:host/port/protocols/cert/key */ ],
"static": [ /* 静态目录挂载 */ ],
"routes": [ /* HTTP API Mock */ ],
"websockets": [ /* WebSocket 剧本 */ ],
"sse": [ /* Server-Sent Events */ ]
}
```
- 各顶层字段均可为空数组。
- 推荐在 Admin 中编辑(带校验与运行态同步),也可本地编辑后通过 Reload 或重启生效。
- JSON 中数字会解析为 `float64`,书写布尔/整型时注意类型。
---
## 监听服务(listen)
用于配置 Mock 服务监听的地址与协议。
```json
{
"listen": [
{
"host": "0.0.0.0",
"port": 8080,
"protocols": ["http", "https"],
"cert_file": "./certs/mock.crt",
"key_file": "./certs/mock.key"
}
]
}
```
| 字段 | 说明 |
|------|------|
| `host` | 监听地址:`localhost`、`127.0.0.1`、`::1`、域名或内网 IP |
| `port` | 端口 |
| `protocols` | 可同时包含 `http` 与 `https`;每个 listen 项会启动一个 Server |
| `cert_file` / `key_file` | HTTPS 证书与私钥路径;启用 `https` 时必填,否则启动会报错 |
---
## 静态资源(static)
将 URL 前缀映射到本地目录,用于托管 Swagger、图片、附件等。
```json
{
"static": [
{
"mount": "/assets/",
"dir": "./public",
"download": false,
"index_files": ["index.html", "home.html"],
"allow_methods": ["GET", "HEAD"],
"headers": { "Cache-Control": "max-age=86400" }
}
]
}
```
| 字段 | 说明 |
|------|------|
| `mount` | URL 前缀;不以 `/` 结尾时会自动补尾斜杠并做 301 重定向 |
| `dir` | 本地目录;请求 `/assets/app.js` 对应 `./public/app.js` |
| `download` | `true` 时所有响应带 `Content-Disposition: attachment` |
| `index_files` | 目录默认首页的查找顺序(可选) |
| `allow_methods` | 允许的 HTTP 方法,如只允许 GET/HEAD;未配置则不限制方法 |
| `headers` | 对静态响应统一添加的头部 |
---
## HTTP 路由(routes)
### 最小示例
```json
{
"method": "GET",
"path": "/api/user/{id}",
"responses": [
{ "status": 200, "body": { "id": "{{param.id}}", "ok": true } }
]
}
```
访问 `GET /api/user/42` 得到:`{ "id": "42", "ok": true }`。
### 方法、路径与 ANY
- `method`:`GET`、`POST`、`PUT`、`PATCH`、`DELETE` 等;配置会转为大写匹配。
- `method: "ANY"` 或空字符串:不限制 HTTP 方法。
- `path`:支持 gorilla/mux 路径,如 `/api/user/{id}/info/{section}`,`{id}` 等会注入为 `param.id`、`param.section`。
### 请求匹配(match)
在进入响应分支前,可要求 **headers / query / body** 同时满足条件;未配置 `match` 则视为通过。
```json
"match": {
"headers": { "x-token": "^test-" },
"query": { "env": "^(dev|qa)$" },
"body": { "meta.version": "^v\\d+$", "items.0.type": "vip" }
}
```
| 维度 | 说明 |
|------|------|
| `headers` / `query` | 值为**正则**;若只做等值,建议写 `^value$` |
| `body` | 键为点路径或带索引路径:`foo.bar`、`items.0.id` 或 `items[0].id`;值可为正则或等值(非字符串会转成字符串比较) |
全部满足才命中该 route;否则返回 404。
### 路由级条件(when)
在 route 上可配置 `when`,与响应分支的 when 同语义:不通过则返回 403。
```json
"when": {
"query.age": ">18",
"header.X-Role": "~^admin|root$"
}
```
when 的 value 支持等值或操作符前缀:`=`、`!=`、`>`、`<`、`~`(正则)、`contains`(字符串包含)。
### 变量提取(extract)
从请求 body/query/header 中按「点路径」提取字段,注入为 `extract.`,供响应/模板使用。
```json
"extract": {
"from": "body",
"rules": {
"customerId": "customer.id",
"ip": "X-Real-IP"
}
}
```
- `from`:`body`、`query`、`header` 之一。
- `rules`:键为变量名,值为**点路径**(如 `customer.id`、`root.users.0.info.data`),**不要**使用 `$.` 前缀。
- 提取结果在模板中以 `{{extract.customerId}}`、`{{extract.ip}}` 使用。
### 变量命名空间
以下变量可在 `when`、`headers`、`cookies`、`body`、`file`、`template` 中使用。
| 命名空间 | 来源 | 说明 |
|----------|------|------|
| `param.xxx` | 路径参数 `/foo/{id}` | mux 自动注入 |
| `query.xxx` | URL 查询参数 | 多值取第一个 |
| `header.xxx` | 请求头 | 与客户端一致(大小写) |
| `body.xxx` | JSON 请求体顶层字段 | 嵌套需通过 extract 或 match.body 点路径 |
| `form.xxx` | 表单字段 | `multipart/form-data`、`application/x-www-form-urlencoded` |
| `form.xxx.filename` / `form.xxx.size` | 上传文件信息 | 仅 multipart 时可用 |
| `extract.xxx` | extract 规则结果 | 来自 `extract.rules` |
引用时建议带命名空间,如 `{{param.id}}`、`{{query.env}}`、`{{body.name}}`、`{{extract.customerId}}`。
### 内置函数模板(func)
除了从请求中提取变量,还支持在模板中调用内置函数,动态生成值。语法为 `{{func.函数名()}}` 或 `{{func.函数名(参数)}}`。
#### 支持的函数列表
| 函数 | 用法 | 说明 |
|------|------|------|
| `uuid` | `{{func.uuid()}}` | 生成 UUID v4 字符串 |
| `timestamp` | `{{func.timestamp()}}` | 获取当前时间戳(秒) |
| `timestamp_sec` | `{{func.timestamp_sec()}}` | 获取当前时间戳(秒) |
| `timestamp_ms` | `{{func.timestamp_ms()}}` | 获取当前时间戳(毫秒) |
| `now` | `{{func.now()}}` | 获取当前时间,默认格式 `2006-01-02 15:04:05` |
| `now` | `{{func.now(2006-01-02)}}` | 获取当前时间,自定义格式 |
| `date` | `{{func.date()}}` | 获取当前日期,默认格式 `2006-01-02` |
| `date` | `{{func.date(01/02/2006)}}` | 获取当前日期,自定义格式 |
| `time` | `{{func.time()}}` | 获取当前时间,默认格式 `15:04:05` |
| `time` | `{{func.time(15:04)}}` | 获取当前时间,自定义格式 |
| `random_int` | `{{func.random_int()}}` | 生成随机整数,默认范围 0-1000 |
| `random_int` | `{{func.random_int(1, 100)}}` | 生成随机整数,指定范围 |
| `random_string` | `{{func.random_string()}}` | 生成随机字符串,默认长度 16 |
| `random_string` | `{{func.random_string(32)}}` | 生成随机字符串,指定长度 |
#### 使用示例
```json
{
"path": "/api/order/create",
"method": "POST",
"responses": [
{
"status": 200,
"headers": {
"Content-Type": "application/json",
"X-Request-Id": "{{func.uuid()}}"
},
"body": {
"code": 0,
"message": "success",
"data": {
"orderId": "{{func.uuid()}}",
"orderNo": "ORD{{func.timestamp()}}",
"createdAt": "{{func.now(2006-01-02 15:04:05)}}",
"timestamp": {{func.timestamp()}},
"token": "{{func.random_string(32)}}",
"luckyNumber": {{func.random_int(1, 100)}}
}
}
}
]
}
```
#### 时间格式说明
Go 语言的时间格式使用特定的参考时间 `2006-01-02 15:04:05`,常用格式:
| 格式字符串 | 输出示例 |
|-----------|---------|
| `2006-01-02` | 2024-01-15 |
| `15:04:05` | 14:30:45 |
| `2006-01-02 15:04:05` | 2024-01-15 14:30:45 |
| `2006/01/02` | 2024/01/15 |
| `01/02/2006` | 01/15/2024 |
| `20060102150405` | 20240115143045 |
### 多响应分支(when)
`responses` 为数组时,按**顺序**匹配:每条 response 的 `when` 全满足则选中并返回;都不满足则使用第一条。
```json
"responses": [
{
"when": { "param.id": "007" },
"status": 200,
"body": { "msg": "for james bond" }
},
{
"when": { "query.age": ">18" },
"headers": { "X-Adult": "true" },
"body": { "ok": true }
},
{
"status": 404,
"body": { "error": "not found" }
}
]
```
- **when**:支持 `param.*`、`query.*`、`header.*`、`body.*`、`extract.*`、`form.*`;多键需全部满足。
- **value 写法**:
- 等值:`"param.id": "007"`;
- 表达式(操作符前缀):`"query.age": ">18"`、`"header.X-Role": "~^admin|root$"`、`"body.name": "contains:alice"`。
支持操作符:`=`、`!=`、`>`、`<`、`~`(正则)、`contains`。
- 若所有分支都不满足,实现上会回退到第一条 response,因此建议最后一条写兜底。
### 响应字段说明
| 字段 | 说明 |
|------|------|
| `status` | HTTP 状态码;可省略,默认 200 |
| `headers` | 任意响应头;值支持模板变量 |
| `cookies` | Cookie 数组,见下表;会做模板替换 |
| `delay_ms` | 延迟若干毫秒再返回,用于模拟慢接口 |
| `body` | 字符串或对象;递归替换模板变量后输出 |
| `file` | 本地文件路径(可含变量);直接将该文件作为响应体 |
| `template` | 模板文件名(无需写 `@`);读取文件内容后做变量替换再返回 |
| `speed_kbps` | 文件下载限速(单位 KB/s);模拟慢速网络环境 |
| `buffered` | 是否使用缓冲式下载(默认 false 流式);true 时先读取整个文件到内存再发送 |
| `compress` | 压缩方式:`gzip`、`deflate`、`auto`(根据客户端 Accept-Encoding 自动选择) |
| `chunks` | 分块传输配置数组;每块可设置 data 和 delay_ms |
| `counter` | 计数器配置;按请求次数选择不同响应 |
| `probability` | 概率权重(0-100);用于概率响应选择 |
**Cookie 配置**(下划线命名):
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` / `value` | string | 名称与值 |
| `path` / `domain` | string | 可选 |
| `expires` | string | 建议 RFC3339 |
| `max_age` | int | 秒 |
| `secure` / `http_only` | bool | 仅 HTTPS / 不可被 JS 读取 |
| `same_site` | string | `lax` / `strict` / `none` |
### 强类型模板占位符(int / float / bool)
默认 `{{key}}` 替换为字符串。若要在 JSON 中输出**数字**或**布尔**,可使用:
- `{{@int:key}}` → 整型(int64)
- `{{@float:key}}` → 浮点(float64)
- `{{@bool:key}}` → 布尔
规则:
- 若**整串**仅为一个强类型占位符(如 `"{{@int:query.age}}"`),则该字段在 JSON 中为对应类型(如 `123`、`true`)。
- 若与其它文字混用(如 `"age={{@int:query.age}}"`),则按字符串替换(如 `"age=77"`)。
- 解析失败时退化为普通字符串替换。
### 模板文件(@filename)
当 `body` 或 `template` 的值为以 `@` 开头的路径时,系统会读取该文件并做 `{{ }}` 替换后返回。
- 示例:`"body": "@payloads/small.json"`,文件内容可为 JSON/XML/文本。
- 若文件不存在,响应为 `"template file not found"`。
### 文件下载配置示例
支持三种下载模式:流式下载(默认)、缓冲式下载、限速下载。
```json
{
"routes": [
{
"method": "GET",
"path": "/download/normal",
"responses": [{
"file": "./files/test.zip",
"status": 200,
"headers": {
"Content-Disposition": "attachment; filename=test.zip"
}
}]
},
{
"method": "GET",
"path": "/download/buffered",
"responses": [{
"file": "./files/test.zip",
"status": 200,
"buffered": true,
"headers": {
"Content-Disposition": "attachment; filename=test.zip"
}
}]
},
{
"method": "GET",
"path": "/download/slow",
"responses": [{
"file": "./files/test.zip",
"status": 200,
"speed_kbps": 100,
"headers": {
"Content-Disposition": "attachment; filename=test.zip"
}
}]
},
{
"method": "GET",
"path": "/download/delayed-slow",
"responses": [{
"file": "./files/test.zip",
"status": 200,
"delay_ms": 3000,
"speed_kbps": 50,
"buffered": true,
"headers": {
"Content-Disposition": "attachment; filename=test.zip"
}
}]
}
]
}
```
**下载模式说明**:
| 模式 | 配置 | 特点 |
|------|------|------|
| 流式下载 | 默认(不配置) | 支持断点续传,内存占用小,适合大文件 |
| 缓冲式下载 | `buffered: true` | 先读取整个文件到内存再发送,适合小文件 |
| 限速下载 | `speed_kbps: 100` | 按 100KB/s 速率发送,模拟慢速网络 |
| 延时+限速 | `delay_ms` + `speed_kbps` | 先延时再限速下载 |
### 7.11 响应重定向
通过 `status` 和 `headers.Location` 实现重定向,模拟页面跳转场景。
```json
{
"routes": [
{
"method": "GET",
"path": "/redirect/demo",
"responses": [{
"status": 302,
"headers": {
"Location": "https://example.com/target"
}
}]
},
{
"method": "GET",
"path": "/redirect/old-api/{path}",
"responses": [{
"status": 301,
"headers": {
"Location": "/api/v2{{param.path}}"
}
}]
}
]
}
```
**重定向状态码**:
- `301`:永久重定向
- `302`:临时重定向
- `307`:临时重定向(保持请求方法)
- `308`:永久重定向(保持请求方法)
### 7.12 响应压缩
支持 gzip 和 deflate 压缩响应,可自动根据客户端 Accept-Encoding 选择。
```json
{
"routes": [
{
"method": "GET",
"path": "/api/compressed",
"responses": [{
"compress": "gzip",
"body": {
"message": "这是一个被压缩的响应",
"data": "大量数据..."
}
}]
},
{
"method": "GET",
"path": "/api/auto-compress",
"responses": [{
"compress": "auto",
"body": {
"message": "根据客户端 Accept-Encoding 自动选择压缩方式"
}
}]
}
]
}
```
### 7.13 分块传输
支持配置分块发送响应体,每块可单独设置延迟,模拟流式 API。
```json
{
"routes": [
{
"method": "GET",
"path": "/api/stream",
"responses": [{
"status": 200,
"headers": {
"Content-Type": "text/plain"
},
"chunks": [
{ "data": "第一块数据\n", "delay_ms": 0 },
{ "data": "第二块数据\n", "delay_ms": 1000 },
{ "data": "第三块数据\n", "delay_ms": 1000 },
{ "data": "第四块数据\n", "delay_ms": 1000 },
{ "data": "完成", "delay_ms": 500 }
]
}]
},
{
"method": "GET",
"path": "/api/stream-json",
"responses": [{
"status": 200,
"headers": {
"Content-Type": "application/x-ndjson"
},
"chunks": [
{ "data": "{\"event\":\"start\"}\n" },
{ "data": "{\"event\":\"data\",\"value\":1}\n", "delay_ms": 500 },
{ "data": "{\"event\":\"data\",\"value\":2}\n", "delay_ms": 500 },
{ "data": "{\"event\":\"end\"}\n", "delay_ms": 200 }
]
}]
}
]
}
```
### 7.14 计数器模式
支持按请求次数返回不同响应,模拟重试场景、服务恢复场景。
```json
{
"routes": [
{
"method": "GET",
"path": "/api/flaky",
"responses": [
{
"counter": { "key": "flaky-api", "max": 100, "loop": true },
"status": 500,
"body": { "error": "服务暂时不可用,请重试" }
},
{
"status": 502,
"body": { "error": "服务暂时不可用,网关错误,请重试" }
},
{
"status": 200,
"body": { "message": "服务已恢复", "counter": "{{counter}}" }
}
]
},
{
"method": "POST",
"path": "/api/counter/reset",
"responses": [{
"counter": { "key": "flaky-api", "reset": true },
"body": { "message": "计数器已重置" }
}]
}
]
}
```
**计数器配置**:
| 字段 | 类型 | 说明 |
|------|------|------|
| `key` | string | 计数器唯一标识;不同路由可共享同一计数器 |
| `max` | int | 最大值;达到后根据 loop 决定是否循环 |
| `loop` | bool | 是否循环;true 时达到 max 后重置为 1 |
| `reset` | bool | 是否重置;true 时将计数器重置为 0 |
**计数器变量**:
- `{{counter}}`:当前计数器值
- `{{counter.}}`:指定 key 的计数器值
### 7.15 概率响应
支持按概率权重随机返回响应,模拟不稳定服务、A/B 测试。
```json
{
"routes": [
{
"method": "GET",
"path": "/api/ab-test",
"responses": [
{
"probability": 70,
"body": { "version": "A", "message": "70% 概率返回此响应" }
},
{
"probability": 30,
"body": { "version": "B", "message": "30% 概率返回此响应" }
}
]
},
{
"method": "GET",
"path": "/api/unstable",
"responses": [
{
"probability": 80,
"status": 200,
"body": { "success": true }
},
{
"probability": 15,
"status": 500,
"body": { "error": "内部错误" }
},
{
"probability": 5,
"status": 503,
"body": { "error": "服务不可用" }
}
]
}
]
}
```
**概率说明**:
- `probability` 取值范围 0-100
- 概率权重按相对比例计算,不要求总和为 100
- 未设置 `probability` 的响应作为默认兜底
### 7.16 请求日志持久化
支持将请求日志持久化到文件,支持日志轮转和格式配置。
```json
{
"log": {
"enable": true,
"dir": "./logs",
"max_size": 10,
"max_files": 5,
"format": "json"
},
"routes": [
{
"method": "GET",
"path": "/api/test",
"responses": [{ "body": { "message": "test" } }]
}
]
}
```
**日志配置**:
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `enabled` | bool | false | 是否启用日志持久化 |
| `dir` | string | ./logs | 日志文件目录 |
| `max_size` | int | 10 | 单个日志文件最大大小(MB) |
| `max_files` | int | 5 | 保留的日志文件数量 |
| `format` | string | json | 日志格式:json 或 text |
### 7.17 表单与文件上传
- 对 `multipart/form-data` 与 `application/x-www-form-urlencoded` 会自动解析并注入 `form.xxx`。
- 上传文件会额外提供 `form..filename`、`form..size`。
- 可在响应或模板中引用这些变量。multipart 解析大小受 `-max-multipart` 限制。
---
## WebSocket Mock
基于 gorilla/websocket,路径与 mux 一致,支持路径参数与模板变量。
```json
{
"websockets": [
{
"path": "/ws/{room}",
"match": {
"query": { "token": ".+" },
"headers": { "Sec-WebSocket-Protocol": "^chat$" }
},
"script": [
{ "send": "{\"type\":\"welcome\",\"room\":\"{{param.room}}\"}" },
{ "await": { "type": "auth" }, "timeout_ms": 5000 },
{ "send": "{\"type\":\"ok\"}" },
{ "delay_ms": 1000, "send": "{\"type\":\"broadcast\"}" },
{ "close": true }
]
}
]
}
```
| 项 | 说明 |
|----|------|
| `path` | 与 HTTP 路由相同,可含 `{param}` |
| `match` | 建立连接前校验 headers/query(支持正则);不满足则 404 |
| `script` | 按顺序执行的动作序列 |
**script 动作**:
| 键 | 说明 |
|----|------|
| `send` | 发送一条文本消息(可 JSON 字符串);支持模板变量 |
| `await` | 阻塞等待客户端一条消息:值为**字符串**时按正则匹配整条文本;值为**对象**时按 JSON 字段等值匹配 |
| `timeout_ms` | 与 `await` 配合,超时后向客户端返回错误并结束 |
| `delay_ms` | 本动作执行前延迟(毫秒) |
| `close` | 为 `true` 时发送正常关闭帧并结束脚本 |
---
## Server-Sent Events(SSE)
```json
{
"sse": [
{
"path": "/sse/{topic}",
"method": "GET",
"match": { "query": { "client": ".+" } },
"headers": { "X-Topic": "{{param.topic}}" },
"status": 200,
"cookies": [],
"events": [
{ "id": "1", "event": "hello", "data": "topic={{param.topic}}", "retry": 1500 },
{ "event": "keepalive", "data": "ping", "delay_ms": 2000 },
{ "data": "done" }
],
"repeat": false
}
]
}
```
| 字段 | 说明 |
|------|------|
| `path` | 路径,可含路径参数 |
| `method` | 默认 `GET`,也可 `POST` 等 |
| `match` | 同 route:headers/query/body 全部满足才建立流 |
| `headers` / `cookies` / `status` | 可选;支持模板变量 |
| `events` | 事件列表;每条可含 `id`、`event`、`data`、`retry`、`delay_ms`;`data` 支持模板 |
| `repeat` | `true` 时 events 发送完后从头循环 |
---
## 模板变量与生命周期
变量注入顺序(与代码一致):
1. 路径参数 → `param.*`
2. Query → `query.*`,Header → `header.*`
3. 若方法允许 body,读取 JSON → `body.*`
4. 若为表单/上传 → `form.*`、`form.*.filename`、`form.*.size`
5. 按 `extract` 规则 → `extract.*`
同一请求中,`when`、`headers`、`cookies`、`body`、`file`、`template` 共用这一套变量表。
---
## 管理控制台(Admin UI)
- **地址**:`http://127.0.0.1:4444/mock/`
- **功能**:
- **拉取配置**:GET 当前 `mock.json` 内容及服务是否运行
- **Reload**:POST 新配置(JSON 字符串),写入文件并热重启 Mock 服务
- **Start / Stop / Restart**:控制 Mock 服务进程(Admin 自身常驻,不随 Mock 停止)
**API**:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/mock/api/config` | 返回 `{ "config": "", "running": bool }` |
| POST | `/mock/api/reload` | Body:`{ "config": "<完整 JSON 字符串>" }`,写入并重启 |
| POST | `/mock/api/start` / `stop` / `restart` | 启停/重启 Mock 服务 |
推荐流程:拉取配置 → 编辑/格式化 → Reload → 根据返回与日志确认是否 `ok`。
---
## 匹配与分流细则
- **路由**:gorilla/mux 逐条注册,路径参数 `{id}` 等自动注入 `param.*`。
- **match(预过滤)**:
- `match.headers` / `match.query`:值按**正则**匹配;等值请写 `^value$`。
- `match.body`:键为点路径(含数组索引);值若为字符串则按正则,否则按字符串化后等值;全部满足才命中,否则 404。
- **when(分支/路由级条件)**:支持 `param.*`、`query.*`、`header.*`、`body.*`、`extract.*`、`form.*`;多键全部满足才通过。value 可为等值或带操作符前缀:`=`、`!=`、`>`、`<`、`~`(正则)、`contains`;比较前会尝试数值解析(>、<)。
- **extract**:仅支持点路径(如 `customer.id`、`root.users.0.info`),不支持 `$.` 前缀;结果通过 `{{extract.}}` 使用。
- **模板替换范围**:`body`、`headers`、`cookies` 各字段及 `@模板文件` 内容均会替换。
---
## 运行时行为与日志
- 启动时会按条输出 `Register route: `,便于确认路由加载。
- 请求会打印路径参数注入结果(如 `DEBUG param injection`),便于排查模板未替换问题。
- 静态/WebSocket/SSE 注册、HTTPS 证书错误、Reload 失败等会在日志中输出。
- 长期运行建议配合 supervisor、systemd 或 Docker 做进程守护。
---
## Mock常见问题排查
| 现象 | 可能原因与处理 |
|------|----------------|
| 命中不到路由 | 检查 method 是否与配置一致(配置会转大写);`match.headers`/query 正则是否过严或大小写不一致 |
| 模板没替换 | 确认使用 `{{param.id}}` 等带命名空间写法;确认请求中确实带对应 query/header/body;看日志中的 `vars` 输出 |
| when 不生效 | when 的 value 支持等值或操作符前缀(`>`、`<`、`~`、`contains`);确保 body/form/extract 已注入(正常 HTTP 已内置) |
| 返回文件 404 | `responses[].file` 为相对路径时相对进程工作目录;可改为与 `mock.json` 同目录或绝对路径;Windows 注意反斜杠 |
| WebSocket await 超时 | 确认客户端按 script 顺序发消息;可增大 `timeout_ms`;查看日志中的 upgrade/await 相关输出 |
| CORS 预检 | 服务对 OPTIONS 做了兜底,返回常用 CORS 头;如需定制可在响应 `headers` 或静态 headers 中补充 |
---
## 🔍 项目常见问题
### ❓ 为什么需要后端转发?
> 由于浏览器同源策略,前端直接请求第三方接口会遇到 CORS 限制。通过本工具的后端转发,前端只需请求本地服务即可,后端再代为请求目标接口,绕过跨域限制。
### ❓ WebCurl变量如何使用,变量替换支持位置?
> 支持请求url,请求头,全局头替换,使用{{xxx}}引用定义的变量。
### ❓ 如何上传多个文件?
> 在前端选择 `form-data`,每个文件都可单独选择,支持多文件上传。后端会自动处理。
### ❓ 如何保存上传的文件到指定目录?
> 启动时通过 `--upload-dir=/your/path` 参数指定目录,form-data上传的文件会自动保存到该目录(存在则覆盖)。目录需提前创建并有写权限。
### ❓ 如何自定义请求头?
> 在前端"请求头"标签页添加即可,支持变量替换。
### ❓ 如何切换为直接请求(不走代理)?
> 只需关闭后端服务或修改 `/api/mode` 返回内容,前端会自动切换为直连模式。
### ❓ 数据安全吗?
> 所有数据仅存储在浏览器本地(localStorage),不会上传到任何服务器。企业内网、敏感环境使用无忧。
### ❓ 支持哪些平台?
> 支持Windows、Linux、MacOS、ARM架构,包括国产信创平台。一次编译,到处运行。同时完美支持IPv4和IPv6网络协议。
### ❓ 支持哪些通信协议?
> 除了传统的HTTP/HTTPS请求,还原生支持WebSocket(双向通信)和SSE(Server-Sent Events,单向实时推送),满足各种实时通信需求。
### ❓ MockServer和EchoServer有啥区别?
> EchoServer返回的内容相对固定,MockServer完全支持自定义,支持根据条件动态构建响应内容。
### ❓ EchoServer有什么用?
> EchoServer提供完整的请求回显和响应控制功能,适合接口调试、自动化测试、网络延迟模拟等场景。支持多种响应格式和流式通信。
### ❓ 如何关闭EchoServer?
> 启动时添加 `--echo-server=false` 参数即可关闭EchoServer调试服务。
### ❓ 如何开启静态文件服务器模式?
> 使用 `--webroot` 参数启动静态文件服务器模式。所有API接口将失效,变成一个纯静态文件服务器。
### ❓ 静态文件服务器支持哪些文件类型?
> 支持所有常见文件类型:HTML、CSS、JS、图片(PNG/JPG/GIF/SVG)、音频(MP3/WAV)、视频(MP4)、字体文件、PDF、压缩包等。会自动设置正确的MIME类型。
---
## 🤝 贡献与反馈
- 🐛 欢迎提交 issue 或 PR,完善功能和文档。
- 💡 如有建议或 bug,欢迎反馈!
---
## 📄 License
MIT
---
如需进一步定制或有疑问,欢迎联系作者。
---
**⭐ 如果这个项目对你有帮助,请给我们一个Star!**
**💬 有任何问题或建议,欢迎在GitHub上讨论!**
---
*让API调试变得简单而优雅* ✨