Go-Zero 接口请求完整流程分析

12 阅读7分钟

📋 目录结构说明

gozero/
├── api/
│   └── user.api              # API 定义文件(手动编写)
├── etc/
│   └── test-api.yaml         # 配置文件(数据库、Redis等配置)
├── internal/
│   ├── config/
│   │   └── config.go         # 配置结构体定义(goctl自动生成)
│   ├── handler/
│   │   ├── routes.go         # 路由注册文件(goctl自动生成)
│   │   ├── loginhandler.go   # 登录处理器(goctl自动生成,可编辑)
│   │   └── registerhandler.go # 注册处理器(goctl自动生成,可编辑)
│   ├── logic/
│   │   ├── loginlogic.go     # 登录业务逻辑(goctl自动生成,可编辑)
│   │   └── registerlogic.go  # 注册业务逻辑(goctl自动生成,可编辑)
│   ├── model/
│   │   └── usermodel.go      # 数据模型和数据库操作(手动编写)
│   ├── svc/
│   │   └── servicecontext.go # 服务上下文(goctl自动生成,可编辑)
│   ├── types/
│   │   └── types.go          # 请求/响应类型定义(goctl自动生成)
│   └── utils/
│       └── jwt.go            # JWT工具函数(手动编写)
├── test.go                   # 程序入口文件(goctl自动生成,可编辑)
└── go.mod                    # Go模块依赖

🔄 完整流程:从 .api 文件到接口返回

第一步:从 .api 文件生成代码

文件路径: api/user.api

执行命令:

goctl api go -api api/user.api -dir . -style gozero

生成的文件:

  1. internal/types/types.go - 根据 .api 中的 type 定义生成 Go 结构体
  2. internal/handler/routes.go - 根据 @handler 注解生成路由注册代码
  3. internal/handler/loginhandler.go - 登录接口的处理器(骨架代码)
  4. internal/handler/registerhandler.go - 注册接口的处理器(骨架代码)
  5. internal/logic/loginlogic.go - 登录业务逻辑(骨架代码)
  6. internal/logic/registerlogic.go - 注册业务逻辑(骨架代码)
  7. internal/svc/servicecontext.go - 服务上下文(骨架代码)
  8. internal/config/config.go - 配置结构体(骨架代码)
  9. test.go - 程序入口文件(骨架代码)

说明:

  • 所有生成的文件都标注了 // Code generated by goctl// Code scaffolded by goctl
  • 标注 DO NOT EDIT 的文件不要手动修改,重新生成会被覆盖
  • 标注 Safe to edit 的文件可以手动编辑,不会被覆盖

第二步:程序启动流程

文件路径: test.go (程序入口)

执行流程:

1. main() 函数启动
   ↓
2. 加载配置文件 etc/test-api.yaml
   ↓
3. 创建 HTTP 服务器 (rest.MustNewServer)
   ↓
4. 创建服务上下文 (svc.NewServiceContext)
   ├── 初始化 MySQL 数据库连接
   ├── 初始化 Redis 连接(如果配置了)
   └── 初始化 UserModel(用户模型)
   ↓
5. 注册路由 (handler.RegisterHandlers)
   ↓
6. 启动服务器 (server.Start())

关键代码位置:

// ... 启动流程代码 ...

第三步:路由注册

文件路径: internal/handler/routes.go

作用: 将 API 路径与处理器函数绑定

关键代码:

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
	server.AddRoutes(
		[]rest.Route{
			{
				Method:  http.MethodPost,
				Path:    "/api/user/login",
				Handler: LoginHandler(serverCtx),
			},
			{
				Method:  http.MethodPost,
				Path:    "/api/user/register",
				Handler: RegisterHandler(serverCtx),
			},
		},
	)
}

说明:

  • 当请求 POST /api/user/login 时,会调用 LoginHandler 函数
  • 当请求 POST /api/user/register 时,会调用 RegisterHandler 函数
  • 路由信息来自 api/user.api 文件中的定义

第四步:请求处理流程(以登录接口为例)

4.1 Handler 层 - 请求接收和参数解析

文件路径: internal/handler/loginhandler.go

作用:

  • 接收 HTTP 请求
  • 解析请求参数(JSON → Go 结构体)
  • 调用 Logic 层处理业务逻辑
  • 返回响应结果

执行流程:

1. 客户端发送 POST 请求到 /api/user/login
   ↓
2. routes.go 路由匹配,调用 LoginHandler
   ↓
