需求场景
最近的开发过程中遇到一个场景,用go-zero开发的api服务中,除了login api外其他的api需要进行登录的认证(很普通的场景)。但由于整个后端架构是微服务模式,提供用户及登录验证的是另一个grpc服务。所以出现了需要在api的中间件中调用 用户rpc服务的问题。查了官网没有得到明确的答案。故将研究过程及最后方案记录如此。供后人享用。
了解api中间件
如下代码为使用go-zero自动生成的初始中间件代码
package middleware
import "net/http"
type AuthMiddleware struct {
}
func NewAuthMiddleware() *AuthMiddleware {
return &AuthMiddleware{}
}
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}
根据官网提示,使用中间件需要在ServiceContext 中注册中间件,示例代码如下
type ServiceContext struct {
Config config.Config #配置文件
UserRpc user.User # 用户grpc服务
AuthMiddleware rest.Middleware # 本次示例中间件
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
AuthMiddleware: middleware.NewAuthMiddleware().Handle, # 初始注册中间件的方法
UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)),
}
}
改造中间件
官网中虽然也提到了《在中间件中调用其他服务》 的章节,但并没有提供详细的demo案例。新萌小白可能不太好理解。这里大概讲解一下(自我领悟),如果需要在中间件中调用其他服务,需要在注册中间件的时候,将被调用服务的句柄(不知道怎么描述这个词暂时用句柄代替吧) 以参数形式传递到中间件里。(举一反三啊 如果想在中间件中调用配置文件参数亦是如此)。所以需要在中间件的初始struct中 先设置好被引入的组件类型。在NewAuthMiddleware()方法中进行初始化。
示例代码如下:(请参照上方初始代码比较观看)
type AuthMiddleware struct {
Config config.Config ## 增加了配置文件
UserRpc user.User ## 增加了用户rpc服务的句柄
}
// 增加了入参c r 分别是配置文件句柄和用户rpc服务句柄
func NewAuthMiddleware(c config.Config, r user.User) *AuthMiddleware {
return &AuthMiddleware{
Config: c, //这里进行初始化
UserRpc: r,
}
}
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
Authorization := r.Header.Get("Authorization")
// 上方注册好之后 在这里就可以调用rpc服务了
tokenData, err := m.UserRpc.CheckLogin(r.Context(), &users.CheckLoginRequest{
Token: Authorization,
})
if err != nil {
httpx.ErrorCtx(r.Context(), w, "没有提供token")
return
}
next(w, r)
}
}
如上 ,改造完成后即可在中间件中调用rpc服务了