一、入门案例
-
1、在黑窗口上安装
go install github.com/zeromicro/go-zero/tools/goctl@latest
-
2、使用
goland
创建一个项目 -
3、在项目中安装依赖
go get -u github.com/zeromicro/go-zero@latest
-
4、模拟创建一个
user
的项目goctl api new user
-
5、安装依赖包
go mod tidy
-
6、补充代码段
func (l *UserLogic) User(req *types.Request) (resp *types.Response, err error) { // todo: add your logic here and delete this line return &types.Response{ Message: "你好,水痕", }, nil }
-
7、在浏览器上输入
http://localhost:8888/from/me
,关于为什么是me
可以查看types.go
文件package types type Request struct { Name string `path:"name,options=you|me"` }
-
8、关于
user
项目目录解说. ├── etc │ └── user-api.yaml # 配置文件 ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── routes.go # 路由文件 │ │ └── userhandler.go # 可以理解为控制层 │ ├── logic │ │ └── userlogic.go # 可以理解为服务层 │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go # 可以理解为DTO、VO ├── user.api └── user.go # 启动文件 7 directories, 9 files
二、自己编写api
文件来生成别的文件
-
1、创建一个空的
module
项目 -
2、在根目录下创建一个
user.api
的文件type LoginRequest { Username string `json:"username"` Password string `json:"password"` } type LoginResponse { Code int64 `json:"code"` Data string `json:"data"` Message string `json:"message"` } type UserInfo { Id int64 `json:"id"` Username string `json:"username"` } type UserInfoResponse { Code int64 `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/userInfo returns (UserInfoResponse) }
-
3、执行脚本
goctl api go -api user.api -dir .
-
4、等生成文件后,安装依赖包
-
5、书写一个获取用户信息的代码
func (l *UserInfoLogic) UserInfo() (resp *types.UserInfoResponse, err error) { // todo: add your logic here and delete this line return &types.UserInfoResponse{ Code: 0, Message: "请求成功", Data: types.UserInfo{ Id: 1, Username: "水痕", }, }, nil }
-
6、运行启动
三、封装自定义返回模板
-
1、创建一个
utils
的文件夹package utils import ( "github.com/zeromicro/go-zero/rest/httpx" "net/http" ) type Body struct { Code int `json:"code"` Message string `json:"message"` Result interface{} `json:"result,omitempty"` } func Response(w http.ResponseWriter, code int, message string, data interface{}) { httpx.OkJson(w, Body{ Code: code, Message: message, Result: data, }) } // Success 成功的请求 func Success(w http.ResponseWriter, data interface{}) { Response(w, 0, "请求成功", data) } // Fail 失败的请求 func Fail(w http.ResponseWriter, message string) { Response(w, 1, message, nil) }
-
2、定义
api
的时候就可以去除这些固定的写法type LoginRequest { Username string `json:"username"` Password string `json:"password"` } type UserInfoResponse { Id int64 `json:"id"` Username string `json:"username"` } // 定义要被方法的方法 service users { @handler login post /api/users/login (LoginRequest) returns (string ) @handler userInfo get /api/users/userInfo returns (UserInfoResponse) }
-
3、重新执行转换脚本
goctl api go -api user.api -dir .
-
4、改写代码,后运行
四、统一前缀
-
1、上面每次在
service
里面都要写/api/users/
这个路径,如果都是一样的,可以提取出去统一到前面// 定义要被方法的方法 @server( prefix: /api/users ) service users { @handler login post /login (LoginRequest) returns (string) @handler userInfo get /userInfo returns (UserInfoResponse) }
-
2、重新转换下
五、jwt
的使用
-
1、改写
user.api
文件type LoginRequest { Username string `json:"username"` Password string `json:"password"` } type UserInfoResponse { Id int64 `json:"id"` Username string `json:"username"` } // 定义要被方法的方法 @server( prefix: /api/users ) service users { @handler login post /login (LoginRequest) returns (string) } @server( prefix: /api/users jwt: Auth ) service users { @handler userInfo get /userInfo returns (UserInfoResponse) }
-
2、重新执行转换文件的脚本
goctl api go -api user.api -dir . goctl api go -api *.api -dir .
-
3、在
etc/users.yaml
文件中添加jwt
的配置Name: users Host: 0.0.0.0 Port: 8888 Auth: AccessSecret: test1test1 # 随机一个数就可以 AccessExpire: 3600 # 过期时间
-
4、在
internal/config/config.go
中配置,任何在yaml
中添加的配置都要在config.go
中添加配置package config import "github.com/zeromicro/go-zero/rest" type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } }
-
5、在
utils
文件夹下创建一个jwt.go
的文件package utils import ( "errors" "github.com/golang-jwt/jwt/v4" "time" ) // JwtPayLoad jwt中payload数据 type JwtPayLoad struct { UserID uint `json:"userId"` // 用户id Username string `json:"username"` // 用户名 } type CustomClaims struct { JwtPayLoad jwt.RegisteredClaims } // GenToken 创建 Token func GenToken(user JwtPayLoad, accessSecret string, expires int64) (string, error) { claim := CustomClaims{ JwtPayLoad: user, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(expires))), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) return token.SignedString([]byte(accessSecret)) } // ParseToken 解析 token func ParseToken(tokenStr string, accessSecret string, expires int64) (*CustomClaims, error) { token, err := jwt.ParseWithClaims(tokenStr, &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") }
-
6、在登录的时候返回
token
给前端func (l *LoginLogic) Login(req *types.LoginRequest) (resp string, err error) { // TODO 模拟查询数据库操作 if req.Username == "admin" && req.Password == "123456" { auth := l.svcCtx.Config.Auth token, err := utils.GenToken(utils.JwtPayLoad{ UserID: 1, Username: req.Username, }, auth.AccessSecret, auth.AccessExpire) if err != nil { fmt.Println("生成token失败") return "", errors.New("账号或密码错误") } return token, nil } else { return "", errors.New("账号或密码错误") } }
-
7、在需要从
token
中获取用户信息func (l *UserInfoLogic) UserInfo() (resp *types.UserInfoResponse, err error) { // todo: add your logic here and delete this line // 从请求头中获取token,解析出来 userId := l.ctx.Value("userId").(json.Number) fmt.Println(userId) fmt.Printf("数据类型:%v,%T\n", userId, userId) username := l.ctx.Value("username").(string) fmt.Println(username) uid, _ := userId.Int64() return &types.UserInfoResponse{ Id: uid, Username: username, }, nil }
-
8、登录接口生成
token
-
9、测试获取用户信息
{ "Authorization":"Bearer eyJhbGciOiJIUzI1Ni" }
六、获取客户端参数
-
1、获取
path
参数,url
上请求的地址为:``,这里xx
就是要获取的地址type MessageReq { Id int64 `path:"id"` } @server( prefix: /api/messages ) service users { @handler message get /message/:id(MessageReq) returns (string) }
-
2、获取
query
参数,url
上请求的地址为localhost:8888/api/message?name=xx&age=zz
type MessageInfoReq { Name string `form:"name"` Age int64 `form:"age"` } @server( prefix: /api/messages ) service users { @handler messageInfo get /messageInfo(MessageInfoReq) returns (string) }
-
3、获取
post
提交的json
数据type LoginRequest { Username string `json:"username"` Password string `json:"password"` } @server( prefix: /api/users ) service users { @handler login post /login (LoginRequest) returns (string) }
-
4、接收方法根据转码后会自动生成,且都是一样的
var req types.MessageReq if err := httpx.Parse(r, &req); err != nil { httpx.ErrorCtx(r.Context(), w, err) return } // 直接从req中获取数据就可以
七、一个项目下多个api
文件
-
1、在实际开发中,我更喜欢一张表就对应一个
api
文件,这样更好维护,唯一注意点就是每个文件里面的service users
这个users
是要一样的就可以,实际根据你项目来写的 -
2、在根目录下创建一个
api
的文件夹,里面包括message.api
和user.api
-
3、
message.api
文件内容syntax = "v2" type MessageInfoReq { Name string `form:"name,default=word"` Age int64 `form:"age,default=20"` } type MessageReq { Id int64 `path:"id"` } @server( prefix: /api/messages ) service users { @handler messageInfo get /messageInfo(MessageInfoReq) returns (string) @handler message get /message/:id(MessageReq) returns (string) }
-
4、
user.api
文件内容syntax = "v2" type LoginRequest { Username string `json:"username"` Password string `json:"password"` } type UserInfoResponse { Id int64 `json:"id"` Username string `json:"username"` } // 定义要被方法的方法 @server( prefix: /api/users ) service users { @handler login post /login (LoginRequest) returns (string) } @server( prefix: /api/users jwt: Auth ) service users { @handler userInfo get /userInfo returns (UserInfoResponse) }
-
5、根目录下创建一个
api.api
的文件syntax = "v2" import "api/user.api" import "api/message.api"
-
6、运行转码命令
goctl api go -api api.api -dir . # 或者直接使用下面的 goctl api go -api *.api -dir .