0


[kitex + gorm-gen + hertz] 快速写出一个基于go的微服务

[kitex + gorm-gen + hertz] 快速写出一个kitex的微服务

原项目:https://github.com/cloudwego/hertz

0、目的

创建一个用户的微服务用来进行添加和查询用户

1、环境安装

Kitex 安装

Kitex 目前对 Windows 的支持并不完善,建议使用虚拟机或 WSL2 进行测试。

这里我采用Ubuntu系统,来自动生成代码然后将生成代码同步到window本地的goland开发!

要开始 Kitex 开发,首先需要安装 Kitex 代码生成工具,

go install

命令可被用于安装 Go 二进制工具(在此之前,请务必检查已正确设置

GOPATH

环境变量,并将

$GOPATH/bin

添加到

PATH

环境变量中)
安装依赖

go install github.com/cloudwego/kitex@latest

go install github.com/cloudwego/thriftgo@latest

go mod edit -replace=github.com/apache/thrift=github.com/apache/[email protected]

docker 安装 相关环境

安装文件 docker-compose.yaml
启动命令

docker-compose up -d 

2、定义 用户的 IDL

namespace go demouser

enum ErrCode {
    SuccessCode                =0
    ServiceErrCode             =10001
    ParamErrCode               =10002
    UserAlreadyExistErrCode    =10003
    AuthorizationFailedErrCode =10004}

struct BaseResp {1: i64 status_code
    2: string status_message
    3: i64 service_time
}

struct User {1: i64 user_id
    2: string username
    3: string avatar
}

struct CreateUserRequest {
    // length of Message should be greater than or equal to 11: string username (vt.min_size ="1")2: string password (vt.min_size ="1")}

struct CreateUserResponse {1: BaseResp base_resp
}

struct MGetUserRequest {1: list<i64> user_ids (vt.min_size ="1")}

struct MGetUserResponse {1: list<User>users2: BaseResp base_resp
}

struct CheckUserRequest {1: string username (vt.min_size ="1")2: string password (vt.min_size ="1")}

struct CheckUserResponse {1: i64 user_id
    2: BaseResp base_resp
}service UserService {
    CreateUserResponse CreateUser(1: CreateUserRequest req)
    MGetUserResponse MGetUser(1: MGetUserRequest req)
    CheckUserResponse CheckUser(1: CheckUserRequest req)}

3、kitex 自动代码生成

官网
有了 IDL (可以理解为接口)以后我们便可以通过 kitex 工具生成项目代码了,执行如下命令:

$ kitex -module example -service example echo.thrift

上述命令中,-module 表示生成的该项目的 go module 名,-service 表明我们要生成一个服务端项目,后面紧跟的 example 为该服务的名字。最后一个参数则为该服务的 IDL 文件。

生成文件说明
build.sh : 构建脚本,将代码变成一个可执行的二进制文件
kitex_gen : IDL内容相关的生成代码,主要是基础的Server/Client代码,kitex的编解码的优化会在里面,这里是生成的主要代码
main.go : 程序入口
handler.go : 用户在该文件里实现IDL service 定义的方法 可以理解为api层

4、导入goland

上面的代码生成是在Linux中的,goland中使用deployment来同步到window中goland下,然后window输入

go mod tidy

来同步包

在这里插入图片描述

5、Demo

5.1、服务端编写handler – 假数据

package main

import("context""log"
    demouser "myuser/kitex_gen/demouser""time")// UserServiceImpl implements the last service interface defined in the IDL.type UserServiceImpl struct{}// CreateUser implements the UserServiceImpl interface.func(s *UserServiceImpl)CreateUser(ctx context.Context, req *demouser.CreateUserRequest)(resp *demouser.CreateUserResponse, err error){
    log.Println("姓名"+ req.Username +"密码"+ req.Password)
    resp =new(demouser.CreateUserResponse)
    resp.BaseResp =&demouser.BaseResp{StatusCode:200, StatusMessage:"ok", ServiceTime: time.Time{}.Unix()}return resp,nil}// MGetUser implements the UserServiceImpl interface.func(s *UserServiceImpl)MGetUser(ctx context.Context, req *demouser.MGetUserRequest)(resp *demouser.MGetUserResponse, err error){
    resp =new(demouser.MGetUserResponse)
    users :=make([]*demouser.User,0)
    users =append(users,&demouser.User{1,"test","test"})
    resp.Users = users
    resp.BaseResp =&demouser.BaseResp{StatusCode:200, StatusMessage:"ok", ServiceTime: time.Time{}.Unix()}return resp,nil}// CheckUser implements the UserServiceImpl interface.func(s *UserServiceImpl)CheckUser(ctx context.Context, req *demouser.CheckUserRequest)(resp *demouser.CheckUserResponse, err error){
    resp =new(demouser.CheckUserResponse)
    resp.UserId =1
    resp.BaseResp =&demouser.BaseResp{StatusCode:200, StatusMessage:"ok", ServiceTime: time.Time{}.Unix()}return resp,nil}

