拦截器
什么是拦截器?
拦截器(Guard)是 Fun 框架中用于在服务方法执行前进行预处理的组件。它们通常用于实现权限验证、身份认证、请求日志记录等通用逻辑。拦截器可以在方法执行前检查请求的合法性,并在必要时阻止方法的执行。
定义拦截器
基本拦截器结构
要定义一个拦截器,需要创建一个实现 fun.Guard 接口的结构体:
go
type AuthGuard struct {
// 可以注入依赖
Config *Config
Redis *RedisClient
}
func (g AuthGuard) Guard(ctx fun.Ctx) {
// 实现权限验证逻辑
// 如果验证失败,可以 panic 抛出错误
}拦截器方法
拦截器必须实现 Guard 方法,该方法接收一个 fun.Ctx 参数,包含请求的上下文信息:
go
func (g AuthGuard) Guard(ctx fun.Ctx) {
// 访问请求信息
token := ctx.State["token"]
ip := ctx.Ip
serviceName := ctx.ServiceName
methodName := ctx.MethodName
// 实现验证逻辑
if !isValidToken(token) {
panic(fun.Error(401, "Unauthorized"))
}
}依赖注入
拦截器依赖注入
与服务类似,拦截器也支持依赖注入。拦截器的第一层字段会自动注入:
go
type AuthGuard struct {
// 第一层字段会自动注入
Config *Config
Redis *RedisClient
Logger *Logger
}对于嵌套结构中的字段,需要使用 fun:"auto" 标签:
go
type Config struct {
// 嵌套结构中的字段需要使用 fun:"auto" 标签
Logger *Logger `fun:"auto"`
}注册拦截器
全局拦截器
可以通过 fun.BindGuard 方法注册全局拦截器,它将应用于所有服务:
go
func init() {
fun.BindGuard(AuthGuard{})
}服务级拦截器
可以通过 fun.BindService 方法为特定服务注册拦截器:
go
func init() {
fun.BindService(UserService{}, AuthGuard{})
}实际示例
身份验证拦截器
go
type AuthGuard struct {
Redis *RedisClient
}
func (g AuthGuard) Guard(ctx fun.Ctx) {
// 检查是否存在 token
token, exists := ctx.State["token"]
if !exists {
panic(fun.Error(401, "Missing authentication token"))
}
// 验证 token 有效性
if !g.isValidToken(token) {
panic(fun.Error(401, "Invalid token"))
}
// 检查 token 是否已过期
if g.isTokenExpired(token) {
panic(fun.Error(401, "Token expired"))
}
}
func (g AuthGuard) isValidToken(token string) bool {
// 实现 token 验证逻辑
// ...
return true
}
func (g AuthGuard) isTokenExpired(token string) bool {
// 实现 token 过期检查逻辑
// ...
return false
}权限检查拦截器
go
type PermissionGuard struct {
Database *Database
}
func (g PermissionGuard) Guard(ctx fun.Ctx) {
userID := ctx.State["userID"]
serviceName := ctx.ServiceName
methodName := ctx.MethodName
// 检查用户是否有权限访问该服务方法
if !g.hasPermission(userID, serviceName, methodName) {
panic(fun.Error(403, "Insufficient permissions"))
}
}
func (g PermissionGuard) hasPermission(userID, serviceName, methodName string) bool {
// 查询数据库检查用户权限
// ...
return true
}多个拦截器
可以为服务注册多个拦截器,它们将按注册顺序依次执行:
go
func init() {
fun.BindService(
UserService{},
AuthGuard{}, // 首先执行身份验证
PermissionGuard{}, // 然后执行权限检查
LoggingGuard{}, // 最后执行日志记录
)
}错误处理
在拦截器中,可以通过 panic 抛出错误来阻止服务方法的执行:
go
func (g AuthGuard) Guard(ctx fun.Ctx) {
if !g.isAuthenticated(ctx.State["token"]) {
// 抛出错误,阻止方法执行
panic(fun.Error(401, "Authentication failed"))
}
if !g.isAuthorized(ctx.ServiceName, ctx.MethodName) {
// 抛出错误,阻止方法执行
panic(fun.Error(403, "Access denied"))
}
}最佳实践
- 单一职责:每个拦截器应该只负责一个特定的功能,如身份验证、权限检查等
- 轻量级:拦截器应该尽可能轻量,避免执行耗时操作
- 错误处理:在拦截器中使用
panic(fun.Error())来抛出适当的错误 - 依赖注入:合理使用依赖注入来获取所需的资源和服务
- 顺序考虑:注意多个拦截器的执行顺序,确保逻辑正确性
- 日志记录:在拦截器中添加适当的日志记录,便于调试和监控
通过合理使用拦截器,可以将通用的验证和处理逻辑从服务方法中分离出来,使代码更加清晰和可维护。