Go 语言单文件 Web 服务实现
使用 Go 语言开发一些简单的 Web 项目时,往往需要使用一些前端静态资源文件。那有没有什么办法可以把前端资源嵌入到 Go 程序中,最好可以和 Go 程序的接口共用一个 HTTP 服务,这样在运行是也不需要考虑跨域的问题,而且单文件服务部署相比后端程序 + 静态文件的部署方式更简单。
如何嵌入资源文件
Go 在 1.16 版本增加了一个特性 go:embed
,可以在编译时嵌入文件或者目录,我们可以使用这个命令实现资源文件的嵌入。
示例项目目录如下:
ginweb
├── go.mod
├── go.sum
├── main.go // 主程序
└── web // 需要嵌入的资源文件
├── index.html // 静态资源文件
└── photo.jpg // 静态资源文件
例如通过 go:embed 将项目中的 web 目录下的所有文件进行嵌入,这时 WebDir
变量就是一个虚拟的文件系统,可以直接当作文件进行操作。
var (
//go:embed web/*
WebDir embed.FS
)
使用 HTTP 服务加载资源
// 通过 Gin 的 StaticFS()
方法,可以将嵌入的资源起一个 HTTP 服务,现在访问 2333 端口默认就会加载 /static/index.html
文件。
package main
import (
"embed"
"github.com/gin-gonic/gin"
"io/fs"
"net/http"
)
var (
//go:embed web/*
WebDir embed.FS
App *gin.Engine
)
func main() {
App = gin.New()
staticFp, _ := fs.Sub(WebDir, "web")
// 起一个静态资源 HTTP 服务
App.StaticFS("/static", http.FS(staticFp))
// 这是一个接口
App.GET("/api", func(context *gin.Context) {
context.JSONP(http.StatusOK, gin.H{
"code": 0,
"message": "hi,dbkuaizi",
})
})
App.Run(":2333")
}
利用 NoRoute 去掉 /static 目录
上面我们虽然实现了静态资源的加载,但为了区分 api 接口和静态资源,不得不多加一层 /static
静态资源目录,其实有更巧妙的方法免除掉这个目录。
我们可以利用 gin.NoRoute()
方法,这个方法的本意是当请求的接口不存在时,用于处理 404 相关的操作。我们可以在接口匹配失败后尝试加载静态资源,请求地址如下:
package main
import (
"embed"
"github.com/gin-gonic/gin"
"io/fs"
"net/http"
)
var (
//go:embed web/*
WebDir embed.FS
App *gin.Engine
)
func main() {
App = gin.New()
// 这是一个接口
App.GET("/api", func(context *gin.Context) {
context.JSONP(http.StatusOK, gin.H{
"code": 0,
"message": "hi,dbkuaizi",
})
})
// 嵌入的 embed 就是一个文件系统,获取 web 目录下的资源文件
staticFp, _ := fs.Sub(WebDir, "web")
// 所有请求先匹配 api 路由,如果没匹配到就当作静态资源文件处理
App.NoRoute(gin.WrapH(http.FileServer(http.FS(staticFp))))
// 实现 api 端口、服务器端口共用一个
App.Run(":2333")
}
在使用引用静态文件时,将 Web 目录当作根目录来使用,例如:
<!doctype html>
<html lang="zh">
<head>
<title>dbkuaizi.com</title>
</head>
<body>
<h1>hi,dbkuaizi</h1>
<img src="./photo.jpg" width="200px">
</body>
</html>
最终效果:
这篇文章写得深入浅出,让我这个小白也看懂了!