package api import ( "context" "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, }) } // 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 := 0; i < v.NumMethod(); i++ { method := v.Method(i) methodType := t.Method(i) methodName := methodType.Name // 使用正则表达式匹配方法名 re := regexp.MustCompile(`^(Post|Get|Put|Delete)([A-Za-z]+)(By[A-Za-z]+)?$`) matches := re.FindStringSubmatch(methodName) if len(matches) == 0 { continue } action := matches[1] route := strings.ToLower(matches[2]) if matches[3] != "" { param := strings.ToLower(matches[3][2:]) route = "/" + route + "/:" + param } else { route = "/" + route } 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)) } } }