Golang-Gin光速入门

安装

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

初始化项目并启动服务

go mod init gin-project

package main

import "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 router

import (
	"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 initialize

import "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 Person
	if 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 Person

	p.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   string
		Name 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   string
		Name 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 user
	if 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 user
	if 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 使用的是 key
	v.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/login
      v1.GET("/submit", submit) //相当于/v1/submit
   }
   v2 := r.Group("/v2")
   {
      v2.POST("/login", login)  //相当于/v2/login
      v2.POST("/submit", submit) //相当于/v2/submit
   }
   r.Run(":8000")
}

路由封装

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

package router

import (
	"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 controller

import (
	"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 initialize

import "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://www.mfbz.cn/a/497871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2.11 Python关键字(保留字)

Python关键字&#xff08;保留字&#xff09;一览表 保留字是Python 语言中一些已经被赋予特定意义的单词&#xff0c;这就要求开发者在开发程序时&#xff0c;不能用这些保留字作为标识符给变量、函数、类、模板以及其他对象命名。 Python 包含的保留字可以执行如下命令进行…

GRE VPN——配置实验

1&#xff0c;按照图示配置IP地址 r1&#xff1a; r2&#xff1a; r3&#xff1a; 2&#xff0c;在R1和R3配置默认路由使公网区域互通 [R1]ip route-static 0.0.0.0 0 100.1.1.2 R3]ip route-static 0.0.0.0 0 100.2.2.2 3&#xff0c;在R1和R3上配置GRE VPN&#xff0c;使…

追踪Aurora(欧若拉)勒索病毒,Emsisoft更新解密工具

Aurora(欧若拉)勒索病毒首次出现于2018年7月左右&#xff0c;加密后的文件后缀为Aurora&#xff0c;2018年11月&#xff0c;此勒索病毒的一款变种样本&#xff0c;加密后的文件后缀为Zorro&#xff0c;同时发现了此勒索病毒的一个BTC钱包地址&#xff1a; 18sj1xr86c3YHK44Mj2…

第二证券今日投资参考:低空经济迎利好 自动驾驶商业化提速

昨日&#xff0c;两市股指盘中弱势震动&#xff0c;午后加快下探&#xff0c;沪指失守3000点大关&#xff0c;深成指、创业板指跌超2%&#xff1b;到收盘&#xff0c;沪指跌1.26%报2993.14点&#xff0c;深成指跌2.4%报9222.47点&#xff0c;创业板指跌2.81%报1789.82点&#x…

RabbitMQ 实验消费原始队列消息, 拒绝(reject)投递死信交换机过程

如果你想通过 RabbitMQ 的死信队列功能实现消费者拒绝消息投递到死信交换机的行为&#xff0c;你可以按照以下步骤操作&#xff1a; 创建原始队列&#xff0c;并将其绑定到一个交换机上&#xff1a; export RABBITMQ_SERVER127.0.0.1 export RABBITMQ_PORT5672 export RAB…

小米汽车正式发布:开启智能电动新篇章

随着科技的不断进步&#xff0c;汽车产业正经历着前所未有的变革。智能电动汽车作为这一变革的重要方向&#xff0c;正吸引着越来越多的目光。在这个充满机遇和挑战的时代&#xff0c;小米汽车凭借其卓越的技术实力和深厚的市场底蕴&#xff0c;终于迈出了坚实的一步。今天&…

微服务demo(三)nacosfeign

一、feign使用 1、集成方法 1.1、pom consumer添加依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.6.RELEASE</version></dependency&…

3.28总结

1.java学习记录 1.方法的重载 重载换而言之其实就是函数名不变&#xff0c;但是其中的参数需要改变&#xff0c;可以三个方面改变&#xff08;参数类型&#xff0c;参数顺序&#xff0c;参数个数这三个方面入手&#xff0c;这样可以运用的&#xff09; 但是&#xff1a;注意…

深度学习论文: Attention is All You Need及其PyTorch实现

深度学习论文: Attention is All You Need及其PyTorch实现 Attention is All You Need PDF:https://arxiv.org/abs/1706.03762.pdf PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks 大多数先进的神经序列转换模型采用编码器-解码器结构&#xff0c;其中编码器将…

IP种子是什么?理解和应用

在网络世界中&#xff0c;IP种子是一个广泛应用于文件共享和网络下载领域的概念。它是一种特殊的标识符&#xff0c;用于识别和连接到基于对等网络&#xff08;P2P&#xff09;协议的文件共享网络中的用户或节点。本文将深入探讨IP种子的含义、作用以及其在网络中的应用。 IP地…

Windows 最佳文件管理器:快速、简单、直观、自由 | 开源日报 No.175

files-community/Files Stars: 30.6k License: MIT Files 是为 Windows 构建的最佳文件管理器应用程序。该项目解决了在 Windows 上进行文件管理时的困难。 它具有以下主要功能和优势&#xff1a; 采用直观设计&#xff0c;使浏览文件变得更加简单支持标签、预览和自定义背景…

OceanBase OBCA 数据库认证专员考证视频

培训概述 OceanBase 认证是 OceanBase 官方推出的唯一人才能力认证体系&#xff0c;代表了阿里巴巴及蚂蚁集团官方对考生关于 OceanBase 技术能力的认可&#xff0c;旨在帮助考生更好地学习 OceanBase 数据库产品&#xff0c;早日融入 OceanBase 技术生态体系&#xff0c;通过由…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

3.Labview字符串与路径精讲(下) — 字符串及路径的用法汇总

本章讲解labview中的字符串和路径具体实践用例&#xff0c;从前面板字符串属性到后面板字符串函数应用做出详细概述&#xff0c;通过本文的学习希望大家了解到字符串及路径在labview编程中的重要地位。 本系列文章为labview 从基础到强化到精通的学习文章&#xff0c;大家可以随…

网站为什么要选择使用安全加速SCDN?

安全加速SCDN&#xff08;安全内容交付网络&#xff09;是一种网络加速服务&#xff0c;旨在提高网站和应用程序的性能和安全性。它使用专门的技术和基础设施来加速内容传输并保护网站免受网络攻击。 安全加速SCDN可以通过内容缓存、快速传输和动态路由技术来加速网站和应用程…

使用Jenkins打包时执行失败,但手动执行没有问题如ERR_ELECTRON_BUILDER_CANNOT_EXECUTE

具体错误信息如&#xff1a; Error output: Plugin not found, cannot call UAC::_ Error in macro _UAC_MakeLL_Cmp on macroline 2 Error in macro _UAC_IsInnerInstance on macroline 1 Error in macro _If on macroline 9 Error in macro FUNCTION_INSTALL_MODE_PAGE_FUNC…

LeetCode:718最长重复子数组 C语言

718. 最长重复子数组 提示 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,…

钡铼技术R40路由器助力智能船舶航行数据实时传输与分析

钡铼技术R40路由器在智能船舶领域的应用&#xff0c;对于航行数据的实时传输与分析具有重要意义。随着航运业的不断发展和智能化水平的提升&#xff0c;船舶航行数据的及时传输和有效分析对船舶的安全、运营效率等方面至关重要。而引入钡铼技术R40路由器&#xff0c;则可以实现…

k8s1.28.8版本配置prometheus监控告警

文章目录 官方架构图组件的具体介绍kube-prometheus包含的组件简介&#xff1a;文件存储路径&#xff1a; 结构分析官网自带的一些规则自己总结流程 1-创建规则磁盘使用率报警规则 详解上面rule流程Alertmanagerg查看 2-报警接收器2.1-邮件报警修改Alertmanager配置查看现有的s…

全局UI方法-弹窗六-自定义弹窗

1、描述 通过CustomDialogController类显示自定义弹窗。使用弹窗组件时&#xff0c;可优先考虑自定义弹窗&#xff0c;便于自定义弹窗的样式与内容。 2、接口 CustomDialogController(value:{builder: CustomDialog, cancel?: () > void, autoCancel?: boolean, alignme…
最新文章