go-zero中跨服务传递载体信息

go-zero中跨服务传递载体信息

➜  plangz git:(dev) ✗ cat pkg/ctxkey/keys.go                                    
package ctxkey

import "context"

type contextKey string

const (
	UserIDKey contextKey = "X-User-ID"
)

func GetUserID(ctx context.Context) int64 {
	v := ctx.Value(UserIDKey)
	if v == nil {
		return 0
	}
	return v.(int64)
}
➜  plangz git:(dev) ✗ 

网关 api 服务中的认证中间件 authmiddleware.go:

func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// 提取并验证JWT
		tokenString := extractToken(r)
		if tokenString == "" {
			zerror.WriteCodeError(w, 401, "未提供认证令牌")
			return
		}

		// 解析令牌
		claims, err := jwtutil.ParseToken(m.secret, tokenString)
		if err != nil {
			zerror.WriteCodeError(w, 401, "无效的访问令牌")
			return
		}

		// 设置上下文
		ctx := context.WithValue(r.Context(), ctxkey.UserIDKey, claims.UserID)

		// 关键步骤:将 UserID 注入 gRPC 元数据
		ctx = metadata.AppendToOutgoingContext(ctx,
			string(ctxkey.UserIDKey),         // 元数据键(转为字符串)
			fmt.Sprintf("%d", claims.UserID), // 元数据值(转为字符串)
		)

		next(w, r.WithContext(ctx))
	}
}

添加 metadata​ 拦截器:

➜  plangz git:(dev) ✗ cat apps/user/internal/interceptor/metadata_interceptor.go
package interceptor

import (
	"context"
	"fmt"
	"plangz/pkg/ctxkey"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

func MetadataInterceptor(ctx context.Context,
	req interface{},
	info *grpc.UnaryServerInfo,
	handler grpc.UnaryHandler,
) (resp interface{}, err error) {
	// 从 gRPC 元数据中提取 UserID
	if md, ok := metadata.FromIncomingContext(ctx); ok {
		values := md.Get(string(ctxkey.UserIDKey))
		if len(values) > 0 {
			userIDStr := values[0]
			var userID int64
			fmt.Sscanf(userIDStr, "%d", &userID)

			// 将 UserID 存入当前请求上下文
			ctx = context.WithValue(ctx, ctxkey.UserIDKey, userID)
		}
	}

	return handler(ctx, req)
}
➜  plangz git:(dev) ✗ 

注册拦截器:

func main() {
	fmt.Println("=== User Service Started ===")
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)
	ctx := svc.NewServiceContext(c)

	// 注册拦截器
	serverOptions := []grpc.ServerOption{
		grpc.UnaryInterceptor(interceptor.MetadataInterceptor),
	}

	s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
		user.RegisterUserServer(grpcServer, server.NewUserServer(ctx))

		if c.Mode == service.DevMode || c.Mode == service.TestMode {
			reflection.Register(grpcServer)
		}
	})

	// 添加拦截器配置
	s.AddOptions(serverOptions...)

	defer s.Stop()

	fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
	s.Start()
}

网关 api 服务中通过 ctxkey.GetUserID(l.ctx)​ 获取载体信息:

func (l *UserInfoLogic) UserInfo() (resp *types.UserInfoResp, err error) {
	// todo: add your logic here and delete this line

	userId := ctxkey.GetUserID(l.ctx)
	fmt.Println("userId = ", userId)

	resp = &types.UserInfoResp{
		Name:  "plangz",
		Phone: "13800138000",
		Email: "email@plangz.com",
	}

	result, err := l.svcCtx.UserRpc.UserInfo(l.ctx, &user.UserInfoReq{})
	fmt.Println("result", result)

	return
}

user 服务中通过 ctxkey.GetUserID(l.ctx)​ 获取载体信息:

func (l *UserInfoLogic) UserInfo(in *user.UserInfoReq) (*user.UserInfoResp, error) {
	// todo: add your logic here and delete this line

	fmt.Println("in user service, userId =", ctxkey.GetUserID(l.ctx))

	return &user.UserInfoResp{
		Name:  "plangz",
		Phone: "13800138000",
		Email: "email@plangz.com",
	}, nil
}

Posted in PHP

发表评论

您的电子邮箱地址不会被公开。