5.2、运行

package main

import("log""myuser/kitex_gen/demouser/userservice")funcmain(){//服务端的地址 [::]:8888,注意要和demouser下的service中名字保持一致,这里userservice
    svr := userservice.NewServer(new(UserServiceImpl))

    err := svr.Run()if err !=nil{
        log.Println(err.Error())}}

在这里插入图片描述

没有报错说明一起正常,下面进行客户端的编写
运行

sh build.sh

以进行编译,编译结果会被生成至

output

目录.

最后,运行

sh output/bootstrap.sh

以启动服务。服务会在默认的 8888 端口上开始运行。要想修改运行端口,可打开

main.go

,为

NewServer

函数指定配置参数:

addr,_:= net.ResolveTCPAddr("tcp","127.0.0.1:9999")
svr := api.NewServer(new(EchoImpl), server.WithServiceAddr(addr))

5.3、客户端 – 测试

package main

import("context""github.com/cloudwego/kitex/client""log""myuser/kitex_gen/demouser""myuser/kitex_gen/demouser/userservice""time")funcmain(){//第一个是服务名字,第二个是指定服务端的地址
    client, err := userservice.NewClient("userservice", client.WithHostPorts("0.0.0.0:8888"))if err !=nil{
        log.Fatal(err)}for{//通过client进行调用
        resp, err := client.CreateUser(context.Background(),&demouser.CreateUserRequest{Username:"wpc", Password:"123456"})//resp, err := client.CheckUser(context.Background(), &demouser.CheckUserRequest{Username: "wpc", Password: "123456"})//resp, err := client.MGetUser(context.Background(), &demouser.MGetUserRequest{UserIds: []int64{1, 2}})if err !=nil{
            log.Fatal(err)return}
        log.Println(resp)
        time.Sleep(time.Second)}}

在这里插入图片描述

在这里插入图片描述
到这里我们就完成了环境的搭建

5.4、使用etcd来完成注册和发现

服务端

package main

import("github.com/cloudwego/kitex/pkg/rpcinfo""github.com/cloudwego/kitex/server"
    etcd "github.com/kitex-contrib/registry-etcd""log""myuser/kitex_gen/demouser/userservice")funcmain(){//服务端的地址 [::]:8888,注意要和demouser下的service中名字保持一致,这里userservice// 填写对应的ip地址和端口
    r, err := etcd.NewEtcdRegistry([]string{"192.168.1.18:2379"})// r不应重复使用。if err !=nil{
        log.Fatal(err)}
    svr := userservice.NewServer(new(UserServiceImpl),
        server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName:"userservice"}),
        server.WithRegistry(r))

    err = svr.Run()if err !=nil{
        log.Println(err.Error())}}

客户端

NewClient() 后面的第一个参数要和前面的ServiceName保持一致

userservice
package main

import("context""github.com/cloudwego/kitex/client"
    etcd "github.com/kitex-contrib/registry-etcd""log""myuser/kitex_gen/demouser""myuser/kitex_gen/demouser/userservice""time")funcmain(){//第一个是服务名字,第二个是指定服务端的地址
    r, err := etcd.NewEtcdResolver([]string{"192.168.1.18:2379"})// r不应重复使用。if err !=nil{
        log.Fatal(err)}
    client, err := userservice.NewClient("userservice", client.WithResolver(r))....}

