Golang-Gin光速入门

安装

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

初始化项目并启动服务

go mod init gin-project

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 上启动服务
}

上面启动了一个服务器,gin.Default方法返回一个带有日志和错误捕获中间件的引擎
在这里插入图片描述

项目目录

参数

项目目录

在这里插入图片描述

main.go中

package main
import ("gin-project/global""gin-project/initialize"
)
func main() {global.GVB_ENG = initialize.InitEngine()if global.GVB_ENG != nil {initialize.InitRouter()global.GVB_ENG.Run()}
}

router/params.go

package routerimport ("gin-project/controller""gin-project/global"
)func InitParamsGroup() {paramsRouter := global.GVB_ENG.Group("/use"){paramsRouter.GET("/uri/:id", controller.ParamsController.GetURI)paramsRouter.GET("/query", controller.ParamsController.GetQuery)paramsRouter.POST("/formdata", controller.ParamsController.GetFomdata)}
}

initialize/router.go

package initializeimport "gin-project/router"func InitRouter() {router.InitParamsGroup()
}

uri参数

文档
uri参数长什么样?->http://fancy_fish.top/123456
在这个url后面的123456就是uri参数

获取uri参数方式一ShouldBindUri

如何获取uri参数呢

  1. 创建一个结构体字段要和uri占位符一致
  2. 给结构体对应字段设置关联标签
  3. 调用ShouldBindUri获取即可
paramsRouter := global.GVB_ENG.Group("/use")
paramsRouter.GET("/uri/:id/:name", 
GetURI)func (receiver *params) GetURI(c *gin.Context) {type Person struct {ID   string `uri:"id" binding:"required"`Name string `uri:"name"`}var p Personif err := c.ShouldBindUri(&p); err != nil {c.JSON(400, gin.H{"msg": err.Error()})return}c.JSON(200, gin.H{"data": p,})
}

在这里插入图片描述

获取URI参数方式二Param

func (receiver *params) GetURI(c *gin.Context) {type Person struct {ID   string `uri:"id" binding:"required"`Name string `uri:"name"`}var p Personp.ID = c.Param("id")p.Name = c.Param("name")c.JSON(200, gin.H{"data": p,})
}

query参数

query参数长什么样?http://127.0.0.1:8080/use/query?id=12452&name=fancy_fish

paramsRouter.GET("/query", GetQuery)func (receiver *params) GetQuery(c *gin.Context) {type Person struct {ID   stringName string}p := Person{}p.ID = c.Query("id")p.Name = c.DefaultQuery("name", "默认Query")c.JSON(200, gin.H{"data": p,})
}

在这里插入图片描述

DefaultQuery

当没有获取到指定query参数,会给默认值。如上图所示。

formdata参数

paramsRouter.POST("/formdata",GetFormdata)func (receiver *params) GetFormdata(c *gin.Context) {type Person struct {ID   stringName string}p := Person{}p.ID = c.PostForm("id")p.Name = c.DefaultPostForm("name", "默认postform")c.JSON(200, gin.H{"data": p,})
}

在这里插入图片描述

DefaultPostForm

当没有获取到指定postform参数,会给默认值。如图所示,代码同上。
在这里插入图片描述

上传单个文件FormFile

在这里插入图片描述

我们先上传个文件打印一下看看获取到的结果是什么

paramsRouter.POST("/file/upload", controller.ParamsController.GetFile)
func (receiver *params) GetFile(c *gin.Context) {f, err := c.FormFile("file")fmt.Println(f.Size, f.Filename, f.Header)// 266438  1.png  map[Content-Disposition:[form-data; name="file"; filename="1.png"] Content-Type:[image/png]]if err != nil {c.String(500, "上传文件失败")}c.JSON(200, gin.H{"data": "",})
}

可以看到可以得到文件的文件名、文件大小、格式等

保存到本地

