diff --git a/context.go b/context.go index 0620c41..5988d4f 100644 --- a/context.go +++ b/context.go @@ -570,11 +570,6 @@ func (c *Context) SetHeaders(headers map[string][]string) { } } -// 获取所有resp Headers -func (c *Context) GetAllRespHeader() http.Header { - return c.Writer.Header() -} - // GetAllReqHeader 获取所有请求头部 func (c *Context) GetAllReqHeader() http.Header { return c.Request.Header diff --git a/engine.go b/engine.go index ff47fc3..8742d81 100644 --- a/engine.go +++ b/engine.go @@ -560,8 +560,7 @@ func (engine *Engine) Use(middleware ...HandlerFunc) IRouter { // Handle 注册通用 HTTP 方法的路由 // 这是所有具体 HTTP 方法注册的基础方法 func (engine *Engine) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) { - //absolutePath := path.Join("/", relativePath) // 修正:统一使用 path.Join 进行路径拼接 - absolutePath := resolveRoutePath("/", relativePath) + absolutePath := path.Join("/", relativePath) // 修正:统一使用 path.Join 进行路径拼接 // 修正:将全局中间件与此路由的处理函数合并 fullHandlers := engine.combineHandlers(engine.globalHandlers, handlers) engine.addRoute(httpMethod, absolutePath, "/", fullHandlers) @@ -623,7 +622,7 @@ func (engine *Engine) GetRouterInfo() []RouteInfo { func (engine *Engine) Group(relativePath string, handlers ...HandlerFunc) IRouter { return &RouterGroup{ Handlers: engine.combineHandlers(engine.globalHandlers, handlers), // 继承全局中间件 - basePath: resolveRoutePath("/", relativePath), + basePath: path.Join("/", relativePath), engine: engine, // 指向 Engine 实例 } } @@ -646,7 +645,7 @@ func (group *RouterGroup) Use(middleware ...HandlerFunc) IRouter { // Handle 注册通用 HTTP 方法的路由到当前组 // 路径是相对于当前组的 basePath func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) { - absolutePath := resolveRoutePath(group.basePath, relativePath) + absolutePath := path.Join(group.basePath, relativePath) fullHandlers := group.engine.combineHandlers(group.Handlers, handlers) group.engine.addRoute(httpMethod, absolutePath, group.basePath, fullHandlers) } @@ -687,7 +686,7 @@ func (group *RouterGroup) ANY(relativePath string, handlers ...HandlerFunc) { func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) IRouter { return &RouterGroup{ Handlers: group.engine.combineHandlers(group.Handlers, handlers), - basePath: resolveRoutePath(group.basePath, relativePath), + basePath: path.Join(group.basePath, relativePath), engine: group.engine, // 指向 Engine 实例 } } diff --git a/midware_x.go b/midware_x.go deleted file mode 100644 index 645fb1d..0000000 --- a/midware_x.go +++ /dev/null @@ -1,89 +0,0 @@ -package touka - -// UseChainIf 是一个条件中间件包装器。 -// 如果 `condition` 为 true,它将执行提供的 `middlewares` 链。 -// 如果 `condition` 为 false,它会直接跳过这些中间件,调用下一个处理器。 -func (engine *Engine) UseChainIf(condition bool, middlewares ...HandlerFunc) HandlerFunc { - if !condition { - return func(c *Context) { - c.Next() - } - } - - // 如果条件为真,返回一个处理器,该处理器负责执行子链。 - // 我们通过修改 Context 的状态来做到这一点。 - // 这比递归闭包或替换 c.Next 要清晰。 - return func(c *Context) { - // 1. 将当前的处理链和索引位置保存下来。 - originalHandlers := c.handlers - originalIndex := c.index - - // 2. 创建一个新的处理链,它由传入的中间件和最后的 "恢复并继续" 处理器组成。 - subChain := make(HandlersChain, len(middlewares)+1) - copy(subChain, middlewares) - - // 3. 在子链的末尾添加一个特殊的 handler。 - // 它的作用是恢复原始的处理链状态,并继续执行原始链中 `If` 之后的处理器。 - subChain[len(middlewares)] = func(ctx *Context) { - ctx.handlers = originalHandlers - ctx.index = originalIndex - ctx.Next() - } - - // 4. 将 Context 的处理器链临时替换为我们的子链,并重置索引。 - c.handlers = subChain - c.index = -1 - - // 5. 调用 c.Next() 来启动子链的执行。 - c.Next() - } -} - -// UseIf 是一个条件中间件包装器 -func (engine *Engine) UseIf(condition bool, middlewares HandlerFunc) HandlerFunc { - if !condition { - return func(c *Context) { - c.Next() - } - } - - return middlewares -} - -func (engine *Engine) UseNewChainIf(condition bool, middlewares ...HandlerFunc) HandlerFunc { - // 如果条件不满足或没有提供中间件,返回一个“穿透”中间件。 - if !condition || len(middlewares) == 0 { - return func(c *Context) { - c.Next() - } - } - - // 如果条件满足,返回一个处理器,该处理器负责按顺序执行这个子链。 - return func(c *Context) { - // 保存原始的处理链和当前执行索引,以便在子链结束后恢复。 - originalHandlers := c.handlers - originalIndex := c.index - - // 创建一个新的临时处理链。 - // 这个链由传入的 `middlewares` 和一个特殊的“恢复”处理器组成。 - subChain := make(HandlersChain, len(middlewares)+1) - copy(subChain, middlewares) - - // 在子链的末尾添加“恢复”处理器。 - // 当所有 `middlewares` 都执行完毕并调用了 Next() 后,这个函数会被执行。 - subChain[len(middlewares)] = func(ctx *Context) { - // 恢复原始的处理链状态。 - ctx.handlers = originalHandlers - ctx.index = originalIndex - // 继续执行原始处理链中 `ChainIf` 之后的下一个处理器。 - ctx.Next() - } - - // 将 Context 的处理器链临时替换为我们的子链,并重置索引以从头开始。 - c.handlers = subChain - c.index = -1 - - // 调用 c.Next() 来启动这个子链的执行。 - c.Next() - } -} diff --git a/path.go b/path.go deleted file mode 100644 index 7a4e1e0..0000000 --- a/path.go +++ /dev/null @@ -1,49 +0,0 @@ -package touka - -import ( - "path" - "strings" -) - -// resolveRoutePath 安全地拼接基础路径和相对路径,并正确处理尾部斜杠。 -// 这是一个为高性能路由注册优化的版本。 -func resolveRoutePath(basePath, relativePath string) string { - // 如果相对路径为空,直接返回基础路径 - if relativePath == "" { - return basePath - } - // 如果基础路径为空,直接返回相对路径(确保以/开头) - if basePath == "" { - return relativePath - } - - // 使用 strings.Builder 来高效构建路径,避免多次字符串分配 - var b strings.Builder - // 估算一个合理的容量以减少扩容 - b.Grow(len(basePath) + len(relativePath) + 1) - b.WriteString(basePath) - - // 检查 basePath 是否以斜杠结尾 - if basePath[len(basePath)-1] != '/' { - b.WriteByte('/') // 如果没有,则添加 - } - - // 检查 relativePath 是否以斜杠开头,如果是,则移除 - if relativePath[0] == '/' { - b.WriteString(relativePath[1:]) - } else { - b.WriteString(relativePath) - } - - // path.Clean 仍然是处理 '..' 和 '//' 等复杂情况最可靠的方式。 - // 我们可以只在最终结果上调用一次,而不是在拼接过程中。 - finalPath := path.Clean(b.String()) - - // 关键:如果原始 relativePath 有尾部斜杠,但 Clean 把它移除了,我们要加回来。 - // 只有当最终路径不是根路径 "/" 时才需要加回。 - if strings.HasSuffix(relativePath, "/") && finalPath != "/" { - return finalPath + "/" - } - - return finalPath -} diff --git a/path_test.go b/path_test.go deleted file mode 100644 index 64ff185..0000000 --- a/path_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// touka/path_test.go -package touka - -import ( - "fmt" - "path" - "strings" - "testing" -) - -func TestResolveRoutePath(t *testing.T) { - // 定义一组测试用例 - testCases := []struct { - basePath string - relativePath string - expected string - }{ - // --- 基本情况 --- - {basePath: "/api", relativePath: "/v1", expected: "/api/v1"}, - {basePath: "/api/", relativePath: "v1", expected: "/api/v1"}, - {basePath: "/api", relativePath: "v1", expected: "/api/v1"}, - {basePath: "/api/", relativePath: "/v1", expected: "/api/v1"}, - - // --- 尾部斜杠处理 --- - {basePath: "/api", relativePath: "/v1/", expected: "/api/v1/"}, - {basePath: "/api/", relativePath: "v1/", expected: "/api/v1/"}, - {basePath: "", relativePath: "/v1/", expected: "/v1/"}, - {basePath: "/", relativePath: "/v1/", expected: "/v1/"}, - - // --- 根路径和空路径 --- - {basePath: "/", relativePath: "/", expected: "/"}, - {basePath: "/api", relativePath: "/", expected: "/api/"}, - {basePath: "/api/", relativePath: "/", expected: "/api/"}, - {basePath: "/", relativePath: "/users", expected: "/users"}, - {basePath: "/users", relativePath: "", expected: "/users"}, - {basePath: "", relativePath: "/users", expected: "/users"}, - - // --- 路径清理测试 (由 path.Clean 处理) --- - {basePath: "/api/v1", relativePath: "../v2", expected: "/api/v2"}, - {basePath: "/api/v1/", relativePath: "../v2/", expected: "/api/v2/"}, - {basePath: "/api//v1", relativePath: "/users", expected: "/api/v1/users"}, - {basePath: "/api/./v1", relativePath: "/users", expected: "/api/v1/users"}, - } - - for _, tc := range testCases { - // 使用 t.Run 为每个测试用例创建一个子测试,方便定位问题 - testName := fmt.Sprintf("base:'%s', rel:'%s'", tc.basePath, tc.relativePath) - t.Run(testName, func(t *testing.T) { - result := resolveRoutePath(tc.basePath, tc.relativePath) - if result != tc.expected { - t.Errorf("resolveRoutePath('%s', '%s') = '%s'; want '%s'", - tc.basePath, tc.relativePath, result, tc.expected) - } - }) - } -} - -// 性能基准测试,用于观测优化效果 -func BenchmarkResolveRoutePath(b *testing.B) { - basePath := "/api/v1/some/long/path" - relativePath := "/users/profile/details/" - - // b.N 是由 testing 包提供的循环次数 - for i := 0; i < b.N; i++ { - // 在循环内调用被测试的函数 - resolveRoutePath(basePath, relativePath) - } -} - -// (可选)可以保留旧的实现,进行性能对比 -func resolveRoutePath_Old(basePath, relativePath string) string { - if relativePath == "/" { - if basePath != "" && basePath != "/" && !strings.HasSuffix(basePath, "/") { - return basePath + "/" - } - return basePath - } - finalPath := path.Clean(basePath + "/" + relativePath) - if strings.HasSuffix(relativePath, "/") && !strings.HasSuffix(finalPath, "/") { - return finalPath + "/" - } - return finalPath -} - -func BenchmarkResolveRoutePath_Old(b *testing.B) { - basePath := "/api/v1/some/long/path" - relativePath := "/users/profile/details/" - for i := 0; i < b.N; i++ { - resolveRoutePath_Old(basePath, relativePath) - } -} - -func BenchmarkJoinStd(b *testing.B) { - basePath := "/api/v1/some/long/path" - relativePath := "/users/profile/details/" - for i := 0; i < b.N; i++ { - path.Join(basePath, relativePath) - } -}