在这里插入图片描述

5.5、项目地址

https://gitee.com/wangpengchengalex/go-easynote/tree/master/demo

6、user微服务

使用 gorm-gen 来快速crud 官网

6.1、创建用户表

当然也可以是SQL创建

package main

import("gorm.io/driver/mysql""gorm.io/gorm")type User struct{
    gorm.Model
    Username string`json:"username"`
    Avatar   string`json:"avatar"`//password懒得改了}funcmain(){
    dsn :="gorm:gorm@tcp(192.168.1.18:3306)/gorm?charset=utf8&parseTime=True&loc=Local"println(dsn)
    db, err := gorm.Open(mysql.Open(dsn),&gorm.Config{})if err !=nil{panic("failed to connect database")}// 迁移 schema
    db.AutoMigrate(&User{})}

这将在数据库中生成
在这里插入图片描述

6.2、gorm-gen生成crud

package main

import("fmt""gorm.io/driver/mysql""gorm.io/gen""gorm.io/gorm")funcmain(){// 连接数据库
    db, err := gorm.Open(mysql.Open("gorm:gorm@tcp(192.168.1.18:3306)/gorm?charset=utf8&parseTime=True&loc=Local"))if err !=nil{panic(fmt.Errorf("cannot establish db connection: %w", err))}
    g := gen.NewGenerator(gen.Config{
        OutPath:"D:\\goCode\\myuser\\gorm-gen\\query",
        Mode:    gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface,// generate mode})

    g.UseDB(db)// Generate basic type-safe DAO API
    g.ApplyBasic(g.GenerateAllTable()...)
    g.Execute()}

6.3、测试crud

package main

import("context""fmt""gorm.io/driver/mysql""gorm.io/gorm""log""myuser/gorm-gen/model""myuser/gorm-gen/query")funcmain(){// 连接数据库
    db, err := gorm.Open(mysql.Open("gorm:gorm@tcp(192.168.1.18:3306)/gorm?charset=utf8&parseTime=True&loc=Local"))if err !=nil{panic(fmt.Errorf("cannot establish db connection: %w", err))}
    query.SetDefault(db)// 增加数据
    u := query.User
    ctx := context.Background()
    users :=[]*model.User{{Username:"test1", Avatar:"test1"},{Username:"test2", Avatar:"test2"}}
    u.WithContext(ctx).Create(users...)// 查询数据 https://gorm.io/gen/query.html
    seach1,_:= u.WithContext(ctx).Where(u.ID.Eq(1)).First()
    log.Println("通过id查询", seach1)// 更新数据 https://blog.csdn.net/Jeffid/article/details/126898000
    u.WithContext(ctx).Where(u.Username.Eq("test2")).Update(u.Username,"wpc")
    seach2,_:= u.WithContext(ctx).Where(u.Username.Eq("wpc")).First()
    log.Println("更新后的查询", seach2)}

6.4、添加到demo中 - dao层

规定 : 将crud的数据访问对象都放入dal文件中,下面主要三个目录 1、db 接下来定义的数据库操作 2、model
3、query (2,3都由gorm-gen生成)
在这里插入图片描述
1、init.go

package db

import("fmt""gorm.io/driver/mysql""gorm.io/gorm""myuser/demo/dal/query")var Q *query.Query

funcInit(){var err error
    db, err := gorm.Open(mysql.Open("gorm:gorm@tcp(192.168.1.18:3306)/gorm?charset=utf8&parseTime=True&loc=Local"))if err !=nil{panic(fmt.Errorf("cannot establish db connection: %w", err))}
    query.SetDefault(db)
    Q = query.Q
    if err !=nil{panic(err)}}

2、user.go

package db

import("context""myuser/demo/dal/model")// MGetUsers multiple get list of user infofuncMGetUsers(ctx context.Context, userIDs []int64)([]*model.User,error){return Q.WithContext(ctx).User.Where(Q.User.ID.In(userIDs...)).Find()}// CreateUser create user infofuncCreateUser(ctx context.Context, users []*model.User)error{return Q.WithContext(ctx).User.Create(users...)}// QueryUser query list of user infofuncQueryUser(ctx context.Context, userName string)([]*model.User,error){return Q.WithContext(ctx).User.Where(Q.User.Username.Eq(userName)).Find()}

3、user_test.go

package db

import("context""log""myuser/demo/dal/model""testing")funcTestCreateUser(t *testing.T){Init()
    users :=make([]*model.User,0)
    users =append(users,&model.User{Username:"wpctest", Avatar:"123187"})CreateUser(context.Background(), users)}funcTestQueryUser(t *testing.T){Init()
    user, err :=QueryUser(context.Background(),"wpctest")if err !=nil{
        log.Fatal(err)}
    log.Println(user[0])}funcTestMGetUsers(t *testing.T){Init()
    users, err :=MGetUsers(context.Background(),[]int64{7,9,10})if err !=nil{
        log.Fatal(err)}for_, user :=range users {
        log.Println(user)}}

相关代码地址

6.5、service层中调用dao层的方法

建一个目录service

,里面是具体的实现方法,

handler.go充当api层

来获取参数和返回数据对象,具体的数据处理由service中的一个一个方法来处理

6.5.1、新建一个异常处理类

package errno

import("errors""fmt""myuser/demo/kitex_gen/demouser")type ErrNo struct{
    ErrCode int64
    ErrMsg  string}func(e ErrNo)Error()string{return fmt.Sprintf("err_code=%d, err_msg=%s", e.ErrCode, e.ErrMsg)}funcNewErrNo(code int64, msg string) ErrNo {return ErrNo{
        ErrCode: code,
        ErrMsg:  msg,}}func(e ErrNo)WithMessage(msg string) ErrNo {
    e.ErrMsg = msg
    return e
}var(
    Success                =NewErrNo(int64(demouser.ErrCode_SuccessCode),"Success")
    ServiceErr             =NewErrNo(int64(demouser.ErrCode_ServiceErrCode),"Service is unable to start successfully")
    ParamErr               =NewErrNo(int64(demouser.ErrCode_ParamErrCode),"Wrong Parameter has been given")
    UserAlreadyExistErr    =NewErrNo(int64(demouser.ErrCode_UserAlreadyExistErrCode),"User already exists")
    AuthorizationFailedErr =NewErrNo(int64(demouser.ErrCode_AuthorizationFailedErrCode),"Authorization failed"))// ConvertErr convert error to ErrnofuncConvertErr(err error) ErrNo {
    Err := ErrNo{}if errors.As(err,&Err){return Err
    }
    s := ServiceErr
    s.ErrMsg = err.Error()return s
}

6.5.2、创建一个createservice类

package service

import("context""crypto/md5""fmt""io""myuser/demo/dal/db""myuser/demo/dal/model"
    errno "myuser/demo/error""myuser/demo/kitex_gen/demouser")type CreateUserService struct{
    ctx context.Context
}// NewCreateUserService new CreateUserServicefuncNewCreateUserService(ctx context.Context)*CreateUserService {return&CreateUserService{ctx: ctx}}func(s *CreateUserService)CreateUser(req *demouser.CreateUserRequest)error{
    users, err := db.QueryUser(s.ctx, req.Username)if err !=nil{return err
    }iflen(users)!=0{return errno.UserAlreadyExistErr
    }//生成md5
    h := md5.New()if_, err = io.WriteString(h, req.Password); err !=nil{return err
    }
    password := fmt.Sprintf("%x", h.Sum(nil))return db.CreateUser(s.ctx,[]*model.User{{Username: req.Username, Avatar: password}})}

