add readme
This commit is contained in:
parent
cc98043aff
commit
8997577181
110
router/README.md
Normal file
110
router/README.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# 路由包使用说明
|
||||||
|
|
||||||
|
## 基础说明
|
||||||
|
|
||||||
|
- 基于 [web框架GIN](https://github.com/gin-gonic/gin) 的二次封装
|
||||||
|
|
||||||
|
## 为什么要二次封装
|
||||||
|
|
||||||
|
二次封装的想法来源于 [web框架GoFrame](https://github.com/gogf/gf)。gf框架本身内置集成了大量组件, 对于轻量应用开发, 相比较而言比较笨重, 但是启根据请求数据结构、响应数据结构自动生成表单的能力十分实用。
|
||||||
|
在采用gin开发时,一般接口文档有两种选择 :
|
||||||
|
- 在内部文档平台手搓文档, 并人工维护, 此方案最终演变方式大概率为文档主键滞后,与接口实现并不一致
|
||||||
|
- 利用golang的文档生成工具[swag]() 自动生成swagger文档, 此种方案需要在代码中引入大量swag工具解析时需要的注释, 且当输入/输出发生变化时需要维护相关注释
|
||||||
|
基于以上问题, 对gin的路由注册进行二次封装
|
||||||
|
|
||||||
|
## 二次封装解决哪些问题
|
||||||
|
|
||||||
|
- 文档维护的繁琐性 : 会通过反射自动基于 **`请求的数据结构 + 返回的数据结构`** , 生成接口文档, 专注于数据结构设计即可, 无需关注文档相关内容, 程序接管, 自动生成
|
||||||
|
- 做接口设计的统一性规范约束, 如 :
|
||||||
|
- 入参数据类型不能为 any
|
||||||
|
- 入参类型不能为 map , 但是特定字段特定场景, 可以强行禁用此规则等
|
||||||
|
- 返回值必须为结构体等
|
||||||
|
|
||||||
|
## 设计方案
|
||||||
|
|
||||||
|
###【可选】路由组定义
|
||||||
|
|
||||||
|
路由组可以通过函数进行定义, 函数名必须需为 : **`RouterPrefix`** , 函数无任何参数, 返回路由组名称, 示例 :
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TestController struct{}
|
||||||
|
|
||||||
|
func (t *TestController) RouterPrefix() string {
|
||||||
|
return "/uri/prefix"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
###【可选】路由组中间件定义
|
||||||
|
|
||||||
|
路由组中间件可以通过函数进行定义, 函数名必须需为 : **`RouterMiddleware`** , 函数无任何参数, 返回路由组中间件列表 **`[]gin.HandlerFunc`**, 示例 :
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TestController struct{}
|
||||||
|
|
||||||
|
func (t *TestController) RouterPrefix() string {
|
||||||
|
return "/uri/prefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
|
||||||
|
return []gin.HandlerFunc{
|
||||||
|
func(ctx *gin.Context) {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 接口逻辑处理定义
|
||||||
|
|
||||||
|
接口逻辑处理函数, 函数名称自定义, 必须是个 **`可导出函数`** , 函数接收两个参数, 分别是 :
|
||||||
|
|
||||||
|
- *gin.Context : gin框架的上下文信息
|
||||||
|
- any: 表单参数 **结构体指针** , 注意 : 类型必须为结构体指针
|
||||||
|
|
||||||
|
函数返回值有两个 :
|
||||||
|
- any : 返回的业务数据, 必须是个 **`结构体指针`**
|
||||||
|
- error : 可以是内置的 **error** , 也可以是 **exception.IException** , 建议使用 exception.IException , 可承载更多的异常信息
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TestController struct{}
|
||||||
|
|
||||||
|
func (t *TestController) RouterPrefix() string {
|
||||||
|
return "/uri/prefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
|
||||||
|
return []gin.HandlerFunc{
|
||||||
|
func(ctx *gin.Context) {
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (t *TestController) Uri(ctx *gin.Context, formData *TestForm) (any, error) {
|
||||||
|
return formData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestForm struct {
|
||||||
|
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
|
||||||
|
Age int `json:"age" form:"age"`
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
Test *Test `json:"test" form:"test"`
|
||||||
|
Num *int64 `json:"num" form:"num"`
|
||||||
|
}
|
||||||
|
type Test struct {
|
||||||
|
L string `json:"l"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseController(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
controller any
|
||||||
|
}
|
||||||
|
Register(8080, &TestController{})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
注意事项:
|
||||||
|
|
||||||
|
- Register 方法第一个入参是监听的端口
|
||||||
|
- 后面若干个controller实例, 朱一必须以指针的方式传入
|
@ -130,6 +130,12 @@ func parseUriConfig(methodType reflect.Type, routerPrefix string) (*UriConfig, e
|
|||||||
Strict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameStrict))) >= 0,
|
Strict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameStrict))) >= 0,
|
||||||
FormDataType: methodType.In(2).Elem(),
|
FormDataType: methodType.In(2).Elem(),
|
||||||
}
|
}
|
||||||
|
// 校验 FormDataType
|
||||||
|
for fieldIdx := 0; fieldIdx < uriConfig.FormDataType.NumField(); fieldIdx++ {
|
||||||
|
if uriConfig.FormDataType.Field(fieldIdx).Type.Kind() == reflect.Interface {
|
||||||
|
panic("request param set type `interface` is not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
return uriConfig, nil
|
return uriConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,11 @@ type TestForm struct {
|
|||||||
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
|
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
|
||||||
Age int `json:"age" form:"age"`
|
Age int `json:"age" form:"age"`
|
||||||
Name string `json:"name" form:"name"`
|
Name string `json:"name" form:"name"`
|
||||||
Test struct {
|
Test *Test `json:"test" form:"test"`
|
||||||
|
Num *int64 `json:"num" form:"num"`
|
||||||
|
}
|
||||||
|
type Test struct {
|
||||||
L string `json:"l"`
|
L string `json:"l"`
|
||||||
} `json:"test" form:"test"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_parseController(t *testing.T) {
|
func Test_parseController(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user