diff --git a/backend/plugins/drivers/driver_http/config.go b/backend/plugins/drivers/driver_http/config.go index df2046c1..af27dd07 100644 --- a/backend/plugins/drivers/driver_http/config.go +++ b/backend/plugins/drivers/driver_http/config.go @@ -15,6 +15,7 @@ type httpAppConfig struct { SessionAge int `config:"session_age" env:"APP_SESSION_AGE" default:"86400"` SessionHTTPOnly bool `config:"session_http_only" env:"APP_SESSION_HTTP_ONLY" default:"true"` SessionSecure bool `config:"session_secure" env:"APP_SESSION_SECURE"` + RedirectTrailingSlash *bool `config:"redirect_trailing_slash" env:"APP_REDIRECT_TRAILING_SLASH"` } type httpRedisConfig struct { diff --git a/backend/plugins/drivers/driver_http/engine.go b/backend/plugins/drivers/driver_http/engine.go index 2ca969a1..7e538f65 100644 --- a/backend/plugins/drivers/driver_http/engine.go +++ b/backend/plugins/drivers/driver_http/engine.go @@ -31,6 +31,11 @@ func BuildEngineWithConfig(appCfg httpAppConfig, redisCfg httpRedisConfig) (*gin // 初始化路由 r := gin.New() + redirect := true + if appCfg.RedirectTrailingSlash != nil { + redirect = *appCfg.RedirectTrailingSlash + } + r.RedirectTrailingSlash = redirect r.Use(gin.Recovery()) r.Use(corsMiddleware()) diff --git a/backend/plugins/drivers/driver_http/engine_slash_test.go b/backend/plugins/drivers/driver_http/engine_slash_test.go new file mode 100644 index 00000000..83c7e111 --- /dev/null +++ b/backend/plugins/drivers/driver_http/engine_slash_test.go @@ -0,0 +1,28 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package driver_http + +import "testing" + +func TestBuildEngineDefaultRedirectsTrailingSlash(t *testing.T) { + eng, err := BuildEngineWithConfig(httpAppConfig{}, httpRedisConfig{}) + if err != nil { + t.Fatal(err) + } + if !eng.RedirectTrailingSlash { + t.Fatal("default RedirectTrailingSlash must be true") + } +} + +func TestBuildEngineCanDisableRedirectTrailingSlash(t *testing.T) { + eng, err := BuildEngineWithConfig(httpAppConfig{RedirectTrailingSlash: boolPtr(false)}, httpRedisConfig{}) + if err != nil { + t.Fatal(err) + } + if eng.RedirectTrailingSlash { + t.Fatal("RedirectTrailingSlash must honor false") + } +} + +func boolPtr(v bool) *bool { return &v }