【Golang】使用 goctl 进行代码生成——Go-Zero 实践初探

前言

在学习go-zero的过程中,我简单记录了http、rpc服务的代码生成方法,以及mysql和mongodb的数据库模型代码生成步骤,作为备忘录使用。初学,如有不妥之处,感谢指正。

包结构

这部分借用官网内容,介绍了生成代码的包结构。

工程维度

.
├── consumer
├── go.mod
├── internal
│   └── model
├── job
├── pkg
├── restful
├── script
└── service
  • consumer: 队列消费服务
  • internal: 工程内部可访问的公共模块
  • job: cron job 服务
  • pkg: 工程外部可访问的公共模块
  • restful:HTTP 服务目录,下存放以服务为维度的微服务
  • script:脚本服务目录,下存放以脚本为维度的服务
  • service:gRPC 服务目录,下存放以服务为维度的微服务

服务维度

example
├── etc
│   └── example.yaml
├── main.go
└── internal    
	├── config    
	│   └── config.go 
	├── handler    
	│   ├── xxxhandler.go
    │   └── xxxhandler.go    
    ├── logic    
    │   └── xxxlogic.go    
    ├── svc    
    │   └── servicecontext.go    
    └── types    
        └── types.go
  • example:单个服务目录,一般是某微服务名称
  • etc:静态配置文件目录
  • main.go:程序启动入口文件
  • internal:单个服务内部文件,其可见范围仅限当前服务
  • config:静态配置文件对应的结构体声明目录
  • handler:handler 目录,可选,一般 http 服务会有这一层做路由管理,handler 为固定后缀
  • logic:业务目录,所有业务编码文件都存放在这个目录下面,logic 为固定后缀
  • svc:依赖注入目录,所有 logic 层需要用到的依赖都要在这里进行显式注入
  • types:结构体存放目录

Http服务代码生成

api描述http服务,编写好api文件后,使用命令goctl api go --api xx.api --dir . 在指定目录生成文件。
注意修改api文件重新生成后记得检查一下是否代码逻辑成功修改了,有些修改不会被检测到,此时要删掉文件重新生成。
api指令常见的用法如下表所示,更详细的用法可以参考 https://go-zero.dev/docs/tutorials/cli/api 。前端代码生成似乎略微有些地方不兼容。

功能 命令 说明
生成 Go 后端服务 goctl api go --api <api_file> --dir <output_dir> --style <style> 根据 .api 文件生成完整的 HTTP 后端服务代码。 --api:输入 .api 文件路径;
--dir:代码输出目录;--style:文件命名风格(gozero/goZero/go_zero)。
创建 .api 模板文件 goctl api new --style <style> <service_name> 创建包含示例语法的 .api 模板文件。 <service_name>:生成 service_name.api 文件; --style:文件命名风格。
格式化 .api 文件 goctl api format --dir <api_file_or_dir> 自动格式化指定的 .api 文件或目录。
校验 .api 文件 goctl api validate --api <api_file> 语法检查(不生成代码),适合在提交前或 CI 流程中使用。
生成 TS/JS 前端代码 goctl api ts --api <api_file> --dir <output_dir> 生成前端调用代码(请求函数 + 类型定义)。 --api:api文件;--dir:输出文件夹;下面所有客户端代码同理。
生成 Dart 客户端代码 goctl api dart --api <api_file> --dir <output_dir> 生成 Dart(用于 Flutter)的客户端调用代码(请求函数 + 类型定义)。
生成 Kotlin 客户端代码 goctl api kt --api <api_file> --dir <output_dir> --pkg xx 生成 Kotlin 语言的客户端调用代码。
生成 文档 文件 goctl api doc --dir <api_dir> --o <output_dir> 生成Markdown说明文件。--dir:api文件所在目录;--o 文档输出目录。
生成 Swagger 文档 goctl api swagger --api <api_file> --dir <output_dir> --filename <filename> 生成 swagger.json 文件。 --filename:指定输出文件名(可选,默认 swagger.json)。
自定义文件生成 goctl api plugin --plugin <plugin_name> --api <api_file> --dir <output_dir> 使用插件生成自定义文件。 --plugin:插件名称

一个包含所有语法块的完整写法如下:
这是主api文件,hello.api。

syntax = "v1"