6.5.3、包装类

package pack

import("github.com/cloudwego/biz-demo/easy_note/cmd/user/dal/db""github.com/cloudwego/biz-demo/easy_note/kitex_gen/demouser")// User pack user infofuncUser(u *db.User)*demouser.User {if u ==nil{returnnil}return&demouser.User{UserId:int64(u.ID), Username: u.Username, Avatar:""}}// Users pack list of user infofuncUsers(us []*db.User)[]*demouser.User {
    users :=make([]*demouser.User,0)for_, u :=range us {if temp :=User(u); temp !=nil{
            users =append(users, temp)}}return users
}

6.6、修改handler调用service方法

之前用的是模拟数据这里就是真实的查询数据库

package main

import("context"
    errno "myuser/demo/error""myuser/demo/pack""myuser/demo/service""myuser/kitex_gen/demouser")// UserServiceImpl implements the last service interface defined in the IDL.type UserServiceImpl struct{}// CreateUser implements the UserServiceImpl interface.func(s *UserServiceImpl)CreateUser(ctx context.Context, req *demouser.CreateUserRequest)(resp *demouser.CreateUserResponse, err error){
    resp =new(demouser.CreateUserResponse)
    err = service.NewCreateUserService(ctx).CreateUser(req)if err !=nil{
        resp.BaseResp = pack.BuildBaseResp(err)return resp,nil}
    resp.BaseResp = pack.BuildBaseResp(errno.Success)return resp,nil}// MGetUser implements the UserServiceImpl interface.func(s *UserServiceImpl)MGetUser(ctx context.Context, req *demouser.MGetUserRequest)(resp *demouser.MGetUserResponse, err error){
    resp =new(demouser.MGetUserResponse)
    users, err := service.NewMGetUserService(ctx).MGetUser(req)if err !=nil{
        resp.BaseResp = pack.BuildBaseResp(err)return resp,nil}

    resp.BaseResp = pack.BuildBaseResp(errno.Success)
    resp.Users = users
    return resp,nil}// CheckUser implements the UserServiceImpl interface.func(s *UserServiceImpl)CheckUser(ctx context.Context, req *demouser.CheckUserRequest)(resp *demouser.CheckUserResponse, err error){
    resp =new(demouser.CheckUserResponse)

    uid, err := service.NewCheckUserService(ctx).CheckUser(req)if err !=nil{
        resp.BaseResp = pack.BuildBaseResp(err)return resp,nil}

    resp.UserId = uid
    resp.BaseResp = pack.BuildBaseResp(errno.Success)return resp,nil}

