// Package router ... // // Description : router ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2025-02-07 18:19 package router import ( "fmt" "net/http" "os" "reflect" "runtime" "strings" "sync" "git.zhangdeman.cn/zhangdeman/graceful" "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" apiDoc "git.zhangdeman.cn/zhangdeman/api-doc" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/gin/middleware" "git.zhangdeman.cn/zhangdeman/gin/middleware/request_cors" "github.com/gin-contrib/pprof" apiDocDefine "git.zhangdeman.cn/zhangdeman/api-doc/define" apiDocEnum "git.zhangdeman.cn/zhangdeman/api-doc/enums" "github.com/gin-gonic/gin" ) func newServerOption(port int, optionList ...SetServerOptionFunc) *serverOption { option := &serverOption{ swaggerUiTheme: apiDocEnum.SwaggerUIThemeRedocFree.String(), swaggerBaseUri: "/doc/swagger", globalMiddlewareList: nil, disableSwaggerDoc: false, serverInfo: &apiDocDefine.Info{ Description: "这是一个微服务,提供一些必要的的数据接口功能", Title: "微服务接口文档", TermsOfService: "", Contact: &apiDocDefine.Contact{ Name: "开发人员", Url: "", Email: "developer@example.com", }, License: &apiDocDefine.License{ Name: consts.LicenseApache20, Url: consts.LicenseUrlTable[consts.LicenseApache20], }, Version: "0.0.1", }, serverList: []*apiDocDefine.ServerItem{ { Url: fmt.Sprintf("http://127.0.0.1:%d", port), Description: "测试服务器", Variables: nil, }, }, } for _, opt := range optionList { if nil == opt { continue } opt(option) } return option } // NewServer server实例 func NewServer(port int, optionList ...SetServerOptionFunc) *server { if port < 80 { panic("port should be greater than 80") } gin.SetMode(gin.ReleaseMode) option := newServerOption(port, optionList...) s := &server{ router: gin.Default(), uiInstance: apiDoc.NewSwaggerUI(option.serverInfo, option.serverList, apiDocEnum.SwaggerUITheme(option.swaggerUiTheme)), port: port, option: option, lock: &sync.RWMutex{}, commonParam: map[string]GetCommonParam{}, consoleOutput: [][]any{}, globalMiddlewareDescList: []string{}, } globalMiddlewareList := s.getGlobalMiddlewareList(option) // 注册全局中间件 s.router.Use(globalMiddlewareList...) // 启用pprof, 注册相关路由 if option.enablePprof { pprof.Register(s.router) } return s } type server struct { router *gin.Engine port int uiInstance *apiDoc.SwaggerUI option *serverOption commonParam map[string]GetCommonParam // 结构体字段名, 注意, 不是TAG lock *sync.RWMutex consoleOutput [][]any // 控制台输出的提示信息 globalMiddlewareDescList []string // 全局中间件描述列表 } func (s *server) getGlobalMiddlewareList(option *serverOption) []gin.HandlerFunc { globalMiddlewareList := make([]gin.HandlerFunc, 0) if nil != option.initContextData { globalMiddlewareList = append(globalMiddlewareList, option.initContextData) // 初始化一些全局的变量 } if !option.disableInitRequest { // 启用了初始化请求 globalMiddlewareList = append(globalMiddlewareList, middleware.InitRequest()) // 初始化请求 } if nil != option.loggerCfg { // 请求日志记录中间件 globalMiddlewareList = append(globalMiddlewareList, middleware.LogRequest(option.loggerCfg)) } if option.enableCors { // 跨域中间件 globalMiddlewareList = append(globalMiddlewareList, request_cors.New(request_cors.Config{ AllowAllOrigins: true, AllowOrigins: nil, AllowOriginFunc: nil, AllowMethods: []string{"*"}, AllowHeaders: []string{"*"}, AllowCredentials: true, ExposeHeaders: nil, MaxAge: 0, AllowWildcard: true, AllowBrowserExtensions: true, AllowWebSockets: true, AllowFiles: true, })) } if len(option.globalMiddlewareList) > 0 { // 自定义全局中间件追加 globalMiddlewareList = append(globalMiddlewareList, option.globalMiddlewareList...) } s.globalMiddlewareDescList = s.getMiddlewareDescList(globalMiddlewareList) return globalMiddlewareList } // Start 启动服务 func (s *server) Start() { // 注册文档 s.uiInstance.RegisterHandler(s.router, s.option.swaggerBaseUri) gracefulServer := graceful.NewServer(fmt.Sprintf(":%d", s.port), s.Router()) defer func() { _ = gracefulServer.Close() }() s.TableOutput(nil, nil) // 控制台打印路由表格 if err := gracefulServer.ListenAndServe(); err != nil { if strings.Contains(err.Error(), "use of closed network connection") { fmt.Println("接收到退出指令, 服务平滑关闭") return } panic("服务启动监听失败" + err.Error()) } } // Router 对外访问路由实例, 不建议直接用 func (s *server) Router() *gin.Engine { return s.router } // Handler404 注册404的处理方法 func (s *server) Handler404(f gin.HandlerFunc) { s.router.NoRoute(f) } // SetCustomRouter 自定义路由处理 func (s *server) SetCustomRouter(f func(r *gin.Engine)) { if nil == f { return } f(s.router) } // Group 注册接口路由 func (s *server) Group(routerPrefix string, middlewareList []gin.HandlerFunc, controllerList ...any) { routerGroup := s.router.Group(routerPrefix) routerGroup.Use(middlewareList...) parser := controllerParser{} middlewareDescList := append([]string{}, s.globalMiddlewareDescList...) middlewareDescList = append(middlewareDescList, s.getMiddlewareDescList(middlewareList)...) for _, itemController := range controllerList { urlTable := parser.Parse(itemController) for _, itemUriCfg := range urlTable { // 设置代理请求函数描述 apiMiddlewareList := append([]string{}, middlewareDescList...) if itemUriCfg.IsSse { apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(reflect.ValueOf(s.SseHandler).Pointer()).Name()) } else { apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(reflect.ValueOf(s.RequestHandler).Pointer()).Name()) } // 设置 logic 函数描述 apiMiddlewareList = append(apiMiddlewareList, runtime.FuncForPC(itemUriCfg.ApiLogicFunc.Func.Pointer()).Name()) _ = s.uiInstance.DocInstance().AddApiFromInAndOut(routerPrefix, itemUriCfg.FormDataType, itemUriCfg.ResultDataType) // 普通 HTTP 请求 handleFunc := s.RequestHandler(itemUriCfg) if itemUriCfg.IsSse { // SSE 连接 handleFunc = s.SseHandler(itemUriCfg) } // 一个接口支持注册多种请求方法 for _, method := range itemUriCfg.RequestMethod { if len(routerPrefix) > 0 { routerPrefix = "/" + strings.TrimSuffix(strings.TrimPrefix(routerPrefix, "/"), "/") } s.consoleOutput = append(s.consoleOutput, []any{ routerPrefix + "/" + strings.TrimPrefix(itemUriCfg.Path, "/"), strings.ToUpper(method), strings.Join(apiMiddlewareList, "\n"), }) s.registerRouter(routerGroup, method, itemUriCfg, handleFunc) } } } } // registerRouter 注册路由 func (s *server) registerRouter(routerGroup *gin.RouterGroup, method string, itemUriCfg UriConfig, handleFunc gin.HandlerFunc) { switch method { case http.MethodGet: routerGroup.GET(itemUriCfg.Path, handleFunc) case http.MethodHead: routerGroup.HEAD(itemUriCfg.Path, handleFunc) case http.MethodPost: routerGroup.POST(itemUriCfg.Path, handleFunc) case http.MethodPut: routerGroup.PUT(itemUriCfg.Path, handleFunc) case http.MethodPatch: routerGroup.PATCH(itemUriCfg.Path, handleFunc) case http.MethodDelete: routerGroup.DELETE(itemUriCfg.Path, handleFunc) case http.MethodOptions: routerGroup.OPTIONS(itemUriCfg.Path, handleFunc) case http.MethodTrace: panic(`method Trace is not supported`) default: panic("method " + method + " is not support") } } // getMiddlewareDescList 获取中间件路径的描述列表 func (s *server) getMiddlewareDescList(middlewareList []gin.HandlerFunc) []string { middlewareDescList := []string{} for _, itemMiddleware := range middlewareList { middlewareValue := reflect.ValueOf(itemMiddleware) // 获取函数的指针 funcPointer := middlewareValue.Pointer() // 通过 runtime 获取函数信息 funcInfo := runtime.FuncForPC(funcPointer) // 获取完整的函数名称(包含包路径) fullName := funcInfo.Name() middlewareDescList = append(middlewareDescList, fullName) } return middlewareDescList } // TableOutput 表格输出 func (s *server) TableOutput(header []any, footer []any) { // 帮助函数 anySlice2TableRow := func(input []any) table.Row { tableRowData := make(table.Row, 0, len(input)) for _, d := range input { tableRowData = append(tableRowData, d) } return tableRowData } // 实例化一个表格 t := table.NewWriter() t.SetColumnConfigs([]table.ColumnConfig{ { Number: 1, AutoMerge: false, VAlign: text.VAlignMiddle, }, { Number: 2, AutoMerge: false, VAlign: text.VAlignMiddle, }, }) // 标准输出 t.SetOutputMirror(os.Stdout) if len(header) > 0 { t.AppendHeader(anySlice2TableRow(header)) } for _, row := range s.consoleOutput { t.AppendRow(anySlice2TableRow(row)) t.AppendSeparator() } if len(footer) > 0 { t.AppendFooter(anySlice2TableRow(footer)) } t.Render() }