info (
	title:   "api 文件完整示例写法"
	date:    "2025 年 7 月"
	version: "v1"
)

import "hello2.api" //使用相对路径,导入其它api文件

// g1
// 定义结构体
type (
	UpdateReq {
   
   
		Arg1 string `json:"arg1"`
	}
	ListItem {
   
   
		Value1 string `json:"value1"`
	}
	// 定义登陆接口的json请求体
	LoginReq {
   
   
		Username string `json:"username"`
		Password string `json:"password"`
	}
	// 定义登陆接口的json响应体
	LoginResp {
   
   
		Name string `json:"name"`
	}
	FormExampleReq {
   
   
		Name string `form:"name"`
	}
	PathExampleReq {
   
   
		// path 标签修饰的 id 必须与请求路由中的片段对应,如
		// id 在 service 语法块的请求路径上一定会有 :id 对应,见下文。
		ID string `path:"id"`
	}
	PathExampleResp {
   
   
		Name string `json:"name"`
	}
)

// g2
type (
	Base {
   
   
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
	UserInfo {
   
   
		Id   int64  `json:"id"`
		Name string `json:"name"`
		Desc string `json:"desc"`
	}
	GetUserInfoReq {
   
   
		Id int64 `json:"id"`
	}
	Nested {
   
   
		Foo string `json:"foo"`
	}
	GetUserInfoResp {
   
   
		// api 支持结构体引用
		Base
		Data UserInfo `json:"data"`
		Nested Nested `json:"nested"`
	}
)

// 定义HTTP服务
// @server 语法块主要用于控制对 HTTP 服务生成时 meta 信息,目前支持功能有:
// 1. 路由分组
// 2. 中间件声明
// 3. 路由前缀
// 4. 超时配置
// 5. jwt 鉴权开关
// 所有声明仅对当前 service 中的路由有效
@server (
	jwt:    Auth // 对当前 Foo 语法块下的所有路由,开启 jwt 认证,不需要则请删除此行
	prefix: /v1 // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行
	group:  g1 // 对当前 Foo 语法块下的所有路由,路由归并到 g1 目录下,不需要则请删除此行
	// 定义一个超时时长为 3 秒的超时配置,这里可填写为 time.Duration 的字符串形式,详情可参考
	// https://pkg.go.dev/time#Duration.String
	timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行
	// 定义一个鉴权控制的中间件,多个中间件以英文逗号分割,如 Middleware1,Middleware2,中间件按声明顺序执行
	middleware: AuthInterceptor // 对当前 Foo 语法块下的所有路由添加中间件,只是生成代码框架,逻辑自己填写
	// 定义一个请求体限制在 1MB 以内的请求,goctl >= 1.5.0 版本支持
	maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持
)
// 微服务名称为Foo
// 定义多个service代码块时,服务名称必须一致
service Foo {
   
   
	// 定义 http.HandleFunc 转换的go文件名称以及方法,每个接口都要有一个handler
	@handler pathExample
	// 定义方法为 get
	// 路由为/path/example/:id
	// 请求体为PathExampleReq
	// 响应体为PathExampleResp
	get /path/example/:id (PathExampleReq) returns (PathExampleResp)

	// 定义没有请求体和响应体的接口,如 ping
	@handler ping
	get /ping

	// 定义只有请求体的接口,如更新信息
	@handler update
	post /update (UpdateReq)

	// 定义只有响应体的结构,如获取全部信息列表
	@handler list
	get /list returns ([]ListItem)

	// 定义有结构体和响应体的接口,如登录
	@handler login
	post /login (LoginReq) returns (LoginResp)

	// 定义表单请求
	@handler formExample
	post /form/example (FormExampleReq)
}

@server (
	prefix: /v2 // 新增v2路由前缀
	group:  g2 // 当前语法块下的路由并到g2文件夹内
)
// 定义一个名称为 Foo 的服务
service Foo {
   
   
	// 定义 http.HandleFunc 转换的 go 文件名称及方法,每个接口都会跟一个 handler
	@handler getUserInfo
	// 定义接口
	// 请求方法为 post
	// 路由为 /user/info
	// 请求体为 GetUserInfoReq
	// 响应体为 GetUserInfoResp,响应体必须有 returns 关键字修饰
	post 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值