# Api
用于从 interface
快速生成 API路由表
以及无外部框架依赖,类型安全的静态调用层代码。
# 使用
# 注解
需要在 interface
对象 及 方法 都添加注解
package x
// +zz:api:[filename][:options...]
type T interface {
// +zz:api:[resource][:options...]
Method(param Param) (result Result, err error)
// 没有注解的方法会被忽略
InternalMethod(param Param) (result Result, err error)
// 继承的接口会被忽略
AnonymousT
}
示例:
package x
// +zz:api:./:prefix=book:public
type BookService interface {
// +zz:api:get|{id}
GetBook(id int) (book Book, err error)
}
# 注解对象
interface
对象 及 interface
方法
# 用于 interface
对象的 必填参数
# filename
指定生成文件路径
示例:
package x
// +zz:api:./
type T interface{}
# 用于 interface
方法 的 必填参数
# resource
会作为 API路由表
中的一级属性
示例:
package x
// +zz:api:./
type T interface {
// +zz:api:get|detail
Method()
}
# 可选参数
该插件可选参数可使用任意 Key-Value
,所有可选参数会作为 map[string]string
类型,收集到 API路由表
的 options
字段。
interface
对象的可选参数 会尝试覆盖到 接口方法上。例:
package x
// +zz:api:./:prefix=book:role=read
type BookService interface {
// +zz:api:get
ListBook() (book []Book, err error)
// +zz:api:get|{id}
GetBook(id int) (book Book, err error)
// +zz:api:post|{id}:role=write
NewBook(book Book) (id int, err error)
// +zz:api:put|{id}:role=write
UpdateBook(book Book) (err error)
}
等价于
package x
// +zz:api:./
type BookService interface {
// +zz:api:get:prefix=book:role=read
ListBook() (book []Book, err error)
// +zz:api:get|{id}:prefix=book:role=read
GetBook(id int) (book Book, err error)
// +zz:api:post|{id}:prefix=book:role=write
NewBook(book Book) (id int, err error)
// +zz:api:put|{id}:prefix=book:role=write
UpdateBook(book Book) (err error)
}
# 其他约定规则
# invoke 调用函数 生成
对满足条件的部分接口方法可以自动生成类型安全的 invoke
静态调用函数:
- 方法最多有 两个入参 及 两个返回值
- 两个入参时,第一个入参类型必须为
context.Context
,且第二个入参为不同类型 - 两个返回值,第二个返回值类型必须为
error
,且第一个返回值为不同类型
# 示例
# 示例一
package api01
import (
"context"
)
type (
QueryBook struct{}
ListBook struct{}
DataBook struct{}
FormBook struct{}
QueryUser struct{}
ListUser struct{}
DataUser struct{}
FormUser struct{}
)
//go:generate gozz run -p "api" ./
// +zz:api:./:prefix={{ snake .Name }}:id={{ .Name }}.{{ .FieldName }}
type (
BookService interface {
// +zz:api:get
List(ctx context.Context, query QueryBook) (ret ListBook, err error)
// +zz:api:get|{id}
Get(ctx context.Context, query QueryBook) (data DataBook, err error)
// +zz:api:post
Create(ctx context.Context, form FormBook) (data DataBook, err error)
// +zz:api:put|{id}
Edit(ctx context.Context, form FormBook) (data DataBook, err error)
}
UserService interface {
// +zz:api:get
List(ctx context.Context, query QueryUser) (ret ListUser, err error)
// +zz:api:get|{id}
Get(ctx context.Context, query QueryUser) (data DataBook, err error)
// +zz:api:post
Create(ctx context.Context, query FormUser) (data DataBook, err error)
// +zz:api:put|{id}
Edit(ctx context.Context, form FormUser) (data DataBook, err error)
}
)
例子中有两个接口,提供了对 User
和 Book
两实体基本的增删查改
执行 gozz run -p "api" ./
,生成了 zzgen.api.go
和默认模版文件。
// Code generated by gozz:api github.com/go-zing/gozz. DO NOT EDIT.
package api01
import (
"context"
)
var _ = context.Context(nil)
type Apis struct {
BookService BookService
UserService UserService
}
func (s Apis) Iterate(fn func(interface{}, []map[string]interface{})) {
for _, f := range []func() (interface{}, []map[string]interface{}){
s._BookService,
s._UserService,
} {
fn(f())
}
}
func (s Apis) _BookService() (interface{}, []map[string]interface{}) {
t := s.BookService
return &t, []map[string]interface{}{
{
"name": "List",
"resource": "get",
"options": map[string]string{
"id": "BookService.List",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryBook
if err := dec(&in); err != nil {
return nil, err
}
return t.List(ctx, in)
},
},
{
"name": "Get",
"resource": "get|{id}",
"options": map[string]string{
"id": "BookService.Get",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Get(ctx, in)
},
},
{
"name": "Create",
"resource": "post",
"options": map[string]string{
"id": "BookService.Create",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Create(ctx, in)
},
},
{
"name": "Edit",
"resource": "put|{id}",
"options": map[string]string{
"id": "BookService.Edit",
"prefix": "book_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormBook
if err := dec(&in); err != nil {
return nil, err
}
return t.Edit(ctx, in)
},
},
}
}
func (s Apis) _UserService() (interface{}, []map[string]interface{}) {
t := s.UserService
return &t, []map[string]interface{}{
{
"name": "List",
"resource": "get",
"options": map[string]string{
"id": "UserService.List",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryUser
if err := dec(&in); err != nil {
return nil, err
}
return t.List(ctx, in)
},
},
{
"name": "Get",
"resource": "get|{id}",
"options": map[string]string{
"id": "UserService.Get",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in QueryUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Get(ctx, in)
},
},
{
"name": "Create",
"resource": "post",
"options": map[string]string{
"id": "UserService.Create",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Create(ctx, in)
},
},
{
"name": "Edit",
"resource": "put|{id}",
"options": map[string]string{
"id": "UserService.Edit",
"prefix": "user_service",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in FormUser
if err := dec(&in); err != nil {
return nil, err
}
return t.Edit(ctx, in)
},
},
}
}
通过自动生成的 invoke
函数,可以快速地对接各种协议自动化参数绑定,并类型安全地调起接口实现获取返回值。
结合 wire 插件的依赖自动注入,可以将接口和接口实现自动化对接到各种Web框架提供API服务。
结合 doc 插件的类型字段注释映射表,可以在 interface
和类型设计时快速同步输出API接口文档。
通过自定义的 options
可以快速地组合和编排接口中间件及中间件参数,实现权限管理,服务自描述等常用功能。
# 示例二
以下示例展示了 invoke
静态调用自动化生成的广泛支持范围。
package api02
import (
"context"
)
//go:generate gozz run -p "api" ./
// +zz:api:./:path={{ snake .FieldName }}
type (
T interface {
// +zz:api:get
Empty()
// +zz:api:get
Ret() (ret int)
// +zz:api:get
Error() (err error)
// +zz:api:get
RetError() (ret int, err error)
// +zz:api:get
Context(ctx context.Context)
// +zz:api:get
ContextRet(ctx context.Context) (ret int)
// +zz:api:get
ContextError(ctx context.Context) (err error)
// +zz:api:get
ContextRetError(ctx context.Context) (ret int, err error)
// +zz:api:get
Param(param int)
// +zz:api:get
ParamRet(param int) (ret error)
// +zz:api:get
ParamError(param int) (err error)
// +zz:api:get
ParamRetError(param int) (ret int, err error)
// +zz:api:get
ContextParam(ctx context.Context, param int)
// +zz:api:get
ContextParamRet(ctx context.Context, param int) (ret int)
// +zz:api:get
ContextParamError(ctx context.Context, param int) (err error)
// +zz:api:get
ContextParamRetError(ctx context.Context, param int) (ret int, err error)
// +zz:api:get
ComplexParam(param map[context.Context][]struct {
Field []func(context.Context) interface {
context.Context
}
})
// +zz:api:get
PtrParam(*int)
}
)
执行 gozz run -p "api" ./
,生成了 zzgen.api.go
。
// Code generated by gozz:api github.com/go-zing/gozz. DO NOT EDIT.
package api02
import (
"context"
)
var _ = context.Context(nil)
type Apis struct {
T T
}
func (s Apis) Iterate(fn func(interface{}, []map[string]interface{})) {
for _, f := range []func() (interface{}, []map[string]interface{}){
s._T,
} {
fn(f())
}
}
func (s Apis) _T() (interface{}, []map[string]interface{}) {
t := s.T
return &t, []map[string]interface{}{
{
"name": "Empty",
"resource": "get",
"options": map[string]string{
"path": "empty",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
t.Empty()
return nil, nil
},
},
{
"name": "Ret",
"resource": "get",
"options": map[string]string{
"path": "ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.Ret(), nil
},
},
{
"name": "Error",
"resource": "get",
"options": map[string]string{
"path": "error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return nil, t.Error()
},
},
{
"name": "RetError",
"resource": "get",
"options": map[string]string{
"path": "ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.RetError()
},
},
{
"name": "Context",
"resource": "get",
"options": map[string]string{
"path": "context",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
t.Context(ctx)
return nil, nil
},
},
{
"name": "ContextRet",
"resource": "get",
"options": map[string]string{
"path": "context_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.ContextRet(ctx), nil
},
},
{
"name": "ContextError",
"resource": "get",
"options": map[string]string{
"path": "context_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return nil, t.ContextError(ctx)
},
},
{
"name": "ContextRetError",
"resource": "get",
"options": map[string]string{
"path": "context_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
return t.ContextRetError(ctx)
},
},
{
"name": "Param",
"resource": "get",
"options": map[string]string{
"path": "param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.Param(in)
return nil, nil
},
},
{
"name": "ParamRet",
"resource": "get",
"options": map[string]string{
"path": "param_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ParamRet(in)
},
},
{
"name": "ParamError",
"resource": "get",
"options": map[string]string{
"path": "param_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ParamError(in)
},
},
{
"name": "ParamRetError",
"resource": "get",
"options": map[string]string{
"path": "param_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ParamRetError(in)
},
},
{
"name": "ContextParam",
"resource": "get",
"options": map[string]string{
"path": "context_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.ContextParam(ctx, in)
return nil, nil
},
},
{
"name": "ContextParamRet",
"resource": "get",
"options": map[string]string{
"path": "context_param_ret",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ContextParamRet(ctx, in), nil
},
},
{
"name": "ContextParamError",
"resource": "get",
"options": map[string]string{
"path": "context_param_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return nil, t.ContextParamError(ctx, in)
},
},
{
"name": "ContextParamRetError",
"resource": "get",
"options": map[string]string{
"path": "context_param_ret_error",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
return t.ContextParamRetError(ctx, in)
},
},
{
"name": "ComplexParam",
"resource": "get",
"options": map[string]string{
"path": "complex_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in map[context.Context][]struct {
Field []func(context.Context) interface {
context.Context
}
}
if err := dec(&in); err != nil {
return nil, err
}
t.ComplexParam(in)
return nil, nil
},
},
{
"name": "PtrParam",
"resource": "get",
"options": map[string]string{
"path": "ptr_param",
},
"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
var in int
if err := dec(&in); err != nil {
return nil, err
}
t.PtrParam(&in)
return nil, nil
},
},
}
}