完善 Golang Gin 框架的静态中间件:Gin-Static
Gin 是 Golang 生态中目前最受用户欢迎和关注的 Web 框架,但是生态中的 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问世之前,其实已经有了分布式计算,只是那个时候的分布式计算都是专用的系统,只能专门处理某一类计算,比如进行大规模数据的排序。 很显然,这样的系统无法复用到其他的大数据计算场景,每一种应用都…...
ubuntu 如何放开防火墙端口,ubuntu 防火墙操作命令,ubuntu 防火墙全面操作说明
本文介绍了Ubuntu操作系统有关防火墙操作的命令。为了便于说明,请使用 root 用户或具有超级管理员权限的用户登录到 Ubuntu 系统,这样操作命令前就不需要加 sudo了。 一、安装防火墙 如果没有安装防火墙,请用如下命令安装: apt …...
计算机视觉入门与调优
大家好啊,我是董董灿。 在 CSDN 上写文章写了有一段时间了,期间不少小伙伴私信我,咨询如何自学入门AI,或者咨询一些AI算法。 90%的问题我都回复了,但有时确实因为太忙,没顾得过来。 在这个过程中&#x…...
Ndk编译hevc静态库
源码下载: https://hg.videolan.org/x265 然后执行以下脚本: #!/bin/bash# 设置NDK路径,根据你的实际安装路径修改 NDK_PATH/mnt/c/Users/Administrator/ubuntu_dev/ndk/android-ndk-r21e# 设置目标平台和ABI版本,可以根据实际情况修改 aarch64-linux-…...
Linux系统安装MySQL
Linux系统安装MySQL 第一步:下载YUM wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm第二步:安装MySQL的YUM 仓库 rpm -ivh mysql57-community-release-el7-11.noarch.rpm第三步:查看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/不出意外,$GOPATH/bin下 已经有了swag 初…...
高效分割视频:批量剪辑,轻松提取m3u8视频技巧
在数字媒体时代,视频分割是一项常见的需求。无论是为了编辑、分享还是其他要求,经常要将长视频分割成多个短片。传统的视频分割方法往往需要手动操作,既耗时又容易出错。现在来看云炫AI智剪高效分割视频的方法,批量剪辑并轻松提取…...
自由DIY预约小程序源码系统:适用于任何行业+自由DIY你的界面布局+全新升级的UI+多用户系统 带安装部署教程
随着移动互联网的普及,预约服务逐渐成为人们日常生活的一部分。从家政服务、医疗挂号到汽车保养,预约已经渗透到各个行业。然而,市面上的预约小程序大多功能单一,界面老旧,无法满足商家和用户的个性化需求。今天来给大…...
el-select 多选,选有一个未选择的选项
多选有未选择这个选项后。会出现一个情况,绑定的数据为[‘未选择’,‘cpu1’,‘cpu2’] 进行一个处理,选择(未选择)就清除(其它的选择),选择(cpu)就清除(未选…...
CISSP 第6章: 密码学与对称加密算法
第六章 密码学与对称加密算法 6.1 密码学历史上的里程碑 6.1.1 凯撒密码 简单的将字母表中的每个字母替换成其后的三个字母,是单一字母的替代置换密码 6.1.2 美国内战 美国内战使用词汇替代和置换的复杂组合,从而试图破坏敌人的破译企图 6.1.3 Ultra与…...
《深入理解C++11:C++11新特性解析与应用》笔记八
第八章 融入实际应用 8.1 对齐支持 8.1.1 数据对齐 c可以通过sizeof查询数据的长度,但是没有对对齐方式有关的查询或者设定进行标准化。c11标准定义的alignof函数可以查看数据的对齐方式。 现在的计算机通常会支持许多向量指令,4组8字节的浮点数据&a…...
算法——BFS解决FloodFill算法
什么是FloodFill算法 中文:洪水灌溉。假设这一块4*4的方格是一块土地,有凸起的地方,也有凹陷的地方(凹陷的地方用负数表示)。此时下大雨发洪水,会把凹陷的地方填满。绿色圈起来的属于一块区域(…...
【Linux】常用的基本命令指令②
前言:前面我们学习了Linux的部分指令,今天我们将接着上次的部分继续将Linux剩余的基本指令. 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:Linux的学习 👈 💯代码仓库:卫卫周大胖的学习日记…...
52、全连接 - 特征与样本空间的对应关系
上一节说到经过全连接层之后,神经网络学习到的特征,会从隐层特征空间逐步映射到样本空间,这主要是由于全连接层可以融合全局的特征。 在经过全连接层之后,在 ResNet50 这个神经网络中会输出1000个特征的得分值,这1000个特征的得分值,便可以对应到图像的分类。 怎么对应…...
Go语言中的包管理工具之Go Vendor的使用
GoLang 中常用的包管理的方式 常用的有三种 Go PathGo VendorGo Modules 关于 Go Vender 1 )概述 在2015年的时候,我们的另一个包管理工具Go Vendor就诞生了它诞生于 2015.8.19 ,是在Go的 1.5 版本当中引入的,它默认是关闭的我…...
QString设置小数点精度位数
QString设置小数点精度位数 Chapter1 QString设置小数点精度位数Chapter2 Qt中QString.toDouble有效位数6位问题以及数据小数点有效位数的处理问题一:QString.toDouble有效位只有6位问题二:小数点有效位数的问题 Chapter3 qt QString转Double只显示6位数字的问题(精…...
基于Java驾校预约管理系统
基于Java的驾校预约管理系统是一个为驾校提供在线预约服务的系统。该系统利用Java编程语言,采用SSM框架,并使用MySQL数据库进行开发。 这个系统主要有三个角色:用户、教练和管理员。 用户可以注册和登录系统,查看驾校的公告信息…...
C++面向对象高级编程(侯捷)笔记2
侯捷C面向对象高级编程 本文是学习笔记,仅供个人学习使用,如有侵权,请联系删除。 如果你对C面向对象的组合、继承和委托不了解,对什么是拷贝构造、什么是拷贝赋值和析构不清楚,对类设计中的Adapter、pImpl、Template…...
双曲正弦函数(*) 优化麦克劳林公式
#include<stdio.h> #include<math.h> int main() {double x,eps,i3,y,item;scanf("%lf%lf",&x,&eps);yx;itemx;while(fabs(item)>eps){itemitem*x*x/i/(i-1);i2;yitem;}printf("%.6f\n",y);return 0; }...
无监督关键词提取算法:TF-IDF、TextRank、RAKE、YAKE、 keyBERT
TF-IDF TF-IDF是一种经典的基于统计的方法,TF(Term frequency)是指一个单词在一个文档中出现的次数,通常一个单词在一个文档中出现的次数越多说明该词越重要。IDF(Inverse document frequency)是所有文档数比上出现某单词的个数,通常一个单词…...
web3 : blockscout剖析
Blockscout 是第一个功能齐全的开源区块链浏览器,可供任何以太坊虚拟机 (EVM) 链使用。项目方可以下载并使用Blockscout作为其链的浏览器,用户可以轻松验证交易、余额、区块确认、智能合约和其他记录。 目录 Blockscout可以做什么主要特征blockscoutDocker容器组件Postgres 1…...
【机器学习基础】DBSCAN
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!相对完整的机器学习基础教学! ⭐特别提醒:针对机器学习,特别开始专栏:机器学习python实战…...
计算机硬件 4.4键盘与鼠标
第四节 键盘与鼠标 一、认识键盘 1.地位:计算机系统最基本的输入设备。 2.外观结构:面板、键帽、底盘、数据线。 3.组成键区:主键区、功能键区、辅助键区和编辑(控制)键区。 二、键盘分类 1.按接口分 ①AT口&…...
Flappy Bird QDN PyTorch博客 - 代码解读
Flappy Bird QDN PyTorch博客 - 代码解读 介绍环境配置项目目录结构QDN算法重要函数解读preprocess(observation)DeepNetWork(nn.Module)BirdDQN类主程序部分 介绍 在本博客中,我们将介绍如何使用QDN(Quantile Dueling Network)算法…...
听GPT 讲Rust源代码--compiler(9)
File: rust/compiler/rustc_trait_selection/src/traits/select/mod.rs 在Rust源代码中,rust/compiler/rustc_trait_selection/src/traits/select/mod.rs文件的作用是实现Rust编译器的trait选择器。 首先,让我们逐个介绍这些struct的作用: Se…...
Go语言中关于go get, go install, go build, go run指令
go get go get 它会执行两个操作 第一个, 是先将远程的代码克隆到Go Path的 src 目录那二个, 是执行go install命令 那如果指定的包可以生成二进制文件那它就会把这个二进制文件保存到这个 Go Path 的bin目录下面这是 go install 命令执行的操作 如果只需要下载包,…...
石头剪刀布游戏 - 华为OD统一考试
OD统一考试 分值: 100分 题解: Java / Python / C++ 题目描述 石头剪刀布游戏有 3 种出拳形状: 石头、剪刀、布。分别用字母 A,B,C 表示游戏规则: 出拳形状之间的胜负规则如下: A>B; B>C; C>A; 左边一个字母,表示相对优势形状。右边一个字母,表示相对劣势形状。…...
【北亚服务器数据恢复】ZFS文件系统服务器ZPOOL下线的数据恢复案例
服务器数据恢复环境: 服务器中有32块硬盘,组建了3组RAIDZ,部分磁盘作为热备盘。zfs文件系统。 服务器故障: 服务器运行中突然崩溃,排除断电、进水、异常操作等外部因素。工作人员将服务器重启后发现无法进入操作系统。…...
网站内页seo查询/搜索引擎优化简称
上一篇文章:【AI绘画】我以Midjourney为主学习AI绘画效果咋样?_山楂山楂丸的博客-CSDN博客 目录 前言 一、「Stable Diffusion」 是什么 二、「Stable Diffusion」上手演练 三、竟然还有ChatGPT? 四、「Stable Diffusion」作品展示 五、…...
山东省建设监理网站/近期时事新闻
最近写了一个VUE的web app项目,需要实现某个部位吸顶的效果。即,页面往上滑动,刚好到达该部位时,该部分,固定在顶部显示。 image.png 1、监听滚动事件 利用VUE写一个在控制台打印当前的scrollTop, 首先,…...
wordpress模板 企业/sem竞价培训
小程序之修改轮播图的小点样式 1、单独写一个小点的元素。 <view classswiper-box><swiper indicator-dots"{{indicatorDots}}" autoplay"{{autoplay}}" interval"{{interval}}" duration"{{duration}}" bindchange"swi…...
房山重庆网站建设/关键词优化的主要工具
我在微服务组里面主要负责配置中心的构建,我们的配置中心使用到了ETCD。在我们的内网环境中搭建了三个节点的ETCD,不过这三个节点的ETCD都搭建在同一台机器上。后来机器资源不够了系统直接kill了ETCD,导致内网的ETCD三个节点全部挂掉了。刚开…...
网站建设198/2023年4月疫情恢复
本文章为《互联网高并发微服务化架构实践》系列课程的第六篇 前五篇为: 微服务化的基石——持续集成 微服务的接入层设计与动静资源隔离 微服务化的数据库设计与读写分离 微服务化之无状态化与容器化 微服务化之缓存的设计 一、服务拆分的前提 说到微服务࿰…...
自己做网站需要啥/曲靖新闻今日头条
保存个链接,免得以后用的时候找不到 下载地址...