package api import ( "errors" "net/http" "net/url" "reflect" "regexp" "strconv" "strings" "git.sxidc.com/go-framework/baize/framework/core/infrastructure/logger" "git.sxidc.com/go-tools/utils/strutils" "github.com/gin-gonic/gin" ) // Handler 请求处理函数 type Handler func(c *Context) // Router 路由接口 type Router interface { // AddMiddlewares 添加中间件 AddMiddlewares(middlewares ...Handler) Router // RegisterVersionedRouter 注册版本路由 RegisterVersionedRouter(version string, middlewares ...Handler) Router // VersionedRouter 获取版本路由 VersionedRouter(version string) Router // AddRoute 添加路由路径 AddRoute(method string, relativePath string, handlers ...Handler) Router // Static 静态路由(指向目录) Static(relativePath string, root string) // StaticFile 静态路由(指向文件) StaticFile(relativePath string, filepath string) // BasePath 路由的基础路径,即去掉http://ip:port之后的URL部分(不包括AddRoute的RelativePath) BasePath() string // DumpPermissionItems 获取权限条目 DumpPermissionItems() []PermissionItem } type PermissionItem struct { Group string `json:"group"` Name string `json:"name"` Description string `json:"description"` Resource string `json:"resource"` Action string `json:"action"` NeedCheckExpire bool `json:"needCheckExpire"` SensitiveWordScene int `json:"sensitiveWordScene"` } func (p *PermissionItem) check() error { if strutils.IsStringEmpty(p.Name) { return errors.New("没有权限条目名称") } if strutils.IsStringEmpty(p.Resource) { return errors.New("没有权限条目resource") } if strutils.IsStringEmpty(p.Action) { return errors.New("没有权限条目action") } return nil } type RootRouter struct { engine *gin.Engine versioned map[string]*PrefixRouter // 由于gin的router的保证,不会有重复的条目 permissionItems []PermissionItem } func newRootRouter(engine *gin.Engine) *RootRouter { return &RootRouter{ engine: engine, versioned: make(map[string]*PrefixRouter), } } func (r *RootRouter) AddMiddlewares(middlewares ...Handler) Router { r.engine.Use(transferHandlers(middlewares...)...) return r } func (r *RootRouter) RegisterVersionedRouter(version string, middlewares ...Handler) Router { versioned := r.engine.Group(version, transferHandlers(middlewares...)...) r.versioned[version] = newPrefixRouter(versioned) return r.versioned[version] } func (r *RootRouter) Static(relativePath string, root string) { r.engine.Static(relativePath, root) } func (r *RootRouter) StaticFile(relativePath string, filepath string) { r.engine.StaticFile(relativePath, filepath) } func (r *RootRouter) BasePath() string { return r.engine.BasePath() } func (r *RootRouter) VersionedRouter(version string) Router { return r.versioned[version] } func (r *RootRouter) DumpPermissionItems() []PermissionItem { return r.permissionItems } func (r *RootRouter) AddRoute(method string, relativePathPattern string, handlers ...Handler) Router { relativePath, permissionItem, err := parseRelativePathPerm(r.engine.BasePath(), relativePathPattern, method) if err != nil { panic(err) } r.engine.Handle(method, relativePath, transferHandlers(handlers...)...) if permissionItem != nil { r.permissionItems = append(r.permissionItems, *permissionItem) } return r } func (r *RootRouter) ServerHttp(w http.ResponseWriter, req *http.Request) { r.engine.ServeHTTP(w, req) } type PrefixRouter struct { groupRouter *gin.RouterGroup versioned map[string]*PrefixRouter // 由于gin的router的保证,不会有重复的条目 permissionItems []PermissionItem } func newPrefixRouter(group *gin.RouterGroup) *PrefixRouter { return &PrefixRouter{ groupRouter: group, versioned: make(map[string]*PrefixRouter), } } func (r *PrefixRouter) AddMiddlewares(middlewares ...Handler) Router { r.groupRouter.Use(transferHandlers(middlewares...)...) return r } func (r *PrefixRouter) RegisterVersionedRouter(version string, middlewares ...Handler) Router { ginMiddlewares := make([]gin.HandlerFunc, 0) for _, m := range middlewares { innerM := m ginMiddlewares = append(ginMiddlewares, func(c *gin.Context) { innerM(&Context{Context: c}) }) } versioned := r.groupRouter.Group(version, ginMiddlewares...) r.versioned[version] = newPrefixRouter(versioned) return r.versioned[version] } func (r *PrefixRouter) VersionedRouter(version string) Router { return r.versioned[version] } func (r *PrefixRouter) AddRoute(method string, relativePathPattern string, handlers ...Handler) Router { relativePath, permissionItem, err := parseRelativePathPerm(r.groupRouter.BasePath(), relativePathPattern, method) if err != nil { panic(err) } r.groupRouter.Handle(method, relativePath, transferHandlers(handlers...)...) if permissionItem != nil { r.permissionItems = append(r.permissionItems, *permissionItem) } return r } func (r *PrefixRouter) Static(relativePath string, root string) { r.groupRouter.Static(relativePath, root) } func (r *PrefixRouter) StaticFile(relativePath string, filepath string) { r.groupRouter.StaticFile(relativePath, filepath) } func (r *PrefixRouter) BasePath() string { return r.groupRouter.BasePath() } func (r *PrefixRouter) DumpPermissionItems() []PermissionItem { return r.permissionItems } func transferHandlers(handlers ...Handler) []gin.HandlerFunc { ginHandlers := make([]gin.HandlerFunc, 0) for _, handler := range handlers { innerHandler := handler ginHandlers = append(ginHandlers, func(c *gin.Context) { innerHandler(&Context{Context: c}) }) } return ginHandlers } const ( tagPartSeparator = ";" tagPartKeyValueSeparator = ":" ) const ( permTagKey = "perm" permTagPartGroup = "group" permTagPartName = "name" permTagPartDescription = "description" permTagPartNeedCheckExpire = "needCheckExpire" permTagPartSensitiveWordScene = "sensitiveWordScene" ) func parseRelativePathPerm(basePath string, relativePathPattern string, method string) (string, *PermissionItem, error) { re := regexp.MustCompile(`\$\{(.+)\}\$`) matches := re.FindStringSubmatch(relativePathPattern) if len(matches) == 0 { return relativePathPattern, nil, nil } relativePath := re.ReplaceAllString(relativePathPattern, "") permTag, ok := reflect.StructTag(matches[1]).Lookup(permTagKey) if !ok { return relativePath, nil, nil } tagParts := strings.Split(permTag, tagPartSeparator) if tagParts == nil || len(tagParts) == 0 { return relativePath, nil, nil } fullPath, err := url.JoinPath(basePath, relativePath) if err != nil { return "", nil, err } permissionItem := &PermissionItem{ Group: "", Name: "", Description: "", Resource: fullPath, Action: method, NeedCheckExpire: false, SensitiveWordScene: 0, } for _, tagPart := range tagParts { tagPartKeyValue := strings.SplitN(strings.TrimSpace(tagPart), tagPartKeyValueSeparator, 2) if strutils.IsStringEmpty(tagPartKeyValue[0]) { continue } switch tagPartKeyValue[0] { case permTagPartGroup: permissionItem.Group = tagPartKeyValue[1] case permTagPartName: permissionItem.Name = tagPartKeyValue[1] case permTagPartDescription: permissionItem.Description = tagPartKeyValue[1] case permTagPartNeedCheckExpire: permissionItem.NeedCheckExpire = true case permTagPartSensitiveWordScene: scene, err := strconv.Atoi(tagPartKeyValue[1]) if err != nil { return relativePath, nil, err } if scene < 1 || scene > 4 { return relativePath, nil, errors.New("不支持的敏感词场景: " + tagPartKeyValue[1]) } permissionItem.SensitiveWordScene = scene default: err := errors.New(permTagKey + "不支持的tag: " + tagPartKeyValue[0]) logger.GetInstance().Error(err) continue } } err = permissionItem.check() if err != nil { return "", nil, err } return relativePath, permissionItem, nil }