当前位置: 首页 > 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…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...