完整代码

https://gitee.com/wangpengchengalex/go-easynote/tree/master/demo

使用Hertz来搭建一个 Golang 微服务 HTTP 框架

很明显上面的是基于微服务的,也不方便调试,需要起一个http框架来使用

官网 https://www.cloudwego.io/docs/hertz/

1.1、定义IDL文件

在目录下创建 idl文件文件夹,写入api.thrift
idl/api.thrift

namespace go demoapi

struct BaseResp {1: i64 status_code
    2:string status_message
    3: i64 service_time
}struct User {1: i64 user_id
    2:string username
    3:string avatar
}struct CreateUserRequest {1:string username (api.form="username", api.vd="len($) > 0")2:string password (api.form="password", api.vd="len($) > 0")}struct CreateUserResponse {1: BaseResp base_resp
}struct CheckUserRequest {1:string username (api.form="username", api.vd="len($) > 0")2:string password (api.form="password", api.vd="len($) > 0")}struct CheckUserResponse {1: BaseResp base_resp
}

service ApiService {
    CreateUserResponse CreateUser(1: CreateUserRequest req)(api.post="/user/register")
    CheckUserResponse CheckUser(1: CheckUserRequest req)(api.post="/user/login")}

1.2、创建新项目

// GOPATH 下执行
hz new-idl idl/api.thrift

// 拉取代码到window中 整理 & 拉取依赖 go mod tidy

1.3、修改handler进行rpc调用

这里路由都已经自动生成了
在这里插入图片描述
调用 方法和 client完全一致,先注册发现然后调用对应方法

1.3.1、新建一个rpc目录并创建一个远程调用类

package rpc

