Feature Request: Support route rewrite in http gateway mappings
Currently, the http gateway in go-zero only supports Path and Method in Mappings, but there is no way to rewrite the route before forwarding to the upstream service.
In many real-world cases, the public API path exposed by the gateway is different from the actual path required by the downstream service. Supporting Rewrite would make the gateway much more flexible.
Example
Upstreams:
- Name: external-cable
Http:
Target: localhost:19103
Timeout: 15000
Mappings:
- Method: POST
Path: /api/abandoned/external/cable/workList
Rewrite: /cable/workList
With this configuration:
The gateway exposes:
POST /api/abandoned/external/cable/workList
But it rewrites and forwards the request to:
POST http://localhost:19103/cable/workList
序
这个功能近期会考虑开发。不过要先总体设计下。 计划是yaml的使用伪代码会在这个issue下面讨论下。 先捋一捋大致需要实现的功能点。 比如根据host/qury string/url 各种维度的重写,中间件的引用上。
概述
本设计旨在为 go-zero 设计新的 HTTP Gateway 组件,采用路由匹配 + 过滤器链(Filter Chain)设计理念,提供灵活、声明式的路由与请求/响应处理能力。所有功能通过 YAML 配置声明,支持细粒度控制请求生命周期。
核心概念
1. Upstream(上游服务)
定义后端服务目标。
Upstreams:
- Name: userservice
Http:
Target: localhost:8080
Timeout: 5000 # ms
2. Mapping(路由映射)
定义匹配规则 + 处理链。一个 Mapping = 一组 Match 规则 + 一组 Filters。
Mappings:
- Match:
Path: /api/v1/users/:id
Method: GET
Header:
X-Version: v1
Filters:
- StripPrefix=2
- AddResponseHeader=X-Handled-By, go-zero-gateway
3. Match(匹配器)
用于判断当前请求是否命中该路由。支持多条件 AND 逻辑组合。
4. Filters(过滤器)
在请求转发前/响应返回前执行操作。支持链式调用,顺序执行。
支持的 Match 类型(路由匹配条件)
1. Path(路径匹配)
支持通配符和路径参数提取。
Match:
Path: /api/v1/users/:id # 提取 :id 供 Filter 使用
2. Method(HTTP 方法匹配)
支持单个或多个方法。
Match:
Method: GET
# 或
Match:
Method: [GET, POST]
3. Query(查询参数匹配)
支持参数存在性检查或值匹配。
Match:
Query:
token: # 存在 token 参数即可
debug: "true" # debug 参数值必须为 "true"
4. Host(主机名匹配)
支持通配符匹配。
Match:
Host: "*.example.com"
# 或
Match:
Host: ["api.example.com", "admin.example.com"]
5. 时间范围匹配
用于维护窗口或灰度发布。
Match:
After: "2025-01-01T00:00:00+08:00"
Before: "2025-12-31T23:59:59+08:00"
6. 权重匹配(灰度/AB测试)
用于流量分配。
Match:
Weight:
Group: "experiment-A"
Value: 30 # 30% 流量
7. Client IP 匹配(RemoteAddr)
支持 CIDR 格式。
Match:
RemoteAddr: "192.168.1.0/24"
Filters 过滤器
请求阶段 Filters
| Filter 名称 | 参数 | 说明 |
|---|---|---|
AddRequestHeader |
key, value |
新增请求头 |
SetRequestHeader |
key, value |
设置或替换指定名称的请求头(若已存在则覆盖) |
RemoveRequestHeader |
key |
删除指定名称的请求头 |
AddRequestParameter |
key, value |
新增查询参数(URL 中的 query string) |
RemoveRequestParameter |
key |
删除指定名称的查询参数 |
PrefixPath |
/prefix |
在原始路径前添加指定前缀(如:/api → /prefix/api) |
RewritePath |
regex, replacement |
使用正则表达式重写路径(支持捕获组替换) |
StripPrefix |
count (整数) |
移除路径前 N 段前缀(如:/v1/user/profile,count=2 → /profile) |
响应阶段 Filters
| Filter 名称 | 参数 | 说明 |
|---|---|---|
AddResponseHeader |
key, value |
新增响应头 |
SetResponseHeader |
key, value |
设置或替换指定名称的响应头(若已存在则覆盖) |
RemoveResponseHeader |
key |
删除指定名称的响应头 |
SetStatus |
code(如 401, 500) |
强制设置 HTTP 响应状态码,可触发短路响应(直接返回,不再继续下游处理) |
注意:所有响应阶段过滤器均支持 URI 模板变量引用,例如:
${id}、${path}等,可在值中动态插入请求上下文中的变量值。
自定义 Match 和 Filter 的 Go 接口规范
为支持扩展性,提供标准接口,允许开发者实现自定义逻辑。
1. 自定义 Match 接口
// MatchPredicate 定义匹配器接口
type MatchPredicate interface {
// Match 判断请求是否匹配,返回 true/false
// ctx: 上下文,可用于传递提取的变量(如 Path 参数)
// req: 原始 HTTP 请求
Match(ctx context.Context, req *http.Request) (bool, context.Context, error)
}
// 示例:自定义 JWT 验证匹配器
type JWTMatchPredicate struct {
Secret string
}
func (j *JWTMatchPredicate) Match(ctx context.Context, req *http.Request) (bool, context.Context, error) {
token := req.Header.Get("Authorization")
claims, err := parseJWT(token, j.Secret)
if err != nil {
return false, ctx, nil // 不匹配,不报错
}
// 将解析后的 claims 注入上下文,供后续 Filter 使用
ctx = context.WithValue(ctx, "jwt_claims", claims)
return true, ctx, nil
}
全局默认 Filters 配置
支持为所有路由设置默认过滤器,减少重复配置。
配置方式
在根级别添加 DefaultFilters 字段:
# gateway.yaml
DefaultFilters:
- AddResponseHeader=X-Powered-By, go-zero-gateway
- AddResponseHeader=Server, MyCompany/1.0
- CustomFilter=request_id_injector # 注入全局请求 ID
Upstreams:
- Name: userservice
Http:
Target: localhost:8080
Mappings:
- Match:
Path: /api/v1/users
# 此路由自动继承上述 3 个 Filter
Filters:
- StripPrefix=2 # 追加特定 Filter
执行顺序
- 全局 Filter 先于路由局部 Filter 执行
- 可在局部 Filter 中覆盖全局行为(如再次
SetResponseHeader会覆盖全局设置) - 全局 Filter 适用于:安全头、追踪 ID、通用日志、CORS 等横切关注点
完整配置示例
DefaultFilters:
- AddResponseHeader=X-Gateway-Version, v2.0
- CustomFilter=trace_id_injector
Upstreams:
- Name: userservice
Http:
Target: localhost:8080
Timeout: 5000
Mappings:
# 示例1:JWT 认证 + 路径重写
- Match:
Path: /v2/users/:id
Method: GET
Filters:
- CustomFilter=jwt_auth
- RewritePath=/v2/users/(?<id>.+), /user/profile/$\{id}
# 示例2:灰度发布 + 全局 Filter 继承
- Match:
Path: /api/v1/experiment
Weight:
Group: "canary"
Value: 10
Filters:
- AddRequestHeader=X-Experiment, true
# 示例3:IP 白名单 + 自定义 Filter
- Match:
Path: /admin/**
RemoteAddr: "10.0.0.0/8"
Filters:
- CustomFilter=admin_audit_log
- StripPrefix=1
全局 CORS 配置 (GlobalCors)
提供声明式、细粒度的跨域资源共享(CORS)控制。
配置结构
在根级别添加 GlobalCors 字段,支持按路径模式配置不同 CORS 策略。
GlobalCors:
AddToSimpleUrlHandlerMapping: true
CorsConfigurations:
'/*': # 路径匹配模式,使用 Gin 风格的通配符
AllowedOrigins: ["https://myapp.com"]
AllowedMethods: [GET, POST, PUT, DELETE, OPTIONS]
AllowedHeaders: ["*"]
ExposedHeaders: ["X-Request-ID"]
AllowCredentials: true
MaxAge: 3600
配置项说明
| 配置项 | 类型 | 说明 | 备注 |
|---|---|---|---|
| AllowedOrigins | 字符串数组 | 允许跨域的源 | * 表示允许所有源(不推荐在生产环境使用) |
| AllowedMethods | 字符串数组 | 允许的 HTTP 方法 | 必须包含 OPTIONS 以支持预检请求 |
| AllowedHeaders | 字符串数组 | 允许客户端发送的请求头 | * 表示允许所有 |
| ExposedHeaders | 字符串数组 | 允许浏览器访问的响应头 | - |
| AllowCredentials | 布尔值 | 是否允许发送 Cookie 或认证头 | 若为 true,AllowedOrigins 不能为 * |
| MaxAge | 整数(秒) | 预检请求(Preflight)结果的缓存时间 | - |
高级选项
为处理未被任何路由匹配的 OPTIONS 预检请求(常见于前端开发),提供兜底开关:
GlobalCors: AddToSimpleUrlHandlerMapping: true # 默认 false CorsConfigurations: '/*': AllowedOrigins: ["https://myapp.com"] AllowedMethods: [GET, POST, OPTIONS]
实现机制
网关在路由匹配之前,优先检查 CORS 配置。
- 对于 OPTIONS 预检请求,直接根据配置返回 200 OK 及 CORS 头,不转发到后端服务。
- 对于普通请求,在响应阶段自动注入
Access-Control-*头。
上游服务 (Upstreams)
Upstreams:
- Name: userservice
Http:
Target: localhost:8080
Timeout: 5000
Mappings:
# 示例1:JWT 认证 + 路径重写
- Match:
Path: /v2/users/:id
Method: GET
Filters:
- CustomFilter=jwt_auth
- RewritePath=/v2/users/(?<id>.+), /user/profile/$\{id}
# 示例2:灰度发布 + 全局 Filter 继承
- Match:
Path: /api/v1/experiment
Weight:
Group: "canary"
Value: 10
Filters:
- AddRequestHeader=X-Experiment, true
# 示例3:IP 白名单 + 自定义 Filter
- Match:
Path: /admin/*
RemoteAddr: "10.0.0.0/8"
Filters:
- CustomFilter=admin_audit_log
- StripPrefix=1
路由映射 (Mappings) 详解
| 配置项 | 说明 |
|---|---|
| Match | 定义请求匹配条件 |
| Path | 请求路径。支持正则表达式和占位符 :id |
| Method | HTTP 方法(如 GET, POST) |
| Weight | 用于灰度发布或流量分片。Group 指定分组,Value 指定权重 |
| RemoteAddr | 根据客户端 IP 地址进行匹配,支持 CIDR 格式(如 10.0.0.0/8) |
| Filters | 在匹配的请求上应用的过滤器 |
| CustomFilter | 应用自定义过滤器 |
| RewritePath | 重写请求路径。格式为 旧路径, 新路径。(?<id>.+) 是捕获组,$\{id} 是替换引用 |
| AddRequestHeader | 向请求头中添加键值对 |
| StripPrefix | 移除请求路径的前缀。StripPrefix=1 表示移除第一个路径段 |
更多Example
filter
新增http header
AddRequestHeader命令,必须接收两个参数,key/value
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- AddRequestHeader =X-Request-red, blue
新增查询字符串
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- AddRequestParameter=red, blue
新增http 响应头
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- AddResponseHeader=X-Response-Red, Blue
给path加上前缀
这会在所有匹配请求的路径前加上 /mypath 前缀。因此,对 /hello 的请求会被发送至 /mypath/hello。
这点和现有的Prefix字段有点重合,目前的想法是慢慢不使用Pefix字段。
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- PrefixPath=/mypath
删除请求http header
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- RemoveRequestHeader=X-Request-Foo
删除响应http header
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- RemoveResponseHeader=X-Response-Foo
删除请求参数(query string)
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- RemoveRequestParameter=red
替换请求http header里面的值
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- SetRequestHeader=X-Request-Red, Blue
替换响应http header里面的值
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- SetResponseHeader=X-Response-Red, Blue
响应http 状态码
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- SetStatus=401
重写path
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /users
- Method: POST
Path: /users/create
Filters:
- RewritePath=/red(?<segment>/?.*), $\{segment}
去掉路径前缀
访问网关的/api/v1/users/create,到后端服务就变成/users/create
Upstreams:
- Name: userservice # 可选,如果未指定则使用 target
Http:
Target: localhost:8080
#Prefix: /api/v1 # 可选
Timeout: 3000 # 单位为毫秒,默认值 3000
Mappings:
- Method: GET
Path: /api/v1/users
- Method: POST
Path: /api/v1/users/create
Filters:
- StripPrefix=2