package api import ( "context" "fmt" "log/slog" "net/http" "reflect" "regexp" "strings" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" echoSwagger "github.com/swaggo/echo-swagger" _ "git.zhouxhere.com/zhouxhere/maptile/docs" "git.zhouxhere.com/zhouxhere/maptile/store" ) type API struct { *echo.Echo store *store.Store } func NewAPI(e *echo.Echo, s *store.Store) { api := &API{e, s} api.Validator = NewCustomValidator() api.Use(middleware.Recover()) api.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{LogStatus: true, LogURI: true, LogError: true, HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { if v.Error != nil { slog.LogAttrs(context.Background(), slog.LevelError, "REQUEST", slog.String("method", v.Method), slog.String("uri", v.URI), slog.Int("status", v.Status), slog.String("latency", v.Latency.String()), slog.String("error", v.Error.Error())) } else { slog.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST", slog.String("method", v.Method), slog.String("uri", v.URI), slog.Int("status", v.Status), slog.String("latency", v.Latency.String())) } return nil }, })) api.HTTPErrorHandler = func(err error, c echo.Context) { req := c.Request() slog.LogAttrs(context.Background(), slog.LevelError, "REQUEST", slog.String("method", req.Method), slog.String("uri", req.RequestURI), slog.String("error", err.Error())) c.JSON(http.StatusOK, Response[interface{}]{ Code: http.StatusInternalServerError, Message: err.Error(), Data: nil, }) } api.Use(middleware.CORSWithConfig(middleware.CORSConfig{ AllowOrigins: []string{"*"}, AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete}, // AllowHeaders: []string{"Content-Type", "Authorization"}, })) // swagger api.GET("/swagger/*", echoSwagger.WrapHandler) api.GET("/swagger", func(c echo.Context) error { return c.Redirect(http.StatusMovedPermanently, "/swagger/index.html") }) api.Register() for _, route := range api.Routes() { fmt.Println(route.Method, route.Path) } } func (a *API) Register() { v := reflect.ValueOf(a) t := reflect.TypeOf(a) group := a.Group("/api/v1") for i := range v.NumMethod() { method := v.Method(i) methodType := t.Method(i) methodName := methodType.Name if method.Type().String() != "func(echo.Context) error" { continue } // 使用正则表达式匹配方法名 re := regexp.MustCompile(`^(Post|Get|Put|Delete)(\w+)$`) matches := re.FindStringSubmatch(methodName) if len(matches) == 0 { continue } action := matches[1] otherParams := strings.Split(matches[2], "By") routeStr := strings.ToLower(otherParams[0]) route := "/" + routeStr otherStr := strings.Join(otherParams[1:], "") otherConditions := strings.Split(otherStr, "And") if len(otherConditions) > 0 { for i := range otherConditions { if otherConditions[i] != "" { route = route + "/:" + strings.ToLower(otherConditions[i]) } } } switch action { case "Post": group.POST(route, method.Interface().(func(echo.Context) error)) case "Get": group.GET(route, method.Interface().(func(echo.Context) error)) case "Put": group.PUT(route, method.Interface().(func(echo.Context) error)) case "Delete": group.DELETE(route, method.Interface().(func(echo.Context) error)) } } }