import("context""github.com/cloudwego/kitex/client""github.com/kitex-contrib/obs-opentelemetry/tracing"
    etcd "github.com/kitex-contrib/registry-etcd"
    errno "myuser/demo/error""myuser/kitex_gen/demouser""myuser/kitex_gen/demouser/userservice")var userClient userservice.Client

funcinitUser(){
    r, err := etcd.NewEtcdResolver([]string{"192.168.1.18:2379"})if err !=nil{panic(err)}

    c, err := userservice.NewClient("userservice",
        client.WithResolver(r),
        client.WithSuite(tracing.NewClientSuite()),)if err !=nil{panic(err)}
    userClient = c
}// CreateUser create user infofuncCreateUser(ctx context.Context, req *demouser.CreateUserRequest)error{
    resp, err := userClient.CreateUser(ctx, req)if err !=nil{return err
    }if resp.BaseResp.StatusCode !=0{return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage)}returnnil}// CheckUser check user infofuncCheckUser(ctx context.Context, req *demouser.CheckUserRequest)(int64,error){
    resp, err := userClient.CheckUser(ctx, req)if err !=nil{return0, err
    }if resp.BaseResp.StatusCode !=0{return0, errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage)}return resp.UserId,nil}

1.3.2、在handler中调用rpc方法

修改biz/handler/demoapi的文件

// Code generated by hertz generator.package demoapi

import("context""myuser/demo/api/biz/model/demoapi""myuser/demo/api/biz/rpc"
    errno "myuser/demo/error""myuser/kitex_gen/demouser""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/protocol/consts")// CreateUser .// @router /user/register [POST]funcCreateUser(ctx context.Context, c *app.RequestContext){var err errorvar req demoapi.CreateUserRequest
    err = c.BindAndValidate(&req)if err !=nil{
        c.String(consts.StatusBadRequest, err.Error())return}//替换掉代码生成的//resp := new(demoapi.CreateUserResponse)
    err = rpc.CreateUser(context.Background(),&demouser.CreateUserRequest{
        Username: req.Username,
        Password: req.Password,})if err !=nil{SendResponse(c, errno.ConvertErr(err),nil)return}//c.JSON(consts.StatusOK, resp)//自定义jsonSendResponse(c, errno.Success,nil)}// CheckUser .// @router /user/login [POST]funcCheckUser(ctx context.Context, c *app.RequestContext){var err errorvar req demoapi.CheckUserRequest
    err = c.BindAndValidate(&req)if err !=nil{
        c.String(consts.StatusBadRequest, err.Error())return}//resp := new(demoapi.CheckUserResponse)////c.JSON(consts.StatusOK, resp)
    id, err := rpc.CheckUser(context.Background(),&demouser.CheckUserRequest{
        Username: req.Username,
        Password: req.Password,})if err !=nil&& id ==0{SendResponse(c, errno.ConvertErr(err),nil)return}//c.JSON(consts.StatusOK, resp)//自定义jsonSendResponse(c, errno.Success,nil)}

1.3.3、自定义响应类

package demoapi

import("github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/protocol/consts"
    errno "myuser/demo/error")type Response struct{
    Code    int64`json:"code"`
    Message string`json:"message"`
    Data    interface{}`json:"data"`}// SendResponse pack responsefuncSendResponse(c *app.RequestContext, err error, data interface{}){
    Err := errno.ConvertErr(err)
    c.JSON(consts.StatusOK, Response{
        Code:    Err.ErrCode,
        Message: Err.ErrMsg,
        Data:    data,})}

在这里插入图片描述

1.4、项目地址

https://gitee.com/wangpengchengalex/go-easynote/tree/master/demo/api

1.5、jwt认证

1.5.1、初始化

在 biz下新建一个mw目录然后复制下面的代码,主要改

Authenticator

中调用rpc即可

1、自定义key和identitykey
在这里插入图片描述
2、这三处保持一致
在这里插入图片描述
3、调用rpc方法

package mw