3. LoginHandler 函数执行:
   ├── 解析请求体 JSON → types.LoginRequest
   ├── 创建 LoginLogic 实例
   ├── 调用 l.Login(&req) 执行业务逻辑
   └── 将响应结果返回给客户端

关键代码:

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)
		}
	}
}

说明:

  • httpx.Parse() 将 HTTP 请求体解析为 types.LoginRequest 结构体
  • types.LoginRequest 定义在 internal/types/types.go,由 user.api 自动生成
  • Handler 层只负责参数解析和结果返回,不包含业务逻辑

4.2 Logic 层 - 业务逻辑处理

文件路径: internal/logic/loginlogic.go

作用:

  • 参数验证
  • 业务逻辑处理
  • 调用 Model 层进行数据查询
  • 调用工具函数(如 JWT 生成)
  • 返回业务结果

执行流程:

1. 接收 LoginRequest 参数
   ↓
2. 参数验证(用户名、密码是否为空)
   ↓
3. 调用 Model 层查询用户
   ├── l.svcCtx.UserModel.FindOneByUsername(req.Username)
   └── 通过 ServiceContext 访问 UserModel
   ↓
4. 验证用户状态(是否被禁用)
   ↓
5. 验证密码(bcrypt 密码比对)
   ↓
6. 生成 JWT Token(调用 utils.GenerateToken)
   ↓
7. 构造 LoginResponse 并返回

关键代码:

func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
	// 1. 参数验证
	if req.Username == "" {
		return &types.LoginResponse{
			Code:    2001,
			Message: "用户名不能为空",
		}, nil
	}
	// ... 更多验证 ...

	// 2. 查找用户
	user, err := l.svcCtx.UserModel.FindOneByUsername(req.Username)
	// ... 错误处理 ...

	// 3. 检查用户状态
	if user.Status != 1 {
		return &types.LoginResponse{
			Code:    2005,
			Message: "用户已被禁用",
		}, nil
	}

	// 4. 验证密码
	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
	// ... 错误处理 ...

	// 5. 生成 JWT Token
	token, err := utils.GenerateToken(user.ID, user.Username)
	// ... 错误处理 ...

	// 6. 返回成功响应
	resp = &types.LoginResponse{
		Code:    0,
		Message: "登录成功",
		Data: types.LoginInfo{
			Token:    token,
			UserID:   user.ID,
			Username: user.Username,
		},
	}
	return resp, nil
}

说明:

  • Logic 层通过 svcCtx (ServiceContext) 访问 Model 层
  • svcCtx.UserModel 是在 servicecontext.go 中初始化的
  • Logic 层包含所有业务逻辑,不直接操作数据库

4.3 Model 层 - 数据库操作

文件路径: internal/model/usermodel.go

作用:

  • 定义数据模型结构(User 结构体)
  • 提供数据库操作方法(查询、插入、更新等)
  • 封装 SQL 语句执行

执行流程(登录场景):

1. Logic 层调用 l.svcCtx.UserModel.FindOneByUsername(username)
   ↓
2. Model 层执行 SQL 查询
   ├── 构建 SQL 语句:SELECT ... FROM user WHERE username = ?
   ├── 执行查询:m.conn.QueryRow(&user, query, username)
   └── 返回 User 结构体
   ↓
3. Logic 层接收查询结果

关键代码:

// FindOneByUsername 根据用户名查找用户
func (m *defaultUserModel) FindOneByUsername(username string) (*User, error) {
	query := `SELECT id, username, password, email, phone, status, created_at, updated_at FROM user WHERE username = ? LIMIT 1`
	var user User
	err := m.conn.QueryRow(&user, query, username)
	if err != nil {
		return nil, err
	}
	return &user, nil
}

说明:

  • Model 层直接操作数据库,使用 go-zero 的 sqlx
  • m.conn 是数据库连接,在 servicecontext.go 中初始化
  • Model 层只负责数据访问,不包含业务逻辑

4.4 ServiceContext - 服务上下文

文件路径: internal/svc/servicecontext.go

作用:

  • 在整个请求生命周期中共享资源
  • 包含:配置、数据库连接、Redis 连接、Model 实例等
  • 所有 Handler 和 Logic 都可以通过 svcCtx 访问这些资源

关键代码:

type ServiceContext struct {
	Config    config.Config
	DB        sqlx.SqlConn    // MySQL 数据库连接
	Redis     *redis.Redis    // Redis 客户端
	UserModel model.UserModel // 用户模型
}

