1 Star 0 Fork 0

hongzhaomin / ginplus

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
ginplus.go 10.43 KB
一键复制 编辑 原始数据 按行查看 历史
package ginplus
import (
"encoding/json"
"errors"
"fmt"
"gitee.com/hongzhaomin/hzm-common-go/strutil"
"gitee.com/hongzhaomin/hzm-common-go/toolkit"
"github.com/gin-gonic/gin"
"net/http"
"reflect"
"strings"
)
const (
rootPath = "/" // 根路径
AccessToken = "accessToken" // 请求token变量名
RequireLogin = "RequireLogin" // Tag的value项,Permission的固定选项值
ginTagForm = "form"
)
// StartHttpServerDefault http服务启动入口
func StartHttpServerDefault() {
StartHttpServer(new(DefaultGinConfiguration))
}
func StartHttpServer(register IGinConfiguration) {
router := gin.Default()
group(router, register)
port := register.ServerPort()
if strutil.IsNotBlank(port) {
_ = router.Run(":" + port)
} else {
// listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
_ = router.Run()
}
}
func group(r *gin.Engine, register IGinConfiguration) {
// 主分组
root := r.Group(register.ContextPath())
nextGroup(root, register)
}
func nextGroup(root *gin.RouterGroup, register IGinConfiguration) {
mappingChains := resolveIcList(register)
url2Mapping = make(map[string]*handlerMapping)
for _, mappingChain := range mappingChains {
controller := mappingChain.controller
controllerPath := controller.RequestMapping()
group := root.Group(controllerPath)
mappings := mappingChain.mappings
for _, mapping := range mappings {
basePath := strutil.JoinPaths(rootPath, register.ContextPath())
fullPath := strutil.JoinPaths(strutil.JoinPaths(basePath, controllerPath), mapping.url)
putHandlerMapping(fullPath, mapping)
httpRegis(group, mapping)
}
}
}
func httpRegis(group *gin.RouterGroup, mapping *handlerMapping) {
url := mapping.url
method := mapping.method
switch method {
case http.MethodGet:
group.GET(url, mainHandlerFun)
case http.MethodPost:
group.POST(url, mainHandlerFun)
case http.MethodPut:
group.PUT(url, mainHandlerFun)
case http.MethodDelete:
group.DELETE(url, mainHandlerFun)
default:
fmt.Println("不支持的http请求")
}
}
func mainHandlerFun(ctx *gin.Context) {
defer func() {
var err = recover()
if err != nil {
fmt.Println("请求异常, err: ", err)
switch err.(type) {
case *bizError:
bizErr := err.(*bizError)
ctx.JSON(http.StatusOK, Fail2(bizErr.Code, bizErr.Msg))
case string:
ctx.JSON(http.StatusOK, Fail(err.(string)))
default:
ctx.JSON(http.StatusOK, Fail("服务开小差了"))
}
}
}()
mapping := getHandlerMapping(ctx.FullPath())
if mapping == nil {
ctx.JSON(http.StatusNotFound, nil)
return
}
// 调用处理器方法前的参数解析操作
rvParams, existCtx := invoke(mapping.fun, ctx)
// todo 可扩展处理器调用前逻辑,如需要对参数做改动,则要拆解 invoke 方法
if ok := invokePre(ctx, mapping.permissions); !ok {
return
}
data := doInvoke(mapping.fun.rvFun, rvParams)
// todo 可扩展处理器调用后逻辑
removeCurrentUserJson()
// 响应处理
response(data, existCtx, ctx, mapping.fun)
}
func invoke(fun handlerFun, ctx *gin.Context) ([]reflect.Value, bool) {
// 构造方法入参列表
existCtx := false
rvParams := make([]reflect.Value, 0)
for _, funParam := range fun.funParams {
// 判断是否为ctx类型参数
if funParam.defineParamIsCtx {
rvParams = append(rvParams, reflect.ValueOf(ctx))
existCtx = true
continue
}
// 创建实例
var rvParam reflect.Value
rtParam := funParam.rtParam
paramName := funParam.pName
defaultValMap := funParam.defaultValMap
switch rtParam.Kind() {
case reflect.Struct:
// 创建结构体实例,拿到指针
rvParam = reflect.New(rtParam)
// GET请求并且不存在form TAG
if ctx.Request.Method == http.MethodGet && toolkit.NotExistTag(rtParam, ginTagForm) {
// 构建新结构体,然后将数据绑定到新的结构体上
rtParamNew := copyStructFields(rtParam, "", defaultValMap)
rvParamNew := reflect.New(rtParamNew)
if err := ctx.ShouldBind(rvParamNew.Interface()); err != nil {
panic(errors.New("数据绑定失败"))
}
// 将新结构体转成json,再将json转成原始结构体
// 从而达到将数据绑定到原始结构体上的目的
toJson := toolkit.ToJson3(rvParamNew.Interface())
toolkit.ToObj(toJson, rvParam.Interface())
} else {
// 做数据绑定
if err := ctx.ShouldBind(rvParam.Interface()); err != nil {
// fixme 做个日志打印
panic(errors.New("数据绑定失败"))
}
setDefaultVal(rvParam.Elem(), "", defaultValMap)
}
// 统一转化为指针指向的值
rvParam = rvParam.Elem()
case reflect.Map:
// 创建 map 体实例
rvParam = reflect.MakeMap(rtParam)
// 做数据绑定
if err := ctx.ShouldBind(rvParam.Interface()); err != nil {
// fixme 做个日志打印
panic(errors.New("数据绑定失败"))
}
case reflect.Slice:
if strutil.IsBlank(paramName) {
panic(errors.New("无法解析参数名为空的切片类型参数"))
}
sliceStr := ctx.Query(paramName)
sliceVal := make([]string, 0)
err := json.Unmarshal([]byte(sliceStr), &sliceVal)
if err != nil {
// fixme 打印日志
sliceVal = strutil.SpitNotLetterAndNumber(sliceStr)
}
// 根据 rtParam 创建一个切片
rvParam = reflect.MakeSlice(rtParam, 0, len(sliceVal))
for _, ele := range sliceVal {
rvParam = reflect.Append(rvParam, basicConvert(rtParam.Elem(), ele).Elem())
}
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:
val := ctx.Query(paramName)
// 统一转化为指针指向的值
rvParam = basicConvert(rtParam, val).Elem()
default:
msg := fmt.Sprintf("不支持%s类型参数的解析", rtParam.Kind())
panic(msg)
}
// 方法入参填值
if funParam.defineParamIsPtr {
rvParams = append(rvParams, rvParam.Addr())
} else {
rvParams = append(rvParams, rvParam)
}
}
return rvParams, existCtx
}
func copyStructFields(rtStruct reflect.Type, fieldName string, defaultValMap map[string]string) reflect.Type {
fieldNum := rtStruct.NumField()
newStructFields := make([]reflect.StructField, 0, fieldNum)
for i := 0; i < fieldNum; 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 {
rtAnonymousField := copyStructFields(rtField, field.Name, defaultValMap)
field.Type = rtAnonymousField
} else {
tag := field.Tag
if _, ok := tag.Lookup(ginTagForm); !ok {
// `form:"form,default=1"`
sb := new(strings.Builder)
sb.WriteString(string(tag))
fieldNameHump := strutil.FirstLetter2Lower(field.Name)
if sb.Len() > 0 {
sb.WriteString(" ")
}
sb.WriteString(ginTagForm)
sb.WriteString(":\"")
sb.WriteString(fieldNameHump)
if strutil.IsNotBlank(defaultValMap[key]) {
sb.WriteString(",default=")
sb.WriteString(defaultValMap[key])
}
sb.WriteString("\"")
field.Tag = reflect.StructTag(sb.String())
}
}
newStructFields = append(newStructFields, field)
}
return reflect.StructOf(newStructFields)
}
func setDefaultVal(rvParam reflect.Value, fieldName string, defaultValMap map[string]string) {
rtParam := rvParam.Type()
for i := 0; i < rvParam.NumField(); i++ {
structField := rtParam.Field(i)
if strutil.IsNotBlank(structField.PkgPath) {
// 字段不可导出
continue
}
var key string
if strutil.IsNotBlank(fieldName) {
key = fieldName + "." + structField.Name
} else {
key = structField.Name
}
isPtr := false
rtField := structField.Type
if rtField.Kind() == reflect.Ptr {
rtField = rtField.Elem()
isPtr = true
}
rvField := rvParam.Field(i)
if rvField.IsZero() {
if rtField.Kind() == reflect.Struct {
rvFieldNew := reflect.New(rtField)
setDefaultVal(rvFieldNew.Elem(), structField.Name, defaultValMap)
if isPtr {
rvField.Set(rvFieldNew)
} else {
rvField.Set(rvFieldNew.Elem())
}
} else {
// 如果是零值,则赋默认值
if strutil.IsNotBlank(defaultValMap[key]) {
structFieldVal := basicConvert(rvField.Type(), defaultValMap[key])
if isPtr {
rvField.Set(structFieldVal)
} else {
rvField.Set(structFieldVal.Elem())
}
}
}
}
}
}
// 执行方法
func doInvoke(rvFun reflect.Value, rvParams []reflect.Value) any {
// 执行方法
results := rvFun.Call(rvParams)
if len(results) > 0 {
// 返回第一个响应的真实类型
// fixme 当方法返回值定义为指针类型时,返回结果为nil,此时的nil并非我们理解的nil,用 val == nil 为false
val := results[0]
if val.IsValid() {
// 判断返回是否为零值,如果是指针里面会判断是否为nil,不为零值则取真实值
if !val.IsZero() {
return val.Interface()
}
}
}
return nil
}
func response(data any, existCtx bool, ctx *gin.Context, fun handlerFun) {
rtFun := fun.rvFun.Type()
// 响应结果个数为0,也没有使用ctx参数
if rtFun.NumOut() == 0 && !existCtx {
ctx.JSON(http.StatusOK, Ok())
return
}
// 存在响应结果
if data != nil {
if SameAsStruct(rtFun.Out(0)) {
ctx.JSON(http.StatusOK, data)
return
}
// fixme 不同使用内置result
ctx.JSON(http.StatusOK, Ok2(data))
} else {
ctx.JSON(http.StatusOK, Ok())
}
}
func GetAccessToken(ctx *gin.Context) string {
// 先从路径中获取token
token := ctx.Query(AccessToken)
if strutil.IsBlank(token) {
// 未获取到,再从header中获取
token = ctx.GetHeader(AccessToken)
}
if strutil.IsBlank(token) {
// 还未获取到,最后从cookie中获取
if cookie, err := ctx.Cookie(AccessToken); err == nil {
// fixme 这个从cookie中获取的方式有待测试
token = cookie
}
}
return token
}
func invokePre(ctx *gin.Context, permissions []string) bool {
removeCurrentUserJson()
for _, permission := range permissions {
switch permission {
case RequireLogin:
token := GetAccessToken(ctx)
if strutil.IsBlank(token) {
ctx.JSON(http.StatusUnauthorized, nil)
return false
}
userJson := GetUserJsonByToken(token)
if strutil.IsBlank(userJson) {
ctx.JSON(http.StatusUnauthorized, nil)
return false
}
setCurrentUserJson(userJson)
return true
}
}
return true
}
Go
1
https://gitee.com/hongzhaomin/ginplus.git
git@gitee.com:hongzhaomin/ginplus.git
hongzhaomin
ginplus
ginplus
v1.0.2

搜索帮助