import("context""net/http""time""github.com/cloudwego/biz-demo/easy_note/cmd/api/biz/model/demoapi""github.com/cloudwego/biz-demo/easy_note/cmd/api/biz/rpc""github.com/cloudwego/biz-demo/easy_note/kitex_gen/demouser""github.com/cloudwego/biz-demo/easy_note/pkg/consts""github.com/cloudwego/biz-demo/easy_note/pkg/errno""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/common/utils""github.com/hertz-contrib/jwt")var JwtMiddleware *jwt.HertzJWTMiddleware

funcInitJWT(){
    JwtMiddleware,_= jwt.New(&jwt.HertzJWTMiddleware{
        Key:[]byte(consts.SecretKey),
        TokenLookup:"header: Authorization, query: token, cookie: jwt",
        TokenHeadName:"Bearer",
        TimeFunc:      time.Now,
        Timeout:       time.Hour,
        MaxRefresh:    time.Hour,
        IdentityKey:   consts.IdentityKey,
        IdentityHandler:func(ctx context.Context, c *app.RequestContext)interface{}{
            claims := jwt.ExtractClaims(ctx, c)return&demoapi.User{
                UserID:int64(claims[consts.IdentityKey].(float64)),}},
        PayloadFunc:func(data interface{}) jwt.MapClaims {if v, ok := data.(int64); ok {return jwt.MapClaims{
                    consts.IdentityKey: v,}}return jwt.MapClaims{}},
        Authenticator:func(ctx context.Context, c *app.RequestContext)(interface{},error){var err errorvar req demoapi.CheckUserRequest
            if err = c.BindAndValidate(&req); err !=nil{return"", jwt.ErrMissingLoginValues
            }iflen(req.Username)==0||len(req.Password)==0{return"", jwt.ErrMissingLoginValues
            }//修改这里return rpc.CheckUser(context.Background(),&demouser.CheckUserRequest{
                Username: req.Username,
                Password: req.Password,})},
        LoginResponse:func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time){
            c.JSON(http.StatusOK, utils.H{"code":   errno.Success.ErrCode,"token":  token,"expire": expire.Format(time.RFC3339),})},
        Unauthorized:func(ctx context.Context, c *app.RequestContext, code int, message string){
            c.JSON(http.StatusOK, utils.H{"code":    errno.AuthorizationFailedErr.ErrCode,"message": message,})},
        HTTPStatusMessageFunc:func(e error, ctx context.Context, c *app.RequestContext)string{switch t := e.(type){case errno.ErrNo:return t.ErrMsg
            default:return t.Error()}},})}

1.5.2、修改handler

// CheckUser .// @router /user/login [POST]funcCheckUser(ctx context.Context, c *app.RequestContext){
    mw.JwtMiddleware.LoginHandler(ctx, c)}

1.5.3、修改main

funcInit(){
    rpc.Init()
    mw.InitJWT()}funcmain(){Init()
    h := server.New(
        server.WithHostPorts(":8080"),
        server.WithHandleMethodNotAllowed(true),// coordinate with NoMethod)register(h)
    h.Spin()}

在这里插入图片描述

jwt原理

https://blog.csdn.net/wzb_wzt/article/details/115730178

https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/middleware/jwt/#payloadfunc

项目中在api层调用带有jwt的user

...
v,_:= c.Get(consts.IdentityKey)//通过Get 来获取 IdentityHandler 中保存的上下文信息 UserID
notes, total, err := rpc.QueryNotes(context.Background(),&demonote.QueryNoteRequest{
    UserId:    v.(*demoapi.User).UserID,//
    SearchKey: req.SearchKey,
    Offset:    req.Offset,
    Limit:     req.Limit,})...

调用是需要加上Authorization的头信息

curl --location --request GET '127.0.0.1:8080/v2/note/query?offset=0&limit=20&search_key=test' \
--header 'Authorization: Bearer $token'
标签: 微服务 github 架构

本文转载自: https://blog.csdn.net/qq_41080854/article/details/128804495
版权归原作者 Alex抱着爆米花 所有, 如有侵权,请联系我们删除。

“[kitex + gorm-gen + hertz] 快速写出一个基于go的微服务”的评论:

还没有评论