当前位置: 首页 > news >正文

完善 Golang Gin 框架的静态中间件:Gin-Static

Gin 是 Golang 生态中目前最受用户欢迎和关注的 Web 框架,但是生态中的 Static 中间件使用起来却一直很不顺手。

所以,我顺手改了它,然后把这个改良版开源了。

写在前面

soulteary/gin-static

Gin-static 的改良版,我开源在了 soulteary/gin-static,也发布在了 Go 软件包市场:pkg.go.dev/github.com/soulteary/gin-static,有需要可以自取。

提到改良优化,那么就不得不提 Go-Gin 和原版的 Gin-Static 对于静态文件的处理。

关于 Go-Gin 和 Gin 社区的静态文件处理

在 Gin 的官方文档中,关于如何使用 Gin 来处理“静态文件相关请求” 写的很清楚:

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:8080router.Run(":8080")
}

不过,这个例子中,官方只考虑到了静态资源都存放于二级目录,并且静态资源目录只存在静态资源的情况。

如果我们的静态资源需要使用 / 根目录,或者在静态目录所在的 /assets/* 中,存在需要 Golang后端程序要进行处理的“动态逻辑”,或者我们希望使用通配符来处理某些静态文件路由,这个玩法就失效了。而这个情况,在很多前端比较重的应用中非常常见,尤其是我们希望用 Golang 来优化 Node 或者纯前端实现的项目时。

这个问题在社区的反馈中有提到过,“#21,不能够在 / 根目录使用静态文件”、“#360,通配符和静态文件冲突”。

所以,在八年前 gin-contrib 社区出现了一个专注于处理静态程序的中间件:gin-contrib/static ,帮助我们解决了这个问题,使用的方法也很简单:

package mainimport ("github.com/gin-contrib/static""github.com/gin-gonic/gin"
)func main() {r := gin.Default()// ...r.Use(static.Serve("/", static.LocalFile("/tmp", false)))// ...
}

不过,当基础功能完备后,这个插件就陷入了沉睡状态,版本号停留在 0.0.1 直至现在。

时过境迁,Golang 的版本已经升到了 1.21,这个中间件中引用的一些软件也变的陈旧,甚至被废弃,社区中也挂起了一些很好的功能实现(比如,“#19,Go 原生文件嵌入实现”),但是因为作者比较忙碌或者没有相同的痛点,所以 PR 一直未能合并。

在若干年后批判古早的代码毫无意义,所以我们就不扯出代码一行行审阅了,我个人认为相对靠谱的动作是帮助它解决问题。

在早些时候,《深入浅出 Golang 资源嵌入方案:前篇》、《深入浅出 Golang 资源嵌入方案:go-bindata篇》这两篇文章中,我提到过的 Golang 官方和社区排名靠前的资源嵌入方案,对于制作性能靠谱、方便分发的单文件应用非常有价值。

所以,结合社区里存在的 PR 提交(feat: Implement embed folder and a better organisation),我提交了一个新的 PR(#46),对之前的程序和 PR 实现的代码都做了一些完善,并且确保这个中间件测试覆盖率是 100%,使用起来能够更安心。

下载 gin-static 优化版

和其他社区软件一样,使用下面的一句话命令,可以完成 gin-static 的下载了:

go get github.com/soulteary/gin-static

如果你是全新使用,在你的在程序中添加下面的引用内容即可:

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

如果你已经使用了社区的 github.com/gin-gonic/gin-static 软件包,并且不想修改已有程序的引用和行为,那么我们可以用另外一种方法。

在你的 go.mod 文件中,我们应该能够看到类似下面的内容:

module your-projectgo 1.21.2require (github.com/gin-gonic/gin v1.9.1github.com/gin-gonic/gin-static v0.0.1
)

我们只需要在 require 之前,添加一条依赖替换规则即可:

module your-projectgo 1.21.2replace (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.1github.com/gin-gonic/gin-static v0.0.1
)

完成内容添加后,我们执行 go mod tidy,完成依赖的更新即可。不论是哪一种使用方式,当你执行完命令后,我们就能够使用支持 Go 原生嵌入文件使用啦。

使用 gin-static 优化版

在项目的示例目录中,我提交了两个使用示例程序,分别包含“基础使用(simple)” 和 支持“文件嵌入”的例子(embed):

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

基础使用

程序的基础使用,和之前社区版本的接口一致,如果我们想在程序中直接使用本地的静态文件:

package mainimport ("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:8080if err := r.Run(":8080"); err != nil {log.Fatal(err)}
}

实际使用过程中,我们还可以对根目录做一些额外的逻辑,使用 r.[Method] 来覆盖默认的静态文件路由:

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

文件嵌入

在早些时候,《深入浅出 Golang 资源嵌入方案:前篇》、《深入浅出 Golang 资源嵌入方案:go-bindata篇》这两篇文章中,我提到过的 Golang 官方和社区排名靠前的资源嵌入方案,对于制作性能靠谱、方便分发的单文件应用非常有价值。

使用 gin-static 来处理嵌入文件非常简单,并且支持多种用法:

package mainimport ("embed""fmt""net/http""github.com/gin-gonic/gin"
)//go:embed public
var EmbedFS embed.FSfunc 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:8080r.Run(":8080")
}

上面的代码中,我们首先使用 //go:embed public 将本地的 public 目录读入 Golang 程序中,转换为程序可以访问的对象。然后你就可以根据你自己的具体情况,使用上面程序中的任意一种用法了。

当我们使用 go build 构建程序后,就能够得到一个包含了所有依赖静态文件的单一可执行文件啦。

个人倾向用法

我个人在使用的过程中,倾向于将上面两种用法合并在一起,当我们在开发的时候,使用本地文件系统(前者),而当我们构建的时候,则使用 Go 内嵌文件系统(后者)。

这样可以确保我们在玩的时候,静态文件支持所见即所得的修改立即生效,下面是我个人喜欢的用法示例:

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))
}

在上面的代码里,我们将本地的静态文件,在开发时默认挂载在 / 根目录,用于“兜底访问(fallback)”,这些文件允许被各种其他的路由覆盖。当我们进行构建或设置 debugMode=false 的时候,我们将静态文件挂载低优先级的 NoRoute 路由中,用于“兜底访问(fallback)”,如果我们需要调整或覆盖一些真实存在的静态文件,那么我们需要在路由前做额外的处理。

最后

好了,这个中间件就是这么简单,我们已经聊完了 80% 相关的内容啦。有机会我们在聊聊更有趣的 Embed 文件优化的故事。

–EOF


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2024年01月03日
统计字数: 6357字
阅读时间: 13分钟阅读
本文链接: https://soulteary.com/2024/01/03/golang-gin-static-middleware-improves.html

相关文章:

完善 Golang Gin 框架的静态中间件:Gin-Static

Gin 是 Golang 生态中目前最受用户欢迎和关注的 Web 框架,但是生态中的 Static 中间件使用起来却一直很不顺手。 所以,我顺手改了它,然后把这个改良版开源了。 写在前面 Gin-static 的改良版,我开源在了 soulteary/gin-static&a…...

html websocket的基本使用

html websocket的基本使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"w…...

大数据 MapReduce是什么?

在Hadoop问世之前&#xff0c;其实已经有了分布式计算&#xff0c;只是那个时候的分布式计算都是专用的系统&#xff0c;只能专门处理某一类计算&#xff0c;比如进行大规模数据的排序。 很显然&#xff0c;这样的系统无法复用到其他的大数据计算场景&#xff0c;每一种应用都…...

ubuntu 如何放开防火墙端口,ubuntu 防火墙操作命令,ubuntu 防火墙全面操作说明

本文介绍了Ubuntu操作系统有关防火墙操作的命令。为了便于说明&#xff0c;请使用 root 用户或具有超级管理员权限的用户登录到 Ubuntu 系统&#xff0c;这样操作命令前就不需要加 sudo了。 一、安装防火墙 如果没有安装防火墙&#xff0c;请用如下命令安装&#xff1a; apt …...

计算机视觉入门与调优

大家好啊&#xff0c;我是董董灿。 在 CSDN 上写文章写了有一段时间了&#xff0c;期间不少小伙伴私信我&#xff0c;咨询如何自学入门AI&#xff0c;或者咨询一些AI算法。 90%的问题我都回复了&#xff0c;但有时确实因为太忙&#xff0c;没顾得过来。 在这个过程中&#x…...

Ndk编译hevc静态库

源码下载: https://hg.videolan.org/x265 然后执行以下脚本: #!/bin/bash# 设置NDK路径&#xff0c;根据你的实际安装路径修改 NDK_PATH/mnt/c/Users/Administrator/ubuntu_dev/ndk/android-ndk-r21e# 设置目标平台和ABI版本&#xff0c;可以根据实际情况修改 aarch64-linux-…...

Linux系统安装MySQL

Linux系统安装MySQL 第一步&#xff1a;下载YUM wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm第二步&#xff1a;安装MySQL的YUM 仓库 rpm -ivh mysql57-community-release-el7-11.noarch.rpm第三步&#xff1a;查看MySQL版本 yum repolist …...

linux go环境安装 swag

下载依赖包 go get -u github.com/swaggo/swag编译 移动到下载的swag包目录,一般在$GOPATH/pkg/mod下 查看 GOPATH echo $GOPATHcd /root/GolangProjects/pkg/mod/github.com/swaggo/swagv1.16.2go install ./cmd/swag/不出意外&#xff0c;$GOPATH/bin下 已经有了swag 初…...

高效分割视频:批量剪辑,轻松提取m3u8视频技巧

在数字媒体时代&#xff0c;视频分割是一项常见的需求。无论是为了编辑、分享还是其他要求&#xff0c;经常要将长视频分割成多个短片。传统的视频分割方法往往需要手动操作&#xff0c;既耗时又容易出错。现在来看云炫AI智剪高效分割视频的方法&#xff0c;批量剪辑并轻松提取…...

自由DIY预约小程序源码系统:适用于任何行业+自由DIY你的界面布局+全新升级的UI+多用户系统 带安装部署教程

随着移动互联网的普及&#xff0c;预约服务逐渐成为人们日常生活的一部分。从家政服务、医疗挂号到汽车保养&#xff0c;预约已经渗透到各个行业。然而&#xff0c;市面上的预约小程序大多功能单一&#xff0c;界面老旧&#xff0c;无法满足商家和用户的个性化需求。今天来给大…...

el-select 多选,选有一个未选择的选项

多选有未选择这个选项后。会出现一个情况&#xff0c;绑定的数据为[‘未选择’,‘cpu1’,‘cpu2’] 进行一个处理&#xff0c;选择&#xff08;未选择&#xff09;就清除&#xff08;其它的选择&#xff09;&#xff0c;选择&#xff08;cpu&#xff09;就清除&#xff08;未选…...

CISSP 第6章: 密码学与对称加密算法

第六章 密码学与对称加密算法 6.1 密码学历史上的里程碑 6.1.1 凯撒密码 简单的将字母表中的每个字母替换成其后的三个字母&#xff0c;是单一字母的替代置换密码 6.1.2 美国内战 美国内战使用词汇替代和置换的复杂组合&#xff0c;从而试图破坏敌人的破译企图 6.1.3 Ultra与…...

《深入理解C++11:C++11新特性解析与应用》笔记八

第八章 融入实际应用 8.1 对齐支持 8.1.1 数据对齐 c可以通过sizeof查询数据的长度&#xff0c;但是没有对对齐方式有关的查询或者设定进行标准化。c11标准定义的alignof函数可以查看数据的对齐方式。 现在的计算机通常会支持许多向量指令&#xff0c;4组8字节的浮点数据&a…...

算法——BFS解决FloodFill算法

什么是FloodFill算法 中文&#xff1a;洪水灌溉。假设这一块4*4的方格是一块土地&#xff0c;有凸起的地方&#xff0c;也有凹陷的地方&#xff08;凹陷的地方用负数表示&#xff09;。此时下大雨发洪水&#xff0c;会把凹陷的地方填满。绿色圈起来的属于一块区域&#xff08;…...

【Linux】常用的基本命令指令②

前言&#xff1a;前面我们学习了Linux的部分指令&#xff0c;今天我们将接着上次的部分继续将Linux剩余的基本指令. &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的学习 &#x1f448; &#x1f4af;代码仓库:卫卫周大胖的学习日记…...

52、全连接 - 特征与样本空间的对应关系

上一节说到经过全连接层之后,神经网络学习到的特征,会从隐层特征空间逐步映射到样本空间,这主要是由于全连接层可以融合全局的特征。 在经过全连接层之后,在 ResNet50 这个神经网络中会输出1000个特征的得分值,这1000个特征的得分值,便可以对应到图像的分类。 怎么对应…...

Go语言中的包管理工具之Go Vendor的使用

GoLang 中常用的包管理的方式 常用的有三种 Go PathGo VendorGo Modules 关于 Go Vender 1 &#xff09;概述 在2015年的时候&#xff0c;我们的另一个包管理工具Go Vendor就诞生了它诞生于 2015.8.19 &#xff0c;是在Go的 1.5 版本当中引入的&#xff0c;它默认是关闭的我…...

QString设置小数点精度位数

QString设置小数点精度位数 Chapter1 QString设置小数点精度位数Chapter2 Qt中QString.toDouble有效位数6位问题以及数据小数点有效位数的处理问题一&#xff1a;QString.toDouble有效位只有6位问题二:小数点有效位数的问题 Chapter3 qt QString转Double只显示6位数字的问题(精…...

基于Java驾校预约管理系统

基于Java的驾校预约管理系统是一个为驾校提供在线预约服务的系统。该系统利用Java编程语言&#xff0c;采用SSM框架&#xff0c;并使用MySQL数据库进行开发。 这个系统主要有三个角色&#xff1a;用户、教练和管理员。 用户可以注册和登录系统&#xff0c;查看驾校的公告信息…...

C++面向对象高级编程(侯捷)笔记2

侯捷C面向对象高级编程 本文是学习笔记&#xff0c;仅供个人学习使用&#xff0c;如有侵权&#xff0c;请联系删除。 如果你对C面向对象的组合、继承和委托不了解&#xff0c;对什么是拷贝构造、什么是拷贝赋值和析构不清楚&#xff0c;对类设计中的Adapter、pImpl、Template…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...