1 Star 0 Fork 0

hongzhaomin / ginplus

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
resolver.go 8.12 KB
一键复制 编辑 原始数据 按行查看 历史
package ginplus
import (
"errors"
"fmt"
"gitee.com/hongzhaomin/hzm-common-go/slice"
"gitee.com/hongzhaomin/hzm-common-go/strutil"
"github.com/gin-gonic/gin"
"net/http"
"reflect"
)
// controller父类名称
var controllerParentName = getTypeName(new(RestController))
// controller类tag,代表整个controller的统一属性
const (
Path = "Path"
Permission = "Permission" // 权限tag,支持整个controller类和属性
Default = "default" // 默认值tag
)
// controller字段tag,这里表示的是方法的属性
const (
GetMapping = "GetMapping"
PostMapping = "PostMapping"
PutMapping = "PutMapping"
DeleteMapping = "DeleteMapping"
MappingHandler = "MappingHandler"
RequestParam = "RequestParam"
)
// 解析Controller
func resolveIcList(register IGinConfiguration) []*handlerMappingChain {
mappingChains := make([]*handlerMappingChain, 0)
for _, ic := range register.RegisControllers() {
mappingChains = append(mappingChains,
&handlerMappingChain{
controller: ic,
mappings: doResolveSingle(ic),
})
}
return mappingChains
}
func doResolveSingle(ic IController) []*handlerMapping {
mustPoint(ic)
var mappings []*handlerMapping
rvIc := reflect.Indirect(reflect.ValueOf(ic))
rtIc := rvIc.Type()
if rtIc.Kind() == reflect.Struct {
for i := 0; i < rtIc.NumField(); i++ {
rtField := rtIc.Field(i)
if rtField.Name != controllerParentName {
fieldTag := rtField.Tag
if funcName, ok := fieldTag.Lookup(MappingHandler); ok {
var rvFun reflect.Value
if fun, ok2 := rtIc.MethodByName(funcName); ok2 {
if strutil.IsNotBlank(fun.PkgPath) {
msg := fmt.Sprintf("[%s]方法[%s]不可导出", rtIc.Name(), funcName)
panic(msg)
}
rvFun = rvIc.MethodByName(funcName)
} else {
rvIcPtr := rvIc.Addr()
fun, ok3 := rvIcPtr.Type().MethodByName(funcName)
if ok3 {
msg := fmt.Sprintf("[%s]找不到方法[%s]", rtIc.Name(), funcName)
panic(msg)
}
if strutil.IsNotBlank(fun.PkgPath) {
msg := fmt.Sprintf("[%s]方法[%s]不可导出", rtIc.Name(), funcName)
panic(msg)
}
rvFun = rvIcPtr.MethodByName(funcName)
}
mapping, pNames := resolveFieldTag(fieldTag)
mapping.fun = resolveFun(funcName, rvFun, pNames)
mappings = append(mappings, mapping)
}
}
}
resolveControllerTag(rtIc, rvIc, mappings)
}
return mappings
}
func resolveControllerTag(rtIc reflect.Type, rvIc reflect.Value, mappings []*handlerMapping) {
if rtParent, ok := rtIc.FieldByName(controllerParentName); ok {
parentTag := rtParent.Tag
if path := parentTag.Get(Path); strutil.IsNotBlank(path) {
rvParent := rvIc.FieldByName(controllerParentName)
rvPath := rvParent.FieldByName(Path)
if rvPath.CanSet() {
rvPath.SetString(path)
}
}
// 解析权限tag
if permissionVal, ok := parentTag.Lookup(Permission); ok {
permissionList := strutil.SpitNotLetterAndNumber(permissionVal)
// 遍历每个handlerMapping
for _, mapping := range mappings {
permissions := mapping.permissions
// 解析出来的权限值逐个加入到mapping中
for _, ele := range permissionList {
if !slice.Contains(permissions, permissionVal) {
permissions = append(permissions, ele)
}
}
// 添加完之后放入结构体中
mapping.permissions = permissions
}
}
}
}
func resolveFieldTag(fieldTag reflect.StructTag) (mapping *handlerMapping, pNames []string) {
mapping = new(handlerMapping)
existTag := func(requestMethod string) bool {
_, ok := fieldTag.Lookup(requestMethod)
return ok
}
// 解析http请求类型和url
var method, url string
switch {
case existTag(GetMapping):
method = http.MethodGet
url = fieldTag.Get(GetMapping)
case existTag(PostMapping):
method = http.MethodPost
url = fieldTag.Get(PostMapping)
case existTag(PutMapping):
method = http.MethodPut
url = fieldTag.Get(PutMapping)
case existTag(DeleteMapping):
method = http.MethodDelete
url = fieldTag.Get(DeleteMapping)
default:
method = http.MethodGet
}
mapping.method = method
mapping.url = url
// 解析请求参数名称(RequestParam)
if pNameStr, ok := fieldTag.Lookup(RequestParam); ok {
pNames = strutil.SpitNotLetterAndNumber(pNameStr)
}
// 解析权限tag(Permission)
if permissionVal, ok := fieldTag.Lookup(Permission); ok {
mapping.permissions = strutil.SpitNotLetterAndNumber(permissionVal)
}
return
}
// 解析方法结构体类型入参的tag
func resolveFunInParamsTag(rtStruct reflect.Type, fieldName string, defaultValMap map[string]string) {
// 解析默认值tag(default)
for i := 0; i < rtStruct.NumField(); i++ {
field := rtStruct.Field(i)
if strutil.IsNotBlank(field.PkgPath) {
// 字段不可导出
continue
}
var key string
if strutil.IsNotBlank(fieldName) {
key = fieldName + "." + field.Name
} else {
key = field.Name
}
rtField := field.Type
if rtField.Kind() == reflect.Ptr {
rtField = rtField.Elem()
}
if rtField.Kind() == reflect.Struct {
resolveFunInParamsTag(rtField, field.Name, defaultValMap)
}
if defaultVal, ok := field.Tag.Lookup(Default); ok {
defaultValMap[key] = defaultVal
}
}
}
func resolveFun(funName string, rvFun reflect.Value, pNames []string) handlerFun {
rtFun := rvFun.Type()
outNum := rtFun.NumOut()
if outNum > 1 {
msg := fmt.Sprintf("方法[%s]的返回结果个数不能大于1", funName)
panic(errors.New(msg))
}
inNum := rtFun.NumIn()
if len(pNames) > inNum {
msg := fmt.Sprintf("方法[%s]的参数名个数大于参数列表个数", funName)
panic(errors.New(msg))
}
// 利用管道FIFO(先进先出)的特性,将参数名称放入管道,方便后面获取
pNameChan := make(chan string, 1)
go putParamName2Chan(pNameChan, pNames)
funParams := make([]handlerFunParam, 0)
for i := 0; i < inNum; i++ {
var funParam handlerFunParam
rtParam := rtFun.In(i)
// 该方法定义参数是否为指针类型
defineParamIsPtr := rtParam.Kind() == reflect.Ptr
if defineParamIsPtr {
rtParam = rtParam.Elem()
}
funParam.defineParamIsPtr = defineParamIsPtr
// 类型分别判断
defineParamIsCtx := false
switch rtParam.Kind() {
case reflect.Struct:
if rtParam.Name() == getTypeName(new(gin.Context)) {
defineParamIsCtx = true
break
}
// 如果参数为结构体,解析默认值tag,获取所有字段默认值
defaultValMap := make(map[string]string)
resolveFunInParamsTag(rtParam, "", defaultValMap)
funParam.defaultValMap = defaultValMap
case reflect.Map:
if defineParamIsPtr {
msg := fmt.Sprintf("方法[%s]map类型的入参不能声明为指针", funName)
panic(errors.New(msg))
}
if rtParam.Key().Kind() != reflect.String {
msg := fmt.Sprintf("方法[%s]的map入参'key'类型须定义为'string'", funName)
panic(errors.New(msg))
}
// 判断map类型的value类型
if rtParam.Elem().Kind() != reflect.String && rtParam.Elem().Kind() != reflect.Slice {
msg := fmt.Sprintf("方法[%s]的map入参'value'类型须定义为'string/[]string'", funName)
panic(errors.New(msg))
}
case reflect.Slice:
if defineParamIsPtr {
msg := fmt.Sprintf("方法[%s]slice类型的入参不能声明为指针", funName)
panic(errors.New(msg))
}
if rtParam.Elem().Kind() == reflect.Struct ||
rtParam.Elem().Kind() == reflect.Map ||
rtParam.Elem().Kind() == reflect.Ptr {
panic(errors.New("切片元素数据类型请定义为基本数据类型"))
}
funParam.pName = <-pNameChan
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
funParam.pName = <-pNameChan
default:
msg := fmt.Sprintf("方法[%s]入参无法解析,建议定义为 struct/map[string]string/map[string][]string", funName)
panic(msg)
}
funParam.defineParamIsCtx = defineParamIsCtx
funParam.rtParam = rtParam
funParams = append(funParams, funParam)
}
return handlerFun{rvFun, funParams}
}
func putParamName2Chan(pNameChan chan<- string, pNames []string) {
defer close(pNameChan)
for _, pName := range pNames {
pNameChan <- pName
}
}
Go
1
https://gitee.com/hongzhaomin/ginplus.git
git@gitee.com:hongzhaomin/ginplus.git
hongzhaomin
ginplus
ginplus
v1.0.2

搜索帮助