探索Gin框架:快速构建高性能的Golang Web应用

前言

        Gin框架是一个轻量级的Web框架,基于Go语言开发,旨在提供高性能和简洁的API。它具有快速的路由和中间件支持,使得构建Web应用变得更加简单和高效。无论是构建小型的API服务还是大型的Web应用,Gin框架都能够满足你的需求。

        无论你是一个有经验的开发者,还是一个刚刚入门的初学者,本文都将为你提供清晰的指导和实用的示例代码。无论你是想构建一个简单的API服务,还是一个复杂的Web应用,Gin框架都能够帮助你快速实现你的想法。

目录

​编辑

前言

适用人群

构建第一个Gin应用

1.下载并安装Gin

2.项目导入

3.快速使用示例

路由和中间件

API路由配置

路由分组

静态文件路由设置

静态路径映射

静态文件路由

路由中间件

优雅封装

Gin客户端初始化

定义api路由

在项目入口启动Gin服务

总结


适用人群

  • 懂得安装 Go 环境及其基本语法
  • 会使用 Go Modules 管理项目

构建第一个Gin应用

1.下载并安装Gin

go get -u github.com/gin-gonic/gin

2.项目导入

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

3.快速使用示例

package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

路由和中间件

API路由配置

Gin的API路由配置相当简单,只需要调用对应请求方式的方法,设置请求路径,与请求函数即可

router.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})

路由分组

我们可通过Group方法设置路由分组

// 可使用Group方法设置路由分组
userGroup := router.Group("/user")
// 该接口实际路径为/user/register
userGroup.POST("/register", controller.UserController.Register)
userGroup.POST("/login", controller.UserController.Login)

静态文件路由设置

静态路径映射

router.Static允许我们指定路径映射,如下,当我们访问路径为localhost:8080/storage时,实际上是访问到localhost:8080/storage/app/public

//当访问路径为localhost:8080/storage时,实际上是访问到localhost:8080/storage/app/public
router.Static("/storage", "./storage/app/public")

静态文件路由

设置静态文件夹路由

router.Static("/assets", "./assets")

设置静态文件路由

router.StaticFile("/favicon.ico", "./resources/favicon.ico")

路由中间件

使用use方法可使用gin自带的中间件或者自定义的中间件

我们这里自定义中间件函数,返回类型需为gin.HandlerFunc,这里我们定义三个常用的中间件作为示例

跨域处理中间件

// 跨域处理中间件
func Cors() gin.HandlerFunc {config := cors.DefaultConfig()config.AllowAllOrigins = trueconfig.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"}config.AllowCredentials = trueconfig.ExposeHeaders = []string{"New-Token", "New-Expires-In", "Content-Disposition"}return cors.New(config)
}

登录认证中间件,这里使用的是JWT认证

// JWTAuth JWT 鉴权中间件
func JWTAuth(GuardName string) gin.HandlerFunc {return func(c *gin.Context) {// Token 获取tokenStr := c.Request.Header.Get("Authorization")if tokenStr == "" {response.TokenFail(c)c.Abort() // 终止请求return}tokenStr = tokenStr[len(service.TokenType)+1:]// Token 解析校验token, err := jwt.ParseWithClaims(tokenStr, &service.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {return []byte(global.App.Config.Jwt.Secret), nil})// Token 黑名单校验if err != nil || service.JwtService.IsInBlacklist(tokenStr) {response.TokenFail(c)c.Abort()return}// Token 发布者校验和过期校验claims := token.Claims.(*service.CustomClaims)if claims.Issuer != GuardName || !token.Valid {response.TokenFail(c)c.Abort()return}// token 续签if claims.ExpiresAt.Time.Unix()-time.Now().Unix() < global.App.Config.Jwt.RefreshGracePeriod {lock := global.Lock("refresh_token_lock", global.App.Config.Jwt.JwtBlacklistGracePeriod)if lock.Get() {err, user := service.JwtService.GetUserInfo(GuardName, claims.ID)if err != nil {global.App.Log.Error(err.Error())lock.Release()} else {tokenData, _, _ := service.JwtService.CreateToken(GuardName, user)c.Header("new-token", tokenData.AccessToken)c.Header("new-expires-in", strconv.Itoa(tokenData.ExpiresIn))_ = service.JwtService.JoinBlackList(token)}}}//将token信息和id信息存入上下文c.Set("token", token)c.Set("id", claims.ID)}
}

gin自带的Recovery中间件默认日志是是打印在控制台的,故使用自定义Recovery中间件来自定义日志输出方式

这些配置信息根据自己情况调整,这里我是通过viper读取配置到全局变量中,后面我应该会出文讲解Go使用Viper读取配置

// CustomRecovery 进行程序的恢复(防止程序因 panic 而终止)和记录错误日志的中间件
func CustomRecovery() gin.HandlerFunc {// 开启程序的恢复,并将错误日志写入到指定的日志文件中return gin.RecoveryWithWriter(&lumberjack.Logger{// 日志文件名Filename:   global.App.Config.Log.RootDir + "/" + global.App.Config.Log.Filename,// 文件最大大小MaxSize:    global.App.Config.Log.MaxSize,// 旧文件的最大个数MaxBackups: global.App.Config.Log.MaxBackups,// 旧文件的最大保留天数MaxAge:     global.App.Config.Log.MaxAge,// 是否压缩Compress:   global.App.Config.Log.Compress,},response.ServerError)
}

挂载自定义的中间件,需要注意使用中间件的顺序,gin.Logger()中间件需要放在其他中间件前面,不然可能导致middleware.CustomRecovery()中的日志无法正常使用

router := gin.New()
router.Use(gin.Logger(), middleware.Cors(), middleware.CustomRecovery())

优雅封装

接下来我们优雅的封装Gin的大部分使用

Gin客户端初始化

我们在RunServer()方法中实现了优雅地关闭服务器,当关闭服务器时,如果有请求未结束,会等待5秒,5秒后再关闭服务器

// bootstrap/Router.go
package bootstrap
import ("context""github.com/gin-gonic/gin"swaggerfiles "github.com/swaggo/files"ginSwagger "github.com/swaggo/gin-swagger""log""net/http"_ "online-practice-system/docs""online-practice-system/global""online-practice-system/internal/middleware""online-practice-system/routes""os""os/signal""syscall""time"
)
// 初始化路由
func setupRouter() *gin.Engine {if global.App.Config.App.Env == "production" {gin.SetMode(gin.ReleaseMode)}router := gin.New()router.Use(gin.Logger(), middleware.Cors(), middleware.CustomRecovery())// 前端项目静态资源router.Static("/storage", "./storage/app/public")// Swagger 配置router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))// 注册 api 分组路由apiGroup := router.Group("/api")routes.SetUserGroupRoutes(apiGroup)return router
}
// RunServer 启动服务器
func RunServer() {r := setupRouter()//创建一个 http.Server 对象 srv,其中指定服务器的监听地址和路由处理器为之前设置的路由 rsrv := &http.Server{Addr:    ":" + global.App.Config.App.Port,Handler: r,}//使用 srv.ListenAndServe() 方法来异步启动服务器。go func() {if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {global.App.Log.Fatal("服务器启动失败:" + err.Error())}}()// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间), 当收到中断信号时,会触发 quit 通道,从而执行后续的关闭服务器操作。quit := make(chan os.Signal)//Notify函数让signal包将输入的中断信号 SIGINT 或终止信号 SIGTERM 转发到通道quitsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)//收到信号后,会执行下面的代码,首先打印日志,然后调用 srv.Shutdown() 方法来关闭服务器。<-quitlog.Println("Shutdown Server ...")//创建一个带有超时的上下文 ctx,超时时间设置为 5 秒ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()//调用 srv.Shutdown() 方法来关闭服务器,此时会触发 http.Server 的关闭事件,从而退出阻塞if err := srv.Shutdown(ctx); err != nil {global.App.Log.Fatal("服务器关闭时出现错误:" + err.Error())}global.App.Log.Fatal("服务器顺利关闭~~~")
}

定义api路由

// api/Router.go
package routes
import ("github.com/gin-gonic/gin""online-practice-system/internal/controller""online-practice-system/internal/middleware""online-practice-system/internal/service"
)
// SetUserGroupRoutes 定义 User 分组路由
func SetUserGroupRoutes(router *gin.RouterGroup) {userGroup := router.Group("/user")userGroup.POST("/register", controller.UserController.Register)userGroup.POST("/login", controller.UserController.Login)//使用 JWTAuth 鉴权中间件authRouter := userGroup.Use(middleware.JWTAuth(service.AppGuardName)){authRouter.GET("/userInfo", controller.UserController.GetUserInfo)authRouter.GET("/logout", controller.UserController.UserLogout)authRouter.POST("/image_upload", controller.UploadController.ImageUpload)authRouter.GET("/image_get_url/:id", controller.UploadController.GetUrlById)}
}

在项目入口启动Gin服务

package mainimport ("online-practice-system/bootstrap"
)func main() {// 启动gin web服务器bootstrap.RunServer()
}

总结

        感谢您的观看,如果您对gin的使用感兴趣的可以看看我几个月前搭建的go web脚手架,使用了一些主流的开发框架,虽然可能部分设计不是很合理,但是对于我个人来说搭建一般的web项目还是足够了。未使用go-wire的版本:go-web-starter: 基于gin,form框架的web开发脚手架 (gitee.com),使用了go-wire进行全局依赖注入的改造版:go-web-wire-starter: 使用go-wire框架与gin框架搭建的web开发脚手架,有助于web开发者快速开发curd操作,以及学习go-wire框架的工程化实践 (gitee.com)

实现的功能有如下:

• 配置统一管理

• Jwt令牌生成,校验,续签,黑名单

• 定时任务

• 文件存储(支持本地,七牛云Kodo,阿里云Oss,腾讯云Cos等存储服务,支持扩展)

• 分布式锁

• 限流器(基于令牌桶算法)

• 邮件服务

• 自定义命令行命令(代码中以数据库迁移migrate命令为示例)

使用的技术栈如下图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/645381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go api(get post传参,数据库,redis) 测试

介绍&#xff1a;分别测试get请求&#xff0c;post请求&#xff0c;请求链接数据库&#xff0c;以及redis操作。 1.api代码 package mainimport (_ "database/sql""encoding/json""github.com/gin-gonic/gin""go-test/com.zs/database&quo…

[BSidesCF 2020]Had a bad day

先看url&#xff0c;发现可能有注入 http://655c742e-b427-485c-9e15-20a1e7ef1717.node5.buuoj.cn:81/index.php?categorywoofers 试试能不能查看index.php直接?categoryindex.php不行&#xff0c;试试伪协议 把.php去掉试试 base64解码 <?php$file $_GET[category];…

Vue3+TS+dhtmlx-gantt实现甘特图

实现样式 因为只做展示&#xff0c;所以实现很简单 实现功能 自定义列头增加斑马线&#xff0c;实际结束时间&#xff08;自定义实现&#xff09;自定义进度展示&#xff0c;根据层级让进度背景颜色变浅marker标记今天自定义提示框内容 实现 import { gantt } from "d…

STM32F407移植OpenHarmony笔记1

参考文档&#xff1a; OpenAtom OpenHarmonywidthdevice-width,initial-scale1.0https://docs.openharmony.cn/pages/v3.2/zh-cn/device-dev/get-code/gettools-acquire.md/ 搭建环境 安装linux系统: Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-91-generic x86_64) 下载源代码&a…

如何在转接的NVME 固态盘上安装WIN 系统并引导启动

问题&#xff1a; 家里的台式机一直挂着一块128G的SSD固态盘&#xff0c;由于家里自己建了NAS存储&#xff0c;所以基本数据都在NAS里&#xff0c;台式机就没有挂机械盘了&#xff0c;但是最近台式机空间被系统侵蚀&#xff0c;显然就不够用了&#xff0c;除了清理系统&#xf…

bash 5.2中文修订4

Compound Commands 复合命令 复合命令是 shell 编程语言的结构。每个构造都以保留字或控制运算符开始&#xff0c;并以相应的保留字或运算符终止。与复合命令关联的任何重定向&#xff08;请参阅 Redirections &#xff09;都适用于该复合命令中的所有命令&#xff0c;除非显式…

vue3使用AntV G6 (图可视化引擎)历程[二]

上期回顾&#xff1a;历程[一]描述了基本的树状图的绘制&#xff0c;默认节点类型defaultNode中的type是circle,下面这篇描述的是节点抽离自定义节点并做数据静态渲染。 官网地址&#xff1a;https://g6-next.antv.antgroup.com/manual/introduction 一、案例效果 二、自定义节…

使用CSS 或 SASS 实现主题背景切换效果

目录 &#x1f389;应用背景 &#x1f389;分析实现思路 &#x1f389;CSS实现主题切换 &#x1f389;SCSS实现主题切换 &#x1f389;结语 &#x1f389;应用背景 现在的主流网站中&#xff0c;无论是一些技术文档获取官网&#xff0c;都存在着使用一个switch按钮实现主题…

Nginx与keepalived实现集群

提醒一下&#xff1a;下面实例讲解是在mac虚拟机里的Ubuntu系统演示的&#xff1b; Nginx与keepalived实现集群实现的效果 两台服务器都安装Nginx与keepalived&#xff1a; mater服务器的ip(192.168.200.2) backup服务器的ip(192.168.200.4) 将 mater服务器Nginx与keepalived都…

【DG 特长生2019】模拟赛赛后总结(2024.1.24)

打了330pt,订正后350pt T1 签到 T2 dfs剪枝&#xff08;虽然我写挂了&#xff09; T3 NOIP原题 T4 floyd 主要是想分享一下T4。 写了一种基于floyd的做法。 感觉好像和大部分人的写法不太一样。 因为看到大小关系&#xff0c;我就想到了传递性。 floyd是可以维护传递…

Unity中URP下获取每一个额外灯数据

文章目录 前言一、我们先来看一下 SimpleLit 中的调用二、获取额外灯索引1、非移动平台2、非GLES平台3、大多数平台 三、获取额外灯数据 前言 在上一篇文章中&#xff0c;我们知道了URP下是怎么获取额外灯数量的。 Unity中URP下获取额外灯数量 在这篇文章中&#xff0c;我们…

虹科分享丨AR与AI融合加速,医疗护理更便捷!

来源&#xff1a;虹科数字化与AR 虹科分享丨AR与AI融合加速&#xff0c;医疗护理更便捷&#xff01; 原文链接&#xff1a;https://mp.weixin.qq.com/s/Fi0wNfk_TDXRo_1-6cSRNQ 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; #AR眼镜 #医疗护理 根据Reports and Da…

MySQL函数—流程函数

MySQL函数—流程函数&#xff1a;用于实现条件筛选&#xff0c;从而题搞语句的效率。 MySQL函数—流程函数 函数功能IF(value,t,f)如果value为true&#xff0c;则返回t&#xff0c;否则返回fIFNULL(value1,value2)如果value1不为空&#xff0c;返回value1&#xff0c;否则返回v…

5G安卓手机定制_基于天玑900的安卓主板方案

5G安卓手机方案是一款采用联发科MT6877(天玑900)平台的高性能、可运行安卓操作系统的5G智能模块。该手机采用台积电6纳米低功耗工艺&#xff0c;主频高达2.4GHz&#xff0c;内存支持LPDDR5&#xff0c;并支持5G Sub-6GHz全频段和5G双载波聚合技术等多种制式。同时&#xff0c;该…

docker相关

下载Ubuntu18.04文件64位&#xff08;32位安装不了MySQL&#xff09; https://old-releases.ubuntu.com/releases/18.04.4/?_ga2.44113060.1243545826.1617173008-2055924693.1608557140 Linux ubuntu16.04打开控制台&#xff1a;到桌面&#xff0c;可以按快捷键ctrlaltt 查…

国民技术N32G430C8开发笔记一-新建IAR工程

一、创建IAR工程 1、新建工程&#xff0c;保存到project文件夹。 2、添加SDK到工程。 根据原厂SDK的文件结构在IAR新建相应分组&#xff0c;把各个文件夹的文件加载进去&#xff0c;其中startup文件选择IAR平台的startup_n32g430_EWARM.s。 3、添加头文件路径&#xff0…

2024-01-24(ElasticSearch)

1.mysql和elasticsearch的架构&#xff1a; 2.IK分词器利于分中文词汇。 底层是有一个中文字典&#xff0c;这个字典中的中文词汇也是可以拓展的和禁用某些词。 3.mapping常见属性&#xff1a; type&#xff1a;数据类型 index&#xff1a;是否索引 analyzer&#xff1a;分…

重设红帽Linux root密码:简单实用指南

在使用红帽Linux系统时&#xff0c;我们可能会遇到忘记root密码的情况红帽linux系统root密码&#xff0c;这时候该怎么办呢&#xff1f;本文将详细介绍如何重置红帽Linux系统的root密码。 1.进入单用户模式 首先&#xff0c;我们需要进入单用户模式。开机时红帽linux系统root…

一个使用pyqt的word文档查重工具

一个使用pyqt的word文档查重工具 使用场景代码使用截图打包好的软件下载链接结尾 使用场景 有时我们在借鉴一篇文档之后还不想有太多重复&#xff0c;这个时候可以使用这个工具对两个word文档进行对比 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWind…

Redisson 分布式锁解决主从一致性问题的原理

目录 一、主从不一致产生原因 二、Redisson 解决主从一致性的原理 一、主从不一致产生原因 1. Redis 主从集群&#xff1a;主从读写分离&#xff0c;主节点将数据同步给从节点 主节点&#xff1a;增删改从节点&#xff1a;读 2. 主从同步存在延迟&#xff0c;若主节点宕机…