【Gin】Gin框架介绍和使用

一、简单使用Gin框架搭建一个服务器

package main

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

func main() {
    // 创建一个默认的路由引擎
    r := gin.Default()
    // GET 请求方法
    r.GET("/hello", func(c *gin.Context) {
        // c.JSON 返回的是JSON格式的数据
        c.JSON(200, gin.H{
            "message" : "hello world",
        })
    }
    // 启动HTTP服务器,默认在 0.0.0.0:8080 启动
    r.Run()
}

       这里,我们只需要记住一下几个函数就可以快速启动一个服务器:还有记住就是一个请求对应一个响应。世界是一个巨大的IO。

gin.Default()  // 创建一个默认的路由引擎

GET("/路由", func(c *gin.Context) {}

c.JSON(状态码, gin.H{})

r.Run() // 启动HTTP服务器

二、RESTful API

       REST 与技术无关,代表的是一种软件架构风格,简单地说,REST的含义就是客户端与服务器之间进行交互的时候,使用HTTP协议中的4种请求方法代表不同的动作。

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源

       只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互的。

我们可以来看一看下面的代码:

func main() {
	r := gin.Default()
	r.GET("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "GET",
		})
	})

	r.POST("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "POST",
		})
	})

	r.PUT("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "DELETE",
		})
	})
}

三、Gin获取参数

前三个进行获取参数的函数都有三种方法,且函数的格式基本一样:

3.1 获取 querystring 参数

       querystring 指的是 URL中 ? 后面携带的参数:/user/search?username=小王子&address=沙河,获取请求的 querystring 参数的方法如下:

func main() {
	//Default返回一个默认的路由引擎
	r := gin.Default()
	r.GET("/user/search", func(c *gin.Context) {
		username := c.DefaultQuery("username", "小王子")
		//username := c.Query("username")
		address := c.Query("address")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})
	r.Run()
}

c.Query()

c.DefaultQuery()

       我之前写的一个项目就是需要处理这种URL,不过是在C++上处理,需要将URL获取到,然后进行字符串的切割。

3.2 获取 form 参数

       当前端请求的数据是通过form表单提交的时候,例如向 /user/search 发一个POST请求,获取请求数据的方式如下:

func main() {
	//Default返回一个默认的路由引擎
	r := gin.Default()
	r.POST("/user/search", func(c *gin.Context) {
		// DefaultPostForm取不到值时会返回指定的默认值
		//username := c.DefaultPostForm("username", "小王子")
		username := c.PostForm("username")
		address := c.PostForm("address")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})
	r.Run(":8080")
}

c.PostForm()

c.DefaultPostForm()

3.3 获取 JSON 参数

       当前端请求的数据通过JSON提交的时候,例如向 /json 发送一个JSON格式的POST请求,则请求参数的方式如下:

r.POST("/json", func(c *gin.Context) {
	// 注意:下面为了举例子方便,暂时忽略了错误处理
	b, _ := c.GetRawData()  // 从c.Request.Body读取请求数据
	// 定义map或结构体
	var m map[string]interface{}
	// 反序列化
	_ = json.Unmarshal(b, &m)

	c.JSON(http.StatusOK, m)
})

3.4 获取 path 参数

       请求的参数通过URL路径传递,例如:/user/search/小王子/沙河。获取请求URL路径中的参数的方式如下:

func main() {
	//Default返回一个默认的路由引擎
	r := gin.Default()
	r.GET("/user/search/:username/:address", func(c *gin.Context) {
		username := c.Param("username")
		address := c.Param("address")
		//输出json结果给调用方
		c.JSON(http.StatusOK, gin.H{
			"message":  "ok",
			"username": username,
			"address":  address,
		})
	})

	r.Run(":8080")
}

c.Param()

c.DefaultParam()