func (receiver *params) GetFile(c *gin.Context) {f, err := c.FormFile("file")if err != nil {c.String(500, "上传文件失败")return}if err := c.SaveUploadedFile(f, path.Join("./assets", f.Filename)); err != nil {fmt.Println(err.Error(), "文件保存失败")return}c.JSON(200, gin.H{"message":  "success","code":     1,"fileName": f.Filename,})
}

在这里插入图片描述

上传多个文件MultipartForm

在这里插入图片描述

paramsRouter.POST("/files/upload", controller.ParamsController.GetFiles)
func (receiver *params) GetFiles(c *gin.Context) {form, err := c.MultipartForm()if err != nil {c.String(500, "上传文件失败")return}files := form.File["files"]fileNames := make([]string, 0)for index, f := range files {fmt.Println(index, f.Filename, f.Size)fileNames = append(fileNames, f.Filename)if err := c.SaveUploadedFile(f, path.Join("./assets", f.Filename)); err != nil {fmt.Println(err.Error(), "文件保存失败")return}}c.JSON(200, gin.H{"message":  "success","code":     1,"fileName": fileNames,})
}

在这里插入图片描述

数据绑定

GIN提供我们API可以让我们将客户端传递的参数直接绑定到结构体,我们只需要对结构体字段打标签即可。GIN可以绑定一下几类数据,接下来直接展示。
Gin使用 go-playground/validator/v10 进行验证,可以查看标签用法的全部文档。
在这里插入图片描述
我们使用ShouildBindWith去绑定就行了

ShouldBindJSON绑定JSON

在这里插入图片描述

paramsRouter.POST("/bind_json", controller.ParamsController.GetBindJson)
// 控制器
func (receiver *params) GetBindJson(c *gin.Context) {type user struct {Name     string `json:"name" binding:"required"`Password uint   `json:"password" binding:"required"`}var u userif err := c.ShouldBindJSON(&u); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if u.Name != "fancy_fish" || u.Password != 123 {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"message": "成功登录"})
}

ShouldBindUri绑定URI

在这里插入图片描述

paramsRouter.GET("/bind_uri/:name/:password", controller.ParamsController.GetBindUri)
func (receiver *params) GetBindUri(c *gin.Context) {type user struct {Name     string `uri:"name" binding:"required"`Password uint   `uri:"password" binding:"required"`}var u userif err := c.ShouldBindUri(&u); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if u.Name != "fancy_fish" || u.Password != 123 {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"message": "成功登录", "data": u})
}

其余的绑定自己查文档体会即可。

参数校验

1.先注册校验器

if v, ok := binding.Validator.Engine().(*validator.Validate); ok {// 这里的 key 和 fn 可以不一样最终在 struct 使用的是 keyv.RegisterValidation("ValidatePassword", utils.ValidatePassword)
}

2.定义校验标签

	type user struct {Name     string `uri:"name" binding:"required"`Password uint   `uri:"password" binding:"required,ValidatePassword"`}

3.定义校验函数

package utils
import ("fmt""github.com/go-playground/validator/v10"
)
var ValidatePassword validator.Func = func(fl validator.FieldLevel) bool {fmt.Println("开启验证")return false
}

路由分组

路由分组可以将相同业务类型的路由划分到一起,便于项目维护和开发。

func main() {// 1.创建路由r := gin.Default()// 路由组v1 ,处理GET请求v1 := r.Group("/v1")// {} 是书写规范{v1.GET("/login", login)  //相当于/v1/loginv1.GET("/submit", submit) //相当于/v1/submit}v2 := r.Group("/v2"){v2.POST("/login", login)  //相当于/v2/loginv2.POST("/submit", submit) //相当于/v2/submit}r.Run(":8000")
}

路由封装

为了提高项目可维护性会将项目结构划分,我们上面的示例都是划分好的。
1.创建router目录管理路由

package routerimport ("gin-project/controller""gin-project/global"
)func InitParamsGroup() {paramsRouter := global.GVB_ENG.Group("/use"){paramsRouter.GET("/uri/:id/:name", controller.ParamsController.GetURI)paramsRouter.GET("/query", controller.ParamsController.GetQuery)paramsRouter.POST("/formdata", controller.ParamsController.GetFormdata)paramsRouter.POST("/file/upload", controller.ParamsController.GetFile)paramsRouter.POST("/files/upload", controller.ParamsController.GetFiles)}
}