func NewServiceContext(c config.Config) *ServiceContext {
	// 初始化 MySQL 连接
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
		c.MySQL.User,
		c.MySQL.Password,
		c.MySQL.Host,
		c.MySQL.Port,
		c.MySQL.DBName,
		c.MySQL.Charset,
	)
	db := sqlx.NewMysql(dsn)

	// 初始化用户模型
	userModel := model.NewUserModel(db)

	return &ServiceContext{
		Config:    c,
		DB:        db,
		Redis:     redisClient,
		UserModel: userModel,
	}
}

说明:

  • ServiceContext 在程序启动时创建一次,所有请求共享
  • Handler 和 Logic 通过 svcCtx 参数访问共享资源
  • 如果需要添加新的数据库表或外部服务,在这里添加字段

第五步:响应返回

流程:

1. Logic 层返回 LoginResponse
   ↓
2. Handler 层接收响应
   ↓
3. Handler 层调用 httpx.OkJsonCtx() 将响应序列化为 JSON
   ↓
4. HTTP 响应返回给客户端

响应格式:

{
  "code": 0,
  "message": "登录成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user_id": 1,
    "username": "testuser"
  }
}

📊 完整流程图

┌─────────────────────────────────────────────────────────────┐
│ 第一步:代码生成                                              │
│ api/user.api → goctl api go → 生成所有骨架代码              │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第二步:程序启动                                              │
│ test.go (main)                                               │
│   ├── 加载配置 etc/test-api.yaml                            │
│   ├── 创建 HTTP 服务器                                       │
│   ├── 创建 ServiceContext(初始化 DB、Redis、Model)         │
│   └── 注册路由 handler.RegisterHandlers()                   │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第三步:请求到达                                              │
│ POST /api/user/login                                         │
│   ↓                                                          │
│ routes.go 路由匹配 → LoginHandler                            │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第四步:Handler 层                                            │
│ internal/handler/loginhandler.go                            │
│   ├── 解析请求参数 (JSON → LoginRequest)                    │
│   ├── 创建 LoginLogic 实例                                  │
│   └── 调用 l.Login(&req)                                    │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第五步:Logic 层                                              │
│ internal/logic/loginlogic.go                                │
│   ├── 参数验证                                               │
│   ├── 调用 Model 查询用户                                   │
│   │   └── svcCtx.UserModel.FindOneByUsername()             │
│   ├── 验证密码                                               │
│   ├── 生成 JWT Token (utils.GenerateToken)                 │
│   └── 返回 LoginResponse                                    │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第六步:Model 层                                              │
│ internal/model/usermodel.go                                 │
│   ├── 执行 SQL 查询                                         │
│   │   SELECT ... FROM user WHERE username = ?              │
│   └── 返回 User 结构体                                       │
└─────────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────────┐
│ 第七步:响应返回                                              │
│ Logic → Handler → httpx.OkJsonCtx() → JSON 响应 → 客户端    │
└─────────────────────────────────────────────────────────────┘

🔑 关键文件说明

自动生成的文件(不要手动修改)

  1. internal/types/types.go

    • user.api 中的 type 定义自动生成
    • 包含所有请求和响应的结构体
  2. internal/handler/routes.go

    • user.api 中的 @handler 注解自动生成
    • 包含路由注册代码

可编辑的文件(业务逻辑在这里)

  1. internal/handler/*handler.go

    • 处理器层,负责参数解析和结果返回
    • 通常不需要修改,除非需要添加中间件
  2. internal/logic/*logic.go

    • 业务逻辑层,核心业务代码在这里编写
    • 包含参数验证、业务处理、调用 Model 层
  3. internal/model/*model.go

    • 数据模型层,手动编写
    • 定义数据结构和数据库操作方法
  4. internal/svc/servicecontext.go

    • 服务上下文,包含共享资源
    • 需要添加新的 Model 或外部服务时在这里添加
  5. internal/utils/*.go

    • 工具函数,手动编写
    • 如 JWT 生成、加密解密等

📝 总结

请求流程:

客户端请求 
  → routes.go (路由匹配)
  → handler (参数解析)
  → logic (业务逻辑)
  → model (数据库操作)
  → logic (处理结果)
  → handler (返回响应)
  → 客户端接收

各层职责:

  • Handler 层:HTTP 请求/响应处理,参数解析
  • Logic 层:业务逻辑处理,参数验证,调用 Model 和工具函数
  • Model 层:数据库操作,SQL 执行
  • ServiceContext:共享资源管理(DB、Redis、Model 等)

数据流向:

JSON 请求 → types.Request → logic 处理 → model 查询 → 数据库
                                                      ↓
JSON 响应 ← types.Response ← logic 构造 ← model 返回 ← 数据库结果