go微服务开发:go-zero入门教程(一)

以下内容,参考了go-zero官方文档,是对官方文档的进阶指南章节的梳理汇总。

go-zero的进阶指南,请参考 https://go-zero.dev/cn/docs/advance/business-dev

 

通过本文,你将学习到如下知识点:

1.如何使用go-zero定义api文件

2.如何为定义的api文件生成api服务

3.如何编写模块业务逻辑

4.go-zero开发注意实现,参见这里 https://www.cnblogs.com/jamstack/p/17223639.html

 

在开始之前,假设你已经对go-zero有了基本的了解,并且了解go-zero编写api文件的语法。如果还不了解,建议先阅读这里 https://go-zero.dev/cn/docs/design/grammar/ 

 

开发环境:

Windows 11

Terminal preview

go 1.19

 

go-zero的进阶指南的演示工程,共包含2个模块:user和search,本篇讲述的是user模块,search模块请参见:go微服务开发:go-zero入门教程(二)

 

第一步:下载演示工程、设计数据库表的ddl、生成数据库表和model文件(如果是已有的数据库表,也可以逆向生成model)

  1.下载并解压go-zero进阶指南提供的演示工程,下载地址为 https://go-zero.dev/cn/assets/files/book-3d0b9e679f9e502cb07685b701c450cf.zip

  2.假设我们使用的是一个新的测试库,不存在数据库表,user模块的model文件夹是我们定义数据库表ddl的地方,这里存在着一个名为user.sql的ddl文件,我们为这个ddl生成数据库表,执行如下命令:

1
goctl model mysql ddl -src user.sql -dir . -c

  3.向生成的数据库表user,写入测试数据

1
INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男');

  

  题外话:如果user表已经存在,我们要为user表生成model,可以执行如下命令:

1
goctl model mysql datasource -url="$datasource" -table="user" -c -dir .

第二步:定义api文件、生成api服务

  1.定义user.api

  cd service/user/api/,转到user模块的api文件夹下,定义一个名为user.api的api文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type (
    LoginReq {
        Username string `json:"username"`
        Password string `json:"password"`
    }
 
    LoginReply {
        Id           int64 `json:"id"`
        Name         string `json:"name"`
        Gender       string `json:"gender"`
        AccessToken  string `json:"accessToken"`
        AccessExpire int64 `json:"accessExpire"`
        RefreshAfter int64 `json:"refreshAfter"`
    }
)
 
service user-api {
    @handler login
    post /user/login (LoginReq) returns (LoginReply)
}

  2.为user.api生成api服务

1
goctl api go -api user.api -dir .

第三步:编写user模块的业务代码

  1.定义数据库的配置

  cd service/user/api/internal/config/,编辑config.go,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package config
 
import (
    "github.com/zeromicro/go-zero/rest"
    "github.com/zeromicro/go-zero/core/stores/cache"
)
 
 
type Config struct {
    rest.RestConf
    Mysql struct{
        DataSource string
    }
 
    CacheRedis cache.CacheConf
    Auth    struct{
        AccessSecret    string
        AccessExpire    int64
    }
}

  2.填写数据库的yaml配置参数

  cd service/user/api/etc/,编辑user-api.yaml,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
Name: user-api
Host: 0.0.0.0
Port: 8888
Mysql:
  DataSource: root:dev@123456@tcp(127.0.0.1)/book?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
CacheRedis:
  - Host: 127.0.0.1:6379
    Pass:
    Type: node
Auth:
  AccessSecret: abcdefgh
  AccessExpire: 3600

  3.为servicecontext完善服务依赖

  cd service/user/api/internal/svc/,编辑servicecontext.go,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
type ServiceContext struct {
    Config    config.Config
    UserModel model.UserModel
}
 
func NewServiceContext(c config.Config) *ServiceContext {
    conn:=sqlx.NewMysql(c.Mysql.DataSource)
    return &ServiceContext{
        Config: c,
        UserModel: model.NewUserModel(conn,c.CacheRedis),
    }
}

  我们可以看到,servicecontext.go里定义了一个名为ServiceContext的结构体,ServiceContext的结构体承载着config.Config和model.UserModel,分别对应的是user模块的数据库配置和UserModel。

   4.编写user模块的业务逻辑,这里对应的是处理登录的业务逻辑

  cd service/user/api/internal/logic/,编辑loginlogic.go,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package logic
 
import (
    "book/service/user/model"
    "context"
    "errors"
    "github.com/golang-jwt/jwt/v4"
    "strings"
    "time"
 
    "book/service/user/api/internal/svc"
    "book/service/user/api/internal/types"
 
    "github.com/zeromicro/go-zero/core/logx"
)
 
type LoginLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}
 
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
    return &LoginLogic{
        Logger: logx.WithContext(ctx),
        ctx:    ctx,
        svcCtx: svcCtx,
    }
}
 
func (l *LoginLogic) Login(req *types.LoginReq) (*types.LoginReply, error) {
    if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 {
        return nil, errors.New("参数错误")
    }
 
    userInfo, err := l.svcCtx.UserModel.FindOneByNumber(l.ctx, req.Username)
    switch err {
    case nil:
    case model.ErrNotFound:
        return nil, errors.New("用户名不存在")
    default:
        return nil, err
    }
 
    if userInfo.Password != req.Password {
        return nil, errors.New("用户密码不正确")
    }
 
    // ---start---
    now := time.Now().Unix()
    accessExpire := l.svcCtx.Config.Auth.AccessExpire
    jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id)
    if err != nil {
        return nil, err
    }
    // ---end---
 
    return &types.LoginReply{
        Id:           userInfo.Id,
        Name:         userInfo.Name,
        Gender:       userInfo.Gender,
        AccessToken:  jwtToken,
        AccessExpire: now + accessExpire,
        RefreshAfter: now + accessExpire/2,
    }, nil
}
 
func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) {
    claims := make(jwt.MapClaims)
    claims["exp"] = iat + seconds
    claims["iat"] = iat
    claims["userId"] = userId
    token := jwt.New(jwt.SigningMethodHS256)
    token.Claims = claims
    return token.SignedString([]byte(secretKey))
}

第四步:启动user模块

  cd service/user/api,执行如下命令:

1
go run user.go -f etc/user-api.yaml

第五步:使用cmd测试user模块

1
curl -i -X POST http://127.0.0.1:8888/user/login -H "Content-Type: application/json" -d "{ \"username\":\"666\", \"password\":\"123456\" }"

  可以看到/user/login接口以json的形式返回了对应的用户信息,但是并不包含password字段,如果我们想得到password字段,该怎么实现呢?使用go-zero的好处就体现出来了,我们不需要编辑生成的api服务,我们只需要在user.api文件的LoginReply结构体里加上Password,重新生成api服务,重新生成api服务时不会覆盖已经生成的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type (
    LoginReq {
        Username string `json:"username"`
        Password string `json:"password"`
    }
 
    LoginReply {
        Id           int64  `json:"id"`
        Name         string `json:"name"`
        Password     string `json:"password"`
        Gender       string `json:"gender"`
        AccessToken  string `json:"accessToken"`
        AccessExpire int64  `json:"accessExpire"`
        RefreshAfter int64  `json:"refreshAfter"`
    }
)
 
service     user-api    {
    @handler login
    post /user/login    (LoginReq)  returns (LoginReply)
}

  

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @   jamstack  阅读(782)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示