目 录CONTENT

文章目录

go-zero api 使用

Sakura
2023-12-04 / 0 评论 / 0 点赞 / 174 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于326天前,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

go-zero api

1. api 语法

api 文件是对这个服务所有接口的描述,go-zero 会根据 api 文件自动生成代码

// 登录请求参数
type LoginRequest {
	Username string `json:"username"`
	Password string `json:"password"`
}

// 响应参数
type LoginResponse {
	// 状态码
	Code int `json:"code"`
	// 响应数据
	Data interface{} `json:"data"`
	// 错误信息
	Message string `json:"message"`
}

type UserInfo {
	UserId   int    `json:"userId"`
	Username string `json:"username"`
}

type userInfoResponse {
	Code    int      `json:"code"`
	Data    UserInfo `json:"data"`
	Message string   `json:"message"`
}

// 定义服务
service users {
	// 登录接口
	@handler login
	// 请求方法 路径 参数 返回值
	post /api/users/login (LoginRequest) returns(LoginResponse)

	// 用户信息接口
	@handler userInfo
	get /api/users/info  returns(userInfoResponse)
}
// 通过 goctl生成代码
goctl api go -api user.api -dir .
// go run users.go
  • 测试 info 接口

  • 测试 login 接口

2. api 响应封装

可以看到上面的 api 文件中,每一个接口都有对应的响应结构体,比较冗余,应该把code,data,messgae 封装起来

  1. 在 common 目录下创建 reponse 对 code,message,data 进行封装

type Body struct {
	Code    int32       `json:"code"`
	Message any         `json:"message"`
	Data    interface{} `json:"data"`
}

func Response(r *http.Request, w http.ResponseWriter, res any, err error) {
	if err != nil {
		body := Body{
			Code:    500,
			Message: "错误",
			Data:    nil,
		}

		httpx.WriteJson(w, http.StatusOK, body)
	}
	body := Body{
		Code:    200,
		Message: res,
		Data:    nil,
	}
	httpx.WriteJson(w, http.StatusOK, body)
}
  1. 修改 api 文件

// 登录请求参数
type LoginRequest {
	Username string `json:"username"`
	Password string `json:"password"`
}

type UserInfoResponse {
	UserId   int    `json:"userId"`
	Username string `json:"username"`
}

// 定义服务
service users {
	// 登录接口
	@handler login
	// 请求方法 路径 参数 返回值
	post /api/users/login (LoginRequest) returns(string)

	// 用户信息接口
	@handler userInfo
	get /api/users/info  returns(UserInfoResponse)
}
// goctl api go -api user.api -dir .
  1. 修改 handler 中对应的响应逻辑

func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req types.LoginRequest
		if err := httpx.Parse(r, &req); err != nil {
			httpx.ErrorCtx(r.Context(), w, err)
			return
		}

		l := logic.NewLoginLogic(r.Context(), svcCtx)
		resp, err := l.Login(&req)
		//if err != nil {
		//	httpx.ErrorCtx(r.Context(), w, err)
		//} else {
		//	httpx.OkJsonCtx(r.Context(), w, resp)
		//}
		response.Response(r, w, resp, err)
	}
}

func userInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		l := logic.NewUserInfoLogic(r.Context(), svcCtx)
		resp, err := l.UserInfo()
		//if err != nil {
		//	httpx.ErrorCtx(r.Context(), w, err)
		//} else {
		//	httpx.OkJsonCtx(r.Context(), w, resp)
		//}
		response.Response(r, w, resp, err)
	}
}
  1. 启动项目,测试接口

3. api-jwt 验证

  1. 首先自行实现 jet token 的生成

package jwt1

import (
	"errors"
	"github.com/golang-jwt/jwt/v5"
	"time"
)

type JwtPayLoad struct {
	UserID   int    `json:"user_id"`
	UserName string `json:"user_name"`
	Role     int    `json:"role"` // 用户权限 1普通用户; 2管理员;
}

type CustomClaims struct {
	JwtPayLoad
	jwt.RegisteredClaims
}

// GetToken 生成token
func GetToken(user JwtPayLoad, accessSecret string, accessExpire int64) (string, error) {
	claims := CustomClaims{
		JwtPayLoad: user,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(accessExpire))),
			Issuer:    "api_study",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	return token.SignedString([]byte(accessSecret))
}

// 解析token
func ParseToken(tokenString string, accessSecret string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(accessSecret), nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("invalid token")
}
  1. 在 Api 文件中定义需要 jwt 验证的

@server (
	prefix: /api/users
)
// 定义服务
service users {
	// 登录接口
	@handler login
	// 请求方法 路径 参数 返回值
	post /login (LoginRequest) returns (string)
}

@server (
	// 前缀
	prefix: /api/users
	// 开启jwt验证
	jwt: Auth
)

// 定义服务
service users {
	// 用户信息接口
	@handler userInfo
	get /info  returns(UserInfoResponse)
}

表示 userInfo 接口需要 jwt 校验

  1. 在 login 接口中颁发 token

func (l *LoginLogic) Login(req *types.LoginRequest) (resp string, err error) {
	// todo: add your logic here and delete this line
	auth := l.svcCtx.Config.Auth
	// 生成token
	token, err := jwt1.GetToken(jwt1.JwtPayLoad{
		UserID:   1,
		UserName: "Sakura",
		Role:     2,
	}, auth.AccessSecret, auth.AccessExpire)
	if err != nil {
		log.Fatal("err:", err)
	}
	// 测试解析的token
	parseToken, err := jwt1.ParseToken(token, auth.AccessSecret)
	fmt.Println(parseToken)
	return token, err
}

  1. 在 info 接口中验证 token

func (l *UserInfoLogic) UserInfo() (resp *types.UserInfoResponse, err error) {
	// todo: add your logic here and delete this line
	// 拿到 token
	fmt.Println(l.ctx.Value("user_id"))
	fmt.Println(l.ctx.Value("user_name"))
	fmt.Println(l.ctx.Value("role"))

	user_id := l.ctx.Value("user_id").(json.Number)
	username := l.ctx.Value("user_name")
	uid, _ := user_id.Int64()

	return &types.UserInfoResponse{
		UserId:   int(uid),
		Username: username.(string),
	}, nil
}

4. 自定义 jwt 验证失败的响应

JWT 认证 | go-zero Documentation

func main() {
	.....
	// 定义一个回调函数
	server := rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(func(w 				  http.ResponseWriter, r *http.Request, err error) {
		// 自定义处理返回
		fmt.Println(err)
		httpx.WriteJson(w, http.StatusOK, response.Body{
			Code:    10086,
			Message: "jwt验证失败",
			Data:    nil,
		})
	}))
	.....
}

5. jwt传输

在HTTP请求添加名为Authorization的header,形式如下

Authorization: Bearer <token>

0

评论区