Static middleware that improves the Golang Gin framework: Gin-Static

Gin is currently the most popular and popular web framework among users in the Golang ecosystem, but Staticthe middleware in the ecosystem has always been difficult to use.

So, I modified it and made this improved version open source.

write in front

soulteary/gin-static

I open sourced the improved version of Gin-static in soulteary/gin-static and also published it in the Go software package market: pkg.go.dev/github.com/soulteary/gin-static . You can pick it up if you need it.

When it comes to improved optimization, we have to mention the processing of static files by Go-Gin and the original Gin-Static.

About static file processing in Go-Gin and Gin community

In the official documentation of Gin, it is very clear on how to use Gin to handle " static file related requests ":

func main() {
    
    
	router := gin.Default()
	router.Static("/assets", "./assets")
	router.StaticFS("/more_static", http.Dir("my_file_system"))
	router.StaticFile("/favicon.ico", "./resources/favicon.ico")

	// Listen and serve on 0.0.0.0:8080
	router.Run(":8080")
}

However, in this example, the official only considered the situation where static resources are stored in the secondary directory, and the static resource directory only contains static resources.

If our static resources need to use /the root directory, or in the directory where the static directory is located /assets/*, there is "dynamic logic" that needs to be processed by the Golang backend program, or we want to use wildcards to handle certain static file routing, this method will not work. . This situation is very common in many front-end-heavy applications, especially when we want to use Golang to optimize Node or pure front-end implementation projects.

This problem has been mentioned in community feedback, " #21, static files cannot be used in the / root directory ", " #360, conflict between wildcards and static files ".

Therefore, eight years ago, a middleware focused on processing static programs appeared in the gin-contrib community: gin-contrib/static , which helped us solve this problem. The method used is also very simple:

package main

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

func main() {
    
    
  r := gin.Default()
  // ...
  r.Use(static.Serve("/", static.LocalFile("/tmp", false)))
  // ...
}

However, when the basic functions were complete, the plug-in fell into a dormant state, and the version number stayed at 0.0.1 until now.

Time has passed, Golang's version has been upgraded to 1.21, some software referenced in this middleware has become obsolete, or even abandoned, and some good functional implementations have been suspended in the community (for example, " #19, Go native file embedding "Achieve "), but because the author is busy or does not have the same pain points, the PR has not been merged.

It is meaningless to criticize the old code after a few years, so we will not pull out the code and review it line by line. I personally think that the relatively reliable action is to help it solve the problem.

Earlier, in the two articles " Golang's resource embedding scheme in simple terms: Part 1 " and " Golang's resource embedding scheme in simple terms: go-bindata ", I mentioned the top-ranked Golang official and community resource embedding schemes. , which is very valuable for making single-file applications with reliable performance and easy distribution.

Therefore, combined with the existing PR submissions in the community ( feat: Implement embed folder and a better organization ), I submitted a new PR (#46) , made some improvements to the previous program and PR implementation code, and ensured The test coverage of this middleware is 100%, which makes it more secure to use.

Download gin-static optimized version

Like other community software, use the following command to complete the download of gin-static:

go get github.com/soulteary/gin-static

If you are new to using it, just add the following reference content to your program:

import "github.com/soulteary/gin-static"

// 或
import (
	static "github.com/soulteary/gin-static"
)

If you are already using community github.com/gin-gonic/gin-staticsoftware packages and do not want to modify the references and behavior of existing programs, then we can use another method.

In your go.modfile, we should see something like this:

module your-project

go 1.21.2

require (
	github.com/gin-gonic/gin v1.9.1
	github.com/gin-gonic/gin-static v0.0.1
)

We only need to requireadd a dependency replacement rule before:

module your-project

go 1.21.2

replace (
	github.com/gin-gonic/gin-static v0.0.1 => github.com/soulteary/gin-static v0.0.5
)

require (
	github.com/gin-gonic/gin v1.9.1
	github.com/gin-gonic/gin-static v0.0.1
)

After adding the content, we execute it go mod tidyto complete the update of dependencies. No matter which way you use it, after you execute the command, we can use the Go native embedded file.

Use gin-static optimized version

In the project's example directory , I submitted two usage example programs, including "basic usage (simple)" and an example that supports "file embedding" (embed):

├── embed
│   ├── go.mod
│   ├── go.sum
│   ├── main.go
│   └── public
│       └── page
└── simple
    ├── go.mod
    ├── go.sum
    ├── main.go
    └── public
        └── index.html

Basic usage

The basic use of the program is consistent with the interface of the previous community version. If we want to use local static files directly in the program:

package main

import (
	"log"

	"github.com/gin-gonic/gin"
	static "github.com/soulteary/gin-static"
)

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

    // 静态文件在默认根路径
	r.Use(static.Serve("/", static.LocalFile("./public", false)))

    // 其他路径 /other-place
    // r.Use(static.Serve("/other-place", static.LocalFile("./public", false)))

	r.GET("/ping", func(c *gin.Context) {
    
    
		c.String(200, "test")
	})

	// Listen and Server in 0.0.0.0:8080
	if err := r.Run(":8080"); err != nil {
    
    
		log.Fatal(err)
	}
}

In actual use, we can also do some additional logic on the root directory and use r.[Method]to override the default static file routing:

// 将静态资源注册到根目录,使用本地的 Public 作为“数据源”
r.Use(static.Serve("/", static.LocalFile("public", false)))
// 允许添加其他的路由规则处理根目录
r.GET("/", func(c *gin.Context) {
    
    
  c.Redirect(http.StatusMovedPermanently, "/somewhere")
})

File embedding

Earlier, in the two articles " Golang's resource embedding scheme in simple terms: Part 1 " and " Golang's resource embedding scheme in simple terms: go-bindata ", I mentioned the top-ranked Golang official and community resource embedding schemes. , which is very valuable for making single-file applications with reliable performance and easy distribution.

Working with embedded files using gin-staticis very simple and supports a variety of usages:

package main

import (
	"embed"
	"fmt"
	"net/http"

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

//go:embed public
var EmbedFS embed.FS

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

	// Method 1: use as Gin Router
	// trim embedfs path `public/page`, and use it as url path `/`
	r.GET("/", static.ServeEmbed("public/page", EmbedFS))

	// OR, Method 2: use as middleware
	// trim embedfs path `public/page`, the embedfs path start with `/`
	r.Use(static.ServeEmbed("public/page", EmbedFS))

	// OR, Method 2.1: use as middleware
	// trim embedfs path `public/page`, the embedfs path start with `/public/page`
	r.Use(static.ServeEmbed("", EmbedFS))

	// OR, Method 3: use as manual
	// trim embedfs path `public/page`, the embedfs path start with `/public/page`
	// staticFiles, err := static.EmbedFolder(EmbedFS, "public/page")
	// if err != nil {
    
    
	// 	log.Fatalln("initialization of embed folder failed:", err)
	// } else {
    
    
	// 	r.Use(static.Serve("/", staticFiles))
	// }

	r.GET("/ping", func(c *gin.Context) {
    
    
		c.String(200, "test")
	})

	r.NoRoute(func(c *gin.Context) {
    
    
		fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path)
		c.Redirect(http.StatusMovedPermanently, "/")
	})

	// Listen and Server in 0.0.0.0:8080
	r.Run(":8080")
}

In the above code, we first use //go:embed publicto read the local publicdirectory into the Golang program and convert it into an object that the program can access. Then you can use any of the above programs according to your own specific situation.

When we use to go buildbuild the program, we will get a single executable file that contains all dependent static files.

Personal usage preference

In my personal use, I tend to merge the above two usages together. When we are developing, we use the local file system (the former), and when we build, we use the Go embedded file system (the latter). By).

This ensures that when we play, the static file supports WYSIWYG modifications to take effect immediately. Here is my personal favorite usage example:

if debugMode {
    
    
	r.Use(static.Serve("/", static.LocalFile("public", false)))
} else {
    
    
	r.NoRoute(
		// 例如,对存在的具体目录进行一些特殊逻辑处理
		func(c *gin.Context) {
    
    
			if c.Request.URL.Path == "/somewhere/" {
    
    
				c.Data(http.StatusOK, "text/html; charset=utf-8", []byte("custom as you like"))
				c.Abort()
			}
		},
		static.ServeEmbed("public", EmbedFS),
	)
	// 或者,不需要额外处理和拦截存在的静态文件
	// r.NoRoute(static.ServeEmbed("public", EmbedFS))
}

In the above code, we mount local static files in the /root directory by default during development for "fallback". These files are allowed to be overwritten by various other routes. When we build or set up debugMode=false, we mount the static files in the low-priority NoRouteroute for "fallback". If we need to adjust or overwrite some real static files, then we need to add them to the route. Do additional processing before.

at last

Okay, this middleware is that simple, we have finished talking about 80% of the related content. We have a chance to talk about more interesting Embed file optimization stories.

–EOF


This article uses the "Attribution 4.0 International (CC BY 4.0)" license agreement. You are welcome to reprint, re-modify and use it, but the source must be indicated. Attribution 4.0 International (CC BY 4.0)

Author of this article: Su Yang

Creation time: January 3, 2024
Statistical word count: 6357 words
Reading time: 13 minutes to read
Link to this article: https://soulteary.com/2024/01/03/golang-gin-static-middleware-improves.html

Guess you like

Origin blog.csdn.net/soulteary/article/details/135372391