转载地址
https://blog.cugxuan.cn/2020/05/07/Go/gin-serve-staticfs-in-root/
最近在写一个自己的小项目 GONEList,技术栈是 gin+vue
。准备在部署的时候,如何使用 gin 来同时部署前后端?
把前端使用 /
进行挂载,后端监听其他的路由,这样会遇到问题,默认的路由规则不允许同时挂载到 staticfs 到根和监听其他路由
在 google 之后 issue 里面找到 gin 的一个库 gin-contrib/static 来解决,直接调用即可
背景
前端是 Sillywa 使用 vue+iView
写的 gonelist-web,由于 Golang 的跨平台特性,在发布的时候想要尽量减少部署的难度,于是想用 gin 来同时路由前端生成的 dist
文件夹。
根据官方文档上的 router.StaticFS()
来进行的话会遇到报错
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.StaticFS("/", http.Dir("my_file_system"))
router.Run(":8080")
}
static-middleware
由于 gin 本身的路由会发生冲突,gin-contrib/static 使用中间件的方式判断是否存在该静态文件,使用起来非常简单,下面是例子
package main
import (
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.Use(static.Serve("/", static.LocalFile("./dist/", false)))
router.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
router.Run(":8080")
}
fileserver 是一个 Handler,判断 fs 中是否有请求的文件,如果有则使用 fileserver 将文件写入到 gin 的 writer 里面返回,否则就不处理,中间件处理结束之后就会去匹配对应的 api。戳我跳转代码
func Serve(urlPrefix string, fs ServeFileSystem) gin.HandlerFunc {
fileserver := http.FileServer(fs)
if urlPrefix != "" {
fileserver = http.StripPrefix(urlPrefix, fileserver)
}
return func(c *gin.Context) {
if fs.Exists(urlPrefix, c.Request.URL.Path) {
fileserver.ServeHTTP(c.Writer, c.Request)
c.Abort()
}
}
}
使用拓展
上面讲到的是对于挂载静态文件的解决方法,主要处理的问题是 gin 默认使用 httprouter 不支持路由的优先级
通过使用中间件来判断不同的 URL 应该走什么样的逻辑,如果你的某条冲突的路由是需要执行自己的某个 api,可以自己写一个中间件来解决问题
假设现在有一个 appV1 是一个 Group,路由为 /apps
,在 AppV1 下有两个 GET 接口(注意 GET 和 POST 是不会冲突的)
/statistics
这个是自己逻辑
/:appid/users
获取某个 appid 的 users
此时如果直接这么写就会产生冲突
appV1 := r.Group("/apps")
appV1.GET("/:appid/users", func(c *gin.Context){...})
appV1.GET("/statistics", func(c *gin.Context){...})
我们可以通过写一个中间件来解决,代码如下
package main
import "github.com/gin-gonic/gin"
func Statistics(c *gin.Context) {
c.JSON(200, gin.H{
"url": c.Request.URL.Path,
})
}
func hand() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.URL.Path == "/apps/statistics" {
Statistics(c)
c.Abort()
}
}
}
func main() {
r := gin.Default()
appV1 := r.Group("/apps")
appV1.Use(hand())
appV1.GET("/:appid/users", func(c *gin.Context) {
appid, _ := c.Get("appid")
c.JSON(200, gin.H{
"appid": appid,
"api": "user",
})
})
r.Run()
}
参考资料