Skip to content

Guard 拦截器

Guard 是方法调用前的中间件,承担鉴权、限流、日志等横切逻辑。v1.3.4 起 Guard 采用错误通道签名——返回 error 表达拒绝,不再需要写响应或 panic。

接口定义

go
type Guard interface {
    Guard(ctx fun.Ctx) error
}
  • 返回 nil:放行,进入下一个 Guard 或业务方法;
  • 返回 error:短路——后续 Guard 与业务方法不再执行,错误走统一 Result 响应;返回 fun.Error(code, msg) 可携带业务错误码。
go
type AuthGuard struct {
    Redis *platform.Redis // Guard 也是 Box,依赖自动注入
}

func (g *AuthGuard) Guard(ctx fun.Ctx) error {
    token := ctx.State["token"]
    if token == "" {
        return fun.Error(4010, "未登录")
    }
    if !g.Redis.Exists("session:" + token) {
        return fun.Error(4011, "会话已过期")
    }
    return nil
}

三个挂载层级

go
f := fun.GetFun()

f.BindGuard(&LogGuard{})                      // ① 全局:所有服务生效

f.BindService(&AdminSvc{}, &AuthGuard{})      // ② 服务级:可变参数,按注册顺序执行

f.BindRoute("GET", "/admin/*", handler, &AuthGuard{}) // ③ 路由级:自定义路由的处理器前执行

执行顺序:全局 → 服务级(各自按注册顺序);路由 Guard 在处理器前按注册顺序执行。任何一层返回 error 即短路。

路由 Guard 的 State

BindRoute 的 Guard 收到的 Ctx.State 已合并 URL 查询与表单参数——token 放查询参数即可鉴权:GET /image/a.png?token=xxx

Guard 里能拿到什么

Guard 的入参就是 Ctx

字段用途
ctx.State客户端透传的 token / 自定义标识
ctx.Ip客户端 IP,做限流、风控
ctx.ServiceName / MethodName即将调用的端点——可做端点级策略表
ctx.RequestCtx原生 fasthttp 上下文(Cookie、Header 等)

从 Cookie 读会话并把校验结果传给服务层的模式:

go
func (g *AuthGuard) Guard(ctx fun.Ctx) error {
    sessionId := string(ctx.RequestCtx.Request.Header.Cookie("session"))
    user := loadSession(sessionId)
    if user == nil {
        return fun.Error(4010, "未登录")
    }
    // 传递给服务层做对象级授权
    ctx.RequestCtx.SetUserValue("user", user)
    return nil
}

// 服务层
func (s *OrderSvc) Get(dto GetOrderDto) (OrderDto, error) {
    user, _ := s.RequestCtx.UserValue("user").(*User)
    // ... 对象级授权判断
}

推荐姿势:显式端点策略表

中大型项目建议缺省拒绝 + 显式放行表,Guard 只写通用逻辑:

go
// allow 记录哪个服务开放给哪类 Guard 组合;缺省 = 拒绝
var publicEndpoints = map[string]bool{
    "UserSvc.Login":  true,
    "UserSvc.Register": true,
}

func (g *AuthGuard) Guard(ctx fun.Ctx) error {
    if publicEndpoints[ctx.ServiceName+"."+ctx.MethodName] {
        return nil // 公开端点放行
    }
    return requireSession(ctx) // 其余一律要求会话
}

下一步

基于 MIT 许可发布