Go-Zero定义API实战:探索API语法规范与最佳实践(五)
前言
上一篇文章带你实现了Go-Zero模板定制化,本文将继续分享如何使用GO-ZERO进行业务开发。
通过编写API层,我们能够对外进行接口的暴露,因此学习规范的API层编写姿势是很重要的。
通过本文的分享,你将能够学习到Go-Zero的API语法规范,以及学会实际上手使用。
概述
下文所说的是 api
是 go-zero 自研的领域特性语言(下文称 api 语言 或 api 描述语言),旨在实现人性化的基础描述语言,作为生成 HTTP 服务最基本的描述语言。
api 领域特性语言包含语法版本、info 块、结构体声明、服务描述等几大块语法组成,其中结构体和 Golang 结构体 语法几乎一样,只是移除了 struct
关键字。
实战前准备
首先需要你在本地安装goctl、go-zero
,下载教学和地址点击这里,按照教程操作即可,非常简单。
下面按顺序和我操作吧,对使用模板快速生成API层不清楚的同学务必先看我前篇文章:Go-Zero goctl实战
这里我假设你已经创建好了一个API服务的demo,且目录结构长这样:
学习API语法
对于Go语言开发者来说,Go-Zero的API语法学习和理解成本极低,我们可以很轻松的学会API语法。下面我会为大家介绍重点需要掌握的语法。更详细的语法规范,可以参考官网:API 规范 | go-zero Documentation
生成API文件
cd demo
goctl api go -api demo.api -dir . -style gozero
- 基础的API文件
ID标识符
golang中的预定义类型、常量、函数,以及关键字在api里面同样适用
- 预定义
//预定义类型:
any bool byte comparable
complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
//预定义常量:
true false iota
//零值:
nil
//预定义函数:
append cap close complex copy delete imag len
make new panic print println real recover
- 关键字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
tip:需要注意的是 goctl api不支持any类型!!!
类型声明
规则
- 类型声明必须以
type
开头 - 不需要声明
struct
关键字 - 不支持嵌套结构体声明
- 不支持别名
示例
type StructureExample {
// 基本数据类型示例
BaseInt int `json:"base_int"`
BaseBool bool `json:"base_bool"`
BaseString string `json:"base_string"`
BaseByte byte `json:"base_byte"`
BaseFloat32 float32 `json:"base_float32"`
BaseFloat64 float64 `json:"base_float64"`
// 切片示例
BaseIntSlice []int `json:"base_int_slice"`
BaseBoolSlice []bool `json:"base_bool_slice"`
BaseStringSlice []string `json:"base_string_slice"`
BaseByteSlice []byte `json:"base_byte_slice"`
BaseFloat32Slice []float32 `json:"base_float32_slice"`
BaseFloat64Slice []float64 `json:"base_float64_slice"`
// map 示例
BaseMapIntString map[int]string `json:"base_map_int_string"`
BaseMapStringInt map[string]int `json:"base_map_string_int"`
BaseMapStringStruct map[string]*StructureExample `json:"base_map_string_struct"`
BaseMapStringIntArray map[string][]int `json:"base_map_string_int_array"`
// 匿名示例
*Base
// 指针示例
Base4 *Base `json:"base4"`
// 新的特性( goctl >= 1.5.1 版本支持 )
// 标签忽略示例
TagOmit string
}
路由前缀
我们可以通过prefix关键字区分路由组
接着再使用goctl api生成代码以及swagger,将swagger导入apifox查看路由前缀,可以看见就增添了前缀/demo。
不知道怎么生成api代码的同学可以看我往期的gozero实战分享——go-zero goctl实战
服务分组
当我们的业务体量上来后,服务接口也会越来越多,生成的代码文件(handler、logic文件等)也会越来越多。这时候我们就需要对不同的接口按一定的分组进行区分,用文件夹进行隔离,以便于开发和维护。
- 分组前的目录结构是这样的
- 我们先将生成的handler和logic文件删除。
- 只需要在@server语句块里面添加关键字group就能进行分组。分组后的结构如下图所示。
JWT校验
- 接下来我们再来看一下api文件中怎么开启jwt认证
- 在配置文件demo-api.yaml中添加jwt配置
- 在config文件中添加一个JWT认证需要的密钥和过期时间配置
JwtAuth struct { // JWT 认证需要的密钥和过期时间配置
AccessSecret string
AccessExpire int64
}
- 使用方法也很简单,我们在@service语句块中添加jwt关键字,使用Auth即可开启jwt。
- 通过测试请求我们可以看见返回401没有权限,说明jwt校验生效了
路由规则
- 路由必须以
/
开头 2. 路由节点必须以/
分隔 - 路由节点中可以包含
:
,但是:
必须是路由节点的第一个字符,:
后面的节点值必须要在结请求体中有path
tag 声明,用于接收路由参数,详细规则可参考 路由参数。 4. 路由节点可以包含字母、数字(goctl 1.5.1
支持,可参考 新版 API 解析器使用)、下划线、中划线
参数规则
接收规则 | 说明 | 生效范围 | 示例 |
---|---|---|---|
json | json 序列化 | 请求体&响应体 | json:"foo" |
path | 路由参数 | 请求体 | path:"id" |
form | post 请求的表单(支持 content-type 为 form-data 和 x-www-form-urlencoded ) 参数请求接收标识,get 请求的 query 参数接收标识 |
请求体 | form:"name" |
header | http 请求体接收标识 | 请求体 | header:"Content-Length" |
中间件声明
- 想要使用中间件,可以在@server语句块中使用关键字middleware生成一个中间件模板。
- TIP:需要注意的是中间件首字母必须大写,否则无法被其他包导入。
- 在svc包的servicecontext.go中注册中间件
- 生成的中间件代码如下
API Import
- 当我们的业务体量上来后,api文件可能会越来越大,又或者我们有一些公共结构体。如果我们都写在同一个api文件中,那么api文件将会变得非常巨大,不易阅读和维护,这时候就需要拆解api文件,通过import来导入。
syntax
- 版本信息,import中的版本信息必须与被import的api版本信息一样。
- 规范写法
syntax = "v1"
- 我们创建一个新的文件demo1.api,并且将分组而写到这个api文件下。
- 因为我们的请求体和响应体是公共结构体,都写在demo.api下面了,我们通过import "demo.api"就能导入demo.api。
完整的api文件模板
syntax = "v1"
type Request {
Name string `path:"name,options=you|me"`
}
type Response {
Message string `json:"message"`
}
@server (
prefix :/demo
group: demo_api
jwt: JwtAuth
middleware: Demo_middleware
)
// 分组1的服务
service demo-api {
@handler DemoHandler
post /from (Request) returns (Response)
}
// 分组2的服务
@server (
prefix :/demo1
group: demo_api1
)
service demo-api {
@handler DemoHandler1
get /from/:name(Request) returns (Response)
}
总结
这篇文章详细介绍了如何使用Go-Zero进行API的定义,并进行了实际演示。希望对你有帮助。
我将继续更新Go-Zero系列文章,如果你对Go语言或者微服务感兴趣,欢迎关注我,也欢迎直接私信我。