首页 > 解决方案 > 如何从除 Gin 中的一个路由之外的所有路由提供静态文件?

问题描述

正如标题所说,考虑一个 Gin 路由器,我想在其中提供来自除一个之外的所有路由的静态文件。假设这条路线是/api. 第一次尝试可能如下所示:

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.StaticFS("/", gin.Dir("static", false))
    r.GET("/api/v1/foo", func(c *gin.Context) { c.JSON(200, gin.H{"foo": true}) })

    r.Run(":9955")
}

引擎盖下的RouterGroup.StaticFSStatic也是)使用通配符路径参数连接相对路径:path.Join(relativePath, "/*filepath")。什么时候relativePath是根路径/,它会恐慌:

恐慌:新路径“/api/v1/foo”中的“/api/v1/foo”与现有前缀“/*filepath”中的现有通配符“/*filepath”冲突

这是由于 Gin 的 http 路由器实现:路由匹配路径 prefix,因此 root 上的通配符将与所有其他路由冲突。可以在此处找到有关此行为的更多详细信息——这是提出此问题的地方。

另一种可能的解决方案是为静态文件路由添加前缀,这样它就不会与/api

r.StaticFS("/static", gin.Dir("static", false))

但这也不允许我从根目录提供资产。如何在根目录上使用通配符或等效项,并且仍然匹配一个特定路径?

标签: gogo-gin

解决方案


一种解决方案是在 root 上仅设置一个通配符路径参数,并使用顶级处理程序来检查路径。根据路径前缀,您将上下文重新输入到第二个路由器中Engine.HandleContext

func main() {
    apiEngine := gin.New()
    apiG := apiEngine.Group("/api")
    {
        apiG.GET("/foo", func(c *gin.Context) { c.JSON(200, gin.H{"foo": true})})
        apiG.GET("/bar", func(c *gin.Context) { c.JSON(200, gin.H{"bar": true})})
    }

    r := gin.New()
    r.GET("/*any", func(c *gin.Context) {
        path := c.Param("any")
        if strings.HasPrefix(path, "/api") {
            apiEngine.HandleContext(c)
        } else {
            assetHandler(c)
        }
    })
    r.Run(":9955")
}

在此示例中,辅助引擎apiEngine仅用于以通常的方式声明路由,例如组和方法处理程序。你不运行它。相反,您HandleContext在检查主通配符处理程序上的路径前缀后将 Gin 上下文传递给它。

在此示例中,所有方法都是GET. 资产通常由 GET 提供,但如果您需要在辅助引擎中处理更多 http 方法,请将主处理程序声明为Any. 就像是:

    r := gin.New()
    // allows any http method
    r.Any("/*any", func(c *gin.Context) {
        path := c.Param("any")
        if strings.HasPrefix(path, "/api") {
            apiEngine.HandleContext(c) // on this you can declare POST, PUT, etc. 

        // sanity check
        } else if c.Request.Method == http.MethodGet {
            assetHandler(c)
        }
    })
    r.Run(":9955")

推荐阅读