2.创建controller抽离控制层

package controllerimport ("fmt""github.com/gin-gonic/gin""path"
)type params struct{}func (receiver *params) GetURI(c *gin.Context) {}
func (receiver *params) GetQuery(c *gin.Context) {}
func (receiver *params) GetFormdata(c *gin.Context) {}
func (receiver *params) GetFile(c *gin.Context) {}
func (receiver *params) GetFiles(c *gin.Context) {}var ParamsController = new(params)

3.创建initialize/router.go初始化路由函数

package initializeimport "gin-project/router"func InitRouter() {router.InitParamsGroup()
}

4.main.go启动服务

	r := gin.Default()if r != nil {initialize.InitRouter()r.Run()}

中间件

gin的中间件和js中koa一样都是使用的洋葱圈模型。
tip:中间件的注册一定在路由之前,否则不会生效。

中间件中使用协程

在Gin框架中,当你在中间件或处理程序(handler)中启动新的Goroutine时,需拷贝上下文对象。应该使用只读副本的原因是为了
这样做的目的是什么?1.避免竞态条件(race condition)2.避免上下文污染(context pollution)。

在Gin框架中,每个请求都有一个独立的上下文(Context),用于存储请求相关的信息和数据。而中间件和处理程序是按顺序执行的,它们可能会在同一个请求中启动多个Goroutine。如果你在Goroutine中直接使用原始的上下文,那么这个Goroutine和处理程序之间就会共享同一个上下文对象。这样一来,如果多个Goroutine同时对上下文进行读写操作,就可能引发竞态条件,导致数据不一致或错误的结果。

当多个Goroutine同时对上下文进行修改时,它们可能会相互影响,导致数据被意外覆盖或混乱。

func main() {r := gin.Default()r.GET("/long_async", func(c *gin.Context) {// 创建在 goroutine 中使用的副本cCp := c.Copy()go func() {// 用 time.Sleep() 模拟一个长任务。time.Sleep(5 * time.Second)// 请注意您使用的是复制的上下文 "cCp",这一点很重要log.Println("Done! in path " + cCp.Request.URL.Path)}()})r.GET("/long_sync", func(c *gin.Context) {// 用 time.Sleep() 模拟一个长任务。time.Sleep(5 * time.Second)// 因为没有使用 goroutine,不需要拷贝上下文log.Println("Done! in path " + c.Request.URL.Path)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(":8080")
}

全局中间件

所有请求都会经过此中间件

package main
import ("fmt""time""github.com/gin-gonic/gin"
)
// 定义中间
func GlobalMiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()fmt.Println("中间件开始执行了")// 设置变量到Context的key中,可以通过Get()取c.Set("request", "中间件")status := c.Writer.Status()fmt.Println("中间件执行完毕", status)t2 := time.Since(t)fmt.Println("time:", t2)}
}func main() {// 1.创建路由r := gin.Default()// 注册中间件r.Use(GlobalMiddleWare()){r.GET("/c", func(c *gin.Context) {// 取值req, _ := c.Get("request")fmt.Println("request:", req)// 页面接收c.JSON(200, gin.H{"request": req})})}r.Run()
}

中间件之间传值

比如有两个中间件A和B ,A执行完B执行,B中间件依赖A中间件的某个数据怎么办呢?看代码

// 定义中间
type AMiddleware struct{}func (receiver AMiddleware) CreateAMiddleware() gin.HandlerFunc {return func(c *gin.Context) {fmt.Println("aaaaaa")c.Set("AKey", "AValue")c.Next()}
}
type BMiddleware struct{}
func (receiver BMiddleware) CreateBMiddleware() gin.HandlerFunc {return func(c *gin.Context) {v, exist := c.Get("AKey")if !exist {fmt.Println("没有传递Avalue")} else {fmt.Println(v)}c.Next()}
}func main() {// 1.创建路由r := gin.Default()// 注册中间件r.Use(AMiddleWare(),BMiddleWare()){r.GET("/c", func(c *gin.Context) {// 取值req, _ := c.Get("request")fmt.Println("request:", req)// 页面接收c.JSON(200, gin.H{"request": req})})}r.Run()
}

gin渲染模版

定义模版

  1. 我们需要在项目根目录下创建template文件夹
  2. 然后配置gin引擎加载
global.GVB_ENG.LoadHTMLGlob("template/**/*")
  1. 之后创建.tmpl文件
    在这里插入图片描述
  2. 添加如下代码
{{ define "header/index.tmpl" }}
<html>
<header><h1>{{ .title }}</h1>
</header>
</html>
{{ end }}

5.控制器,gin.H会将参数传递进去,然后我们可以看到页面。

func (receiver params) GetTemplate(c *gin.Context) {c.HTML(http.StatusOK, "header/index.tmpl", gin.H{"title": "这是传递给模版的参数",})
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

成都市酷客焕学新媒体科技有限公司:实现品牌的更大价值!

成都市酷客焕学新媒体科技有限公司专注于短视频营销&#xff0c;深知短视频在社交媒体中的巨大影响力。该公司巧妙地将品牌信息融入富有创意和趣味性的内容中&#xff0c;使观众在轻松愉悦的氛围中接受并传播这些信息。凭借独特的创意和精准的营销策略&#xff0c;成都市酷客焕…

常用植被物候提取方法 (TIMESATE/R语言/Python)-3.0

文章内容仅用于自己知识学习和分享&#xff0c;如有侵权&#xff0c;还请联系并删除 &#xff1a;&#xff09; 常用植被物候提取方法 (TIMESATE/R语言/Python)-1.0见 link常用植被物候提取方法 (TIMESATE/R语言/Python)-2.0见 link 这里主要介绍一下自己读到的论文&#xff…

element-ui 自定义点击图标/文本/按钮触发el-date-picker时间组件,不使用插槽

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1. 图片预览 2.上代码 2.1html <el-button class"hide_input" size"small"><svg t"1711608996149" class"icon" viewBox"0 0 1024 1024" version"1.1"…

专题:一个自制代码生成器(嵌入式脚本语言)之应用实例

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 专题&#xff1a;一个自制代码…

Day29 集合的常用类

Day29 集合的常用类 文章目录 Day29 集合的常用类一、Collections二、 ConcurrentHashMap三、HashMap vs LinkedHashMap vs Hashtable vs ConcurrentHashMap四、LinkedHashMap五、Properties 一、Collections 1、概念&#xff1a; java.util.Collections是Java集合框架中的一…

使用C++ 20协程实现Raft共识算法

本文描述了如何在不使用任何额外库的情况下在c 20中实现Raft Server共识模块。文章分为三个主要部分: Raft算法的全面概述关于Raft服务器开发的详细说明对基于协程的自定义网络库的描述 该实现利用了C 20的强大功能&#xff0c;特别是协同程序&#xff0c;为构建分布式系统的…

RecyclerView 调用 notifyItemInserted 自动滚动到底部的问题

项目中发现一个奇怪的现象 RecyclerView 加载完数据以后&#xff0c;调用 notifyItemInserted 方法&#xff0c;RecyclerView 会滑动到底部。 简化后的效果图&#xff1a; 因为这个 RecyclerView 的适配器有一个 FootViewHolder&#xff0c;所以怀疑是 FootViewHolder 的问题…

车载以太网AVB交换机 gptp透明时钟 5口 全千兆 SW1500

全千兆车载以太网交换机 一、产品简要分析 5端口千兆车载以太网交换机&#xff0c;包含4个通道的1000BASE-T1接口使用罗森博格H-MTD和泰科MATEnet双接口&#xff0c;1个通道1000BASE-T标准以太网(RJ45接口)&#xff0c;可以实现车载以太网多通道交换&#xff0c;千兆和百兆车载…

GPT-1原理-Improving Language Understanding by Generative Pre-Training

文章目录 前言提出动机模型猜想模型提出模型结构模型参数 模型预训练训练的目标训练方式训练参数预训练数据集预训练疑问点 模型微调模型输入范式模型训练微调建议微调疑问点 实验结果分析 前言 首先想感慨一波 这是当下最流行的大模型的的开篇之作&#xff0c;由OpenAI提出。…

蓝桥杯-卡片换位

solution 有一个测试点没有空格&#xff0c;要特别处理&#xff0c;否则会有一个测试点运行错误&#xff01; 还有输入数据的规模在变&#xff0c;小心顺手敲错了边界条件 #include<iostream> #include<string> #include<queue> #include<map> #incl…

持续集成流程主要系统构成介绍(CI)

目录 一、概述 二、版本控制系统 2.1 概述 2.2 版本控制系统使用流程示意图 2.3 版本控制软件划分 2.3.1 集中式版本控制软件 2.3.2 分布式版本控制软件 2.3.3 总结 2.4 常用版本控制软件介绍 三、编译构建系统 3.1 概述 3.2 编译构建流程示意图 3.3 列举Java 源码…

Kafka重要配置参数全面解读(重要)

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Kafka重要配置参数全面解读(重要 前言auto.create.topics.enableauto.leader.rebalance.enablelog.retention.{hour|minutes|ms}offsets.topic.num.partitions 和 offsets.topic.replication.factorlo…

回文数个数-第12届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第43讲。 回文数个数&#…

macOS Sonoma如何查看隐藏文件

在使用Git进行项目版本控制时&#xff0c;我们可能会遇到一些隐藏文件&#xff0c;比如.gitkeep文件。它通常出现在Git项目的子目录中&#xff0c;主要作用是确保空目录也可以被跟踪。 终端命令 在尝试查看.gitkeep文件时&#xff0c;使用Terminal命令来显示隐藏文件 default…

推动制药行业数字化转型:基于超融合架构的MES一体机解决方案

随着中国对信息化重视程度的不断加深&#xff0c;制药行业作为国民经济的重要支柱之一&#xff0c;也在积极寻求通过数字化手段提升产业效率与产品质量。自党的十六大提出“以信息化带动工业化”的战略以来&#xff0c;制药业的这一转型探索尤为迫切。 在现代制药生产中&#…

C# OpenCv Haar、LBP 人脸检测

目录 效果 代码 下载 效果 代码 using OpenCvSharp;namespace OPenCVDemo {class Program{static void Main(string[] args){// Load the cascadesvar haarCascade new CascadeClassifier("haarcascade_frontalface_default.xml");var lbpCascade new Casca…

CSS(六)

一、精灵图 1.1 为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 因此&#xff0c;为了有效…

国外的Java面试题和国内的相比谁更卷

前言 有很多朋友很好奇国外的Java面试题长啥样&#xff0c;今天我们就去找5道国外的面试来和国内的对比一下看看谁难一些&#xff01; 面试题分享 1. Is Java Platform Independent if then how?&#xff08; Java平台是独立的吗&#xff1f;&#xff09; Yes, Java is a…

【氮化镓】位错对氮化镓(GaN)电子能量损失谱(EEL)的影响

本文献《Influence of dislocations on electron energy-loss spectra in gallium nitride》由C. J. Fall等人撰写&#xff0c;发表于2002年。研究团队通过第一性原理计算&#xff0c;探讨了位错对氮化镓&#xff08;GaN&#xff09;电子能量损失谱&#xff08;EEL&#xff09;…

大话设计模式之迪米特法则

迪米特法则&#xff0c;也称为最少知识原则&#xff08;Law of Demeter&#xff09;&#xff0c;是面向对象设计中的一个重要原则&#xff0c;其核心思想是降低耦合度、减少对象之间的依赖关系&#xff0c;从而使系统更加灵活、易于维护和扩展。 根据迪米特法则&#xff0c;一…