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 .