3.5 参数绑定

       为了可以更方便的获取请求相关参数,提高开发效率,我们可以基于请求的 Content-Type 识别请求数据类型并利用反射机制自动提取请求中 QueryString、form 表单、JSON、XML等参数到结构体中,我们可以使用 .ShouldBind() 函数来进行强大的功能,他能够基于请求自动提取 JSON、form表单和QueryString 类型的数据,并把值绑定到指定的结构体对象。

// Binding from JSON
type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

func main() {
	router := gin.Default()

	// 绑定JSON的示例 ({"user": "q1mi", "password": "123456"})
	router.POST("/loginJSON", func(c *gin.Context) {
		var login Login

		if err := c.ShouldBind(&login); err == nil {
			fmt.Printf("login info:%#v\n", login)
			c.JSON(http.StatusOK, gin.H{
				"user":     login.User,
				"password": login.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	// 绑定form表单示例 (user=q1mi&password=123456)
	router.POST("/loginForm", func(c *gin.Context) {
		var login Login
		// ShouldBind()会根据请求的Content-Type自行选择绑定器
		if err := c.ShouldBind(&login); err == nil {
			c.JSON(http.StatusOK, gin.H{
				"user":     login.User,
				"password": login.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	// 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)
	router.GET("/loginForm", func(c *gin.Context) {
		var login Login
		// ShouldBind()会根据请求的Content-Type自行选择绑定器
		if err := c.ShouldBind(&login); err == nil {
			c.JSON(http.StatusOK, gin.H{
				"user":     login.User,
				"password": login.Password,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	// Listen and serve on 0.0.0.0:8080
	router.Run(":8080")
}

ShouldBind 会按照下面的顺序解析请求中的数据进行绑定:

  1. 如果是 GET 请求,只使用 Form 绑定引擎(query)。
  2. 如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用Form(form-data)

四、文件上传

4.1 单个文件上传

文件上传前端页面代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>上传文件示例</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="f1">
    <input type="submit" value="上传">
</form>
</body>
</html>

后端 gin 框架部分代码:

func main() {
	router := gin.Default()
	// 处理multipart forms提交文件时默认的内存限制是32 MiB
	// 可以通过下面的方式修改
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// 单个文件
		file, err := c.FormFile("f1")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"message": err.Error(),
			})
			return
		}

		log.Println(file.Filename)
		dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
		c.JSON(http.StatusOK, gin.H{
			"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
		})
	})
	router.Run()
}

4.2 多个文件上传

五、重定向

5.1 HTTP重定向

r.GET("/test", func(c *gin.Context) {
	c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})

5.2 路由重定向

r.GET("/test", func(c *gin.Context) {
    // 指定重定向的URL
    c.Request.URL.Path = "/test2"
    r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"hello": "world"})
})

六、Gin路由

6.1 普通路由

6.2 路由组

七、Gin中间件

       我感觉这个中间件有点像C++回调函数。Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

7.1 定义中间件

Gin中的中间件必须是一个 gin.HandlerFunc 类型的。

// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
		// 调用该请求的剩余处理程序
		c.Next()
		// 不调用该请求的剩余处理程序
		// c.Abort()
		// 计算耗时
		cost := time.Since(start)
		log.Println(cost)
	}
}

7.2 注册中间件

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// 注册一个全局中间件
	r.Use(StatCost())
	
	r.GET("/test", func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
	r.Run()
}
// 给/test2路由单独注册中间件(可注册多个)
	r.GET("/test2", StatCost(), func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
shopGroup := r.Group("/shop", StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

7.3 中间件注意事项

7.3.1 gin 默认中间件

gin.Default()默认使用了LoggerRecovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由

7.3.2 gin 中间件中使用 goroutine 

       当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

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

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

相关文章

Java应用程序的测试覆盖率之设计与实现(二)-- jacoco agent

说在前面的话 要想获得测试覆盖率报告&#xff0c;第一步要做的是&#xff0c;采集覆盖率数据&#xff0c;并输入到tcp。 而本文便是介绍一种java应用程序部署下的推荐方式。 作为一种通用方案&#xff0c;首先不想对应用程序有所侵入&#xff0c;其次运维和管理方便。 正好…

高级的SQL查询技巧有哪些?

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于高级SQL查询技巧方面的相关内容&#xf…

协程必知必会-系列4-协程本地变量

文章目录 协程本地变量相关结构体实现原理代码实现代码示例思考题 协程本地变量 在上一篇文章中&#xff0c;我们介绍了如何通过协程来实现批量并发执行&#xff0c;本篇文章将向大家介绍如何在协程的基础之上&#xff0c;实现协程本地变量。 注意&#xff1a;「为了减轻大家…

Docker基础部署

一、安装Ubuntu系统 1.1 新建虚拟机 打开VMware Workstation&#xff0c;选择文件->新建虚拟机->典型&#xff08;推荐T&#xff09;->安装程序光盘映像文件->输入虚拟的名字->一直下一步即可 安装程序光盘映像文件 注意&#xff1a;选择CentOS-7-x86_64-DVD-…

Springboot 使用EasyExcel导出Excel文件

Springboot 使用EasyExcel导出Excel文件 Excel导出系列目录&#xff1a;引入依赖创建导出模板类创建图片转化器 逻辑处理controllerservice 导出效果遗留问题 Excel导出系列目录&#xff1a; 【Springboot 使用EasyExcel导出Excel文件】 【Springboot 使用POI导出Excel文件】 …

大模型带来新安全机遇

当前网络空间安全面临攻击隐蔽难发现、数据泄露风险高和违法信息审核难等挑战。大模型展现出强大的信息理解、知识抽取、意图和任务编排等能力&#xff0c;为网络空间安全瓶颈问题提供了新的解决思路和方法。与此同时&#xff0c;大模型发展也催生了恶意软件自动生成、深度伪造…

vue 项目i18n国际化,快速抽离中文,快速翻译

国际化大家都知道vue-i18n 实现的&#xff0c;但是有个问题&#xff0c;就是繁杂的抽离中文字符的过程&#xff0c;以及翻译中文字符的过程&#xff0c;关于这个有些小工具可以希望可以帮到大家 1.安装vue-i18n npm i vue-i18n8.22.22.ElementUI多语言配置 在src目录下创建…

《Python基础教程》笔记(ch0-1)

前言 在Python生态系统中&#xff0c;各种包轮番登场&#xff0c;各种编码实践大行其道后又日渐式微。 引言 Python是什么&#xff1f;为何要使用它&#xff1f;官方宣传说&#xff1a;Python是一种面向对象的解释性高级编程语言&#xff0c;具有动态语义。 这句话的要点在…

Java网络编程-简单的API调用

Get请求 - 无参数 安装依赖库 首先需要安装一个库&#xff1a; Okhttp3&#xff0c;这是一个非常流行的 HTTP 库&#xff0c;可以简单、快速的实现 HTTP 调用。 安装 Okhttp3 的方式是在 pom.xml 文件中增加依赖&#xff1a; <!-- https://mvnrepository.com/artifact/c…

08 实战:色彩空间展示(本程序以视频为主)

程序效果如下: 我在这里讲解RGB和YCbCr的原理: 一、RGB颜色空间 1.1 基本概念 RGB颜色空间是一种最基础和常用的颜色表示方式,它基于人眼感知色彩的三原色原理。RGB分别代表: R(Red):红色G(Green):绿色B(Blue):蓝色通过这三种基本颜色的不同组合,可以产生人眼…

计算机毕业设计Spark+大模型动漫推荐系统 动漫视频推荐系统 漫画分析可视化大屏 漫画爬虫 漫画推荐系统 漫画爬虫 知识图谱 大数据

《Spark大模型动漫推荐系统》开题报告与任务书 一、引言 随着互联网技术的飞速发展&#xff0c;动漫产业的数据量急剧增长。用户面临着海量动漫作品的选择难题&#xff0c;如何从这些数据中高效地提取有价值的信息&#xff0c;为用户推荐符合其喜好的动漫作品&#xff0c;成为…

如何快速生成大量有意义的测试数据?

如何获取 MySQL 的测试数据&#xff0c;这是个很经典的问题&#xff0c;在开发、测试和性能优化的各个环节中&#xff0c;获取合适的测试数据都是必不可少的。MySQL 官方还特地提供了示例库 employees&#xff0c;用于测试用途&#xff0c;但 employees 并不是万能的&#xff0…

为您的 WordPress 网站打造完美广告布局 A5广告单元格插件

一个为 WordPress 网站量身定制的强大工具,它将彻底改变您展示广告的方式 灵活多变的布局设计 A5 广告单元格插件的核心优势在于其无与伦比的灵活性。无论您是想要创建整齐的网格布局,还是希望打造独特的不规则设计,这款插件都能满足您的需求。 自定义网格数量&#xff1a;从 2…

vue 页面导出gif图片 img 导出gif 超简单~ 可修改播放速度

1.首先需要新建一个文件件 新建gif文件夹。这两个文件在文章最后面需要可自提 2.出gif分为两种情况 第一种情况 页面是img标签&#xff0c;直接导出图片作为gif 第二种情况 页面是div标签&#xff0c;需要导出div里面的图片作为gif 2.1页面是img标签&#xff0c;直接导出图…

博弈论:博弈类型空间集合;三层博弈拓展式;

目录 博弈论:博弈类型空间集合 θ(Dss-1=1 )就是博弈类型空间集合; 一、博弈的基本要素 二、博弈的主要类型 三、博弈类型空间集合的构建 三层博弈拓展式: 博弈论:博弈类型空间集合 这的博弈类型空间集合:指一方选择的策略,用符号进行表达:SDss-2(θDss-1=1) = …

手机玩使命召唤21:黑色行动6?GameViewer远程玩使命召唤教程

使命召唤21&#xff1a;黑色行动 6这个第一人称射击游戏&#xff0c;将于10月25号上线&#xff01;如果你是使命召唤的老玩家&#xff0c;是不是也在期待这部新作&#xff1f;其实这个游戏不仅可以用电脑玩&#xff0c;还可以用手机玩&#xff0c;使用网易GameViewer远程就能让…

Golang | Leetcode Golang题解之第502题IPO

题目&#xff1a; 题解&#xff1a; func findMaximizedCapital(k, w int, profits, capital []int) int {n : len(profits)type pair struct{ c, p int }arr : make([]pair, n)for i, p : range profits {arr[i] pair{capital[i], p}}sort.Slice(arr, func(i, j int) bool {…

FileLink跨网文件摆渡系统:重构跨网文件传输新时代

在数字化浪潮的推动下&#xff0c;企业对于数据的高效利用和安全管理提出了前所未有的要求。面对不同网络环境间的文件传输难题&#xff0c;传统方法往往显得力不从心&#xff0c;不仅效率低下&#xff0c;还存在极大的安全隐患。而FileLink跨网文件摆渡系统的出现&#xff0c;…

http服务器的实现和性能测试

http服务器的实现 本文使用上一篇博文实现的epollreactor百万并发的服务器实现了一个使用http协议和WebSocket协议的WebServer。 完整代码请看我的github项目 1. 水平触发(Level Trigger)与边沿触发(Edge Trigger) 1.1 水平触发 水平触发是一种状态驱动机制。当文件描述符&a…

【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】

【学习AI-相关路程-mnist手写数字分类-python-硬件&#xff1a;jetson orin NX-自我学习AI-基础知识铺垫-遇到问题&#xff08;1&#xff09; 】 1、前言2、先行了解&#xff08;1&#xff09;学习基础知识-了解jetson orin nx 设备&#xff08;2&#xff09;学习python&AI…