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
封装起来
在 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)
}
修改 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 .
修改 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)
}
}
启动项目,测试接口
3. api-jwt 验证
首先自行实现 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")
}
在 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 校验
在 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
}
在 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>
评论区