企业管理系统定制开发Golang gin框架

gin学习

一、Gin 介绍

  • Gin 是一个 Go (Golang) 企业管理系统定制开发编写的轻量级 http web 框架,企业管理系统定制开发运行速度非常快,企业管理系统定制开发如果你是性能和高效的追求者
  • Gin 企业管理系统定制开发最擅长的就是 Api 企业管理系统定制开发接口的高并发,企业管理系统定制开发如果项目的规模不大,企业管理系统定制开发业务相对简单,企业管理系统定制开发这个时候我们也推荐您使用 Gin
  • Gin 的官网:
  • Github 地址:

二、Gin 环境搭建

  • 要安装 Gin 软件包,企业管理系统定制开发需要先安装 Go 并设置 Go 工作区(demo)
  1. 企业管理系统定制开发下载并安装 gin
    $ go get -u github.com/gin-gonic/gin
  2. 将 gin 企业管理系统定制开发引入到代码中
    import "github.com/gin-gonic/gin"
  3. (可选)企业管理系统定制开发如果使用诸如 http.StatusOK 企业管理系统定制开发之类的常量,企业管理系统定制开发则需要引入 net/http 包:
    import "net/http"
  4. 进入项目demo根目录, 第三方包用mod.mod 管理
    $ go mod init demo
  5. 新建 main.go 配置路由
package mainimport (	"github.com/gin-gonic/gin")func main() {	// 创建一个默认的路由引擎	r := gin.Default()	// 配置路由	r.GET("/", func(c *gin.Context) {		c.JSON(200, gin.H{			"username": "name1",			"data": "data1",		})	})	// 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务	r.Run()}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  1. 运行你的项目
    $ go run main.go
  2. 可改变默认启动端口
    `r.Run(":9000")

三、golang 程序的热加载

  • 所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中 是非常便利的,可以快速进行代码测试,省去了每次手动重新编译
  • 工具一:
  • 安装fresh

在GOPATH下bin目录下有fresh.exe,没有的话,就是安装失败

$cd demo$go get github.com/pilu/fresh$fresh
  • 1
  • 2
  • 3

其中:
当运行fresh出现下列错误的时候的解决办法

fresh : 无法将“fresh”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。所在位置 行:1 字符: 1+ fresh+ ~~~~~    + CategoryInfo          : ObjectNotFound: (fresh:String) [], CommandNotFoundException    + FullyQualifiedErrorId : CommandNotFoundException
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 手动下载fresh包
    git clone https://github.com/gravityblast/fresh.git
  2. 安装fresh
    go install github.com/pilu/fresh@latest
  3. 在GOPATH下bin目录下有fresh.exe 如图
  • 工具二:
    命令:
    go get github.com/codegangsta/gin
    gin run main.go

四、Gin 框架中的路由

4.1 路由概述

路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问

  • RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。
  • 在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
请求资源说明
GET (SELECT)从服务器取资源
POST(CREATE)新建资源
PUT (UPDATE)更新资源
DELETE删除资源

4.2 简单的路由配置

  • 简单的路由配置(可以通过 postman 测试)
  • 当用 GET 请求访问一个网址的时候,做什么事情:
r.GET("/", func(c *gin.Context) {	c.String(200, "hello world")})
  • 1
  • 2
  • 3
  • 路由里面获取 Get 传值, 域名/news?aid=20
	r.GET("/news", func(c *gin.Context) {		aid := c.Query("aid")		c.JSON(200, gin.H{			"news": aid, // aid = 20		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中:H 为 map[string]interface{}类型

4.3 动态路由

  • 域名/user/20
	r.GET("/user/:id", func(c *gin.Context) {		id := c.Param("id")		c.JSON(200, gin.H{			"news": id, // id 20		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.4 c.String() c.JSON() c.JSONP() c.XML() c.HTML()

4.4.1 返回一个字符串

	r.GET("/news", func(c *gin.Context) {		aid := c.Query("aid")		c.String(200, "string")	})
  • 1
  • 2
  • 3
  • 4

4.4.2 返回一个 JSON 数据

	r.GET("/json", func(c *gin.Context) {		aid := c.Query("aid")		// 方式一:自己拼接 JSON		c.JSON(http.StatusOK, gin.H{			"msg": "JSON"		})	})	r.GET("/structJson", func(c *gin.Context) {		// 结构体方式		var msg struct {			Username string `json:"username"`			Msg string `json:"msg"`			Age string `json:"age"`		}		msg.Username = "name1"		msg.Msg = "msg1"		msg.Age = "18"		c.JSON(200, msg)	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4.4.3 返回JSOPN

	r.GET("/JSONP", func(c *gin.Context) { 	data := map[string]interface{}{		"foo": "bar",	}	// /JSONP?callback=x	// 将输出:x({\"foo\":\"bar\"}) 	c.JSONP(http.StatusOK, data)})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.4.4 渲染模板

	r.GET("/", func(c *gin.Context) {		c.HTML(		http.StatusOK, "default/index.html",		map[string]interface{}{ "title": "前台首页"		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

五、Gin HTML 模板渲染

5.1 全部模板放在一个目录里面的配置方法

  1. 我们首先在项目根目录新建 templates 文件夹,然后在文件夹中新建 index.html
<!DOCTYPE  html><html  lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h1>这是一个 html 模板</h1><h3>{{.title}}</h3></body></html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. Gin 框架中使用 c.HTML 可以渲染模板, 渲染模板前需要使用 LoadHTMLGlob() 或者LoadHTMLFiles()方法加载模板。
	r.GET("/", func(c *gin.Context) {		c.HTML(http.StatusOK,			"default/index.html",			map[string]interface{}{"title": "前台首页"})	})	r.GET("/", func(c *gin.Context) {		c.HTML(http.StatusOK, "index.html", gin.H{			"title": "Main website",		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.2 模板放在不同目录里面的配置方法

5.3 gin 模板基本语法

六、静态文件服务

  • 当我们渲染的 HTML 文件中引用了静态文件时,我们需要配置静态 web 服务
    r.Static("/static", “./static”) 前面的/static 表示路由 后面的./static 表示路径
	r := gin.Default()	r.Static("/static", "./static")	r.LoadHTMLGlob("templates/**/*")
  • 1
  • 2
  • 3

七、路由详解

  • 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。
  • 前面章节我们给大家介绍了路由基础以及路由配置,这里我们详细给大家讲讲路由传值、路 由返回值

7.1 GET POST 以及获取 Get Post 传值

7.1.1 Get 请求传值

GET /user?uid=20&page=1

	r.GET("/user", func(c *gin.Context) { 		uid := c.Query("uid")		page := c.DefaultQuery("page", "0") 		c.String(200, "uid=%v page=%v", uid, page)	})
  • 1
  • 2
  • 3
  • 4
  • 5

7.1.2 动态路由传值

get /user/20

	r.GET("/user/:id", func(c *gin.Context) {		id := c.Param("id")		c.JSON(200, gin.H{			"news": id, // id 20		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7.1.3 Post 请求传值 获取 form 表单数据

  • 通过 c.PostForm 接收表单传过来的数据
  • postman进行数据提交
	r.POST("/doAddUser", func(c *gin.Context) {		username := c.PostForm("username")		password := c.PostForm("password")		age := c.DefaultPostForm("age", "20")		c.JSON(200, gin.H{			"usernmae": username, "password": password, "age": age,		})	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

7.1.4 Get 传值绑定到结构体

/?username=zhangsan&password=123456

type Userinfo struct {	Username string `form:"username" json:"user"`	Password string `form:"password" json:"password"`}func main() {	// 创建一个默认的路由引擎	r := gin.Default()	// 配置路由	r.GET("/", func(c *gin.Context) {		var userinfo Userinfo		if err := c.ShouldBind(&userinfo); err == nil {			c.JSON(http.StatusOK, userinfo)		} else {			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})		}	})}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 返回数据
    {"user":"zhangsan","password":"123456"}

7.1.5 Post 传值绑定到结构体

	r.POST("/doLogin", func(c *gin.Context) {		var userinfo Userinfo		if err := c.ShouldBind(&userinfo); err == nil {			c.JSON(http.StatusOK, userinfo)		} else {			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})		}	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

7.1.6 获取 Post Xml 数据

  1. 通过ctx.ShouldBindXML获取xml中的数据
<?xml version="1.0" encoding="UTF-8"?><article>	<content type="string">我是张三</content>	<title type="string">张三</title></article>
  • 1
  • 2
  • 3
  • 4
  • 5
type Article struct {	Title   string `json:"title" xml:"title"`	Content string `json:"content" xml:"content"`}	r.POST("/xml", func(ctx *gin.Context) {		var article Article		if err := ctx.ShouldBindXML(&article); err == nil {			ctx.JSON(http.StatusOK, article)		}else {			ctx.JSON(http.StatusBadRequest, gin.H {				"err": err.Error()})		}	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  1. 通过ctx.GetRawData()获取数据

7.2 路由分组

	r := gin.Default()	apiRouter := r.Group("/api")	{		apiRouter.GET("/", func(ctx *gin.Context) {			ctx.String(200, "api")		})		apiRouter.GET("articles", func(ctx *gin.Context) {			ctx.String(200, "/api/articles")		})	}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

7.3 路由分离

  • 在项目新建文件夹router,然后在router目录下创建apiRouter.goadminRouter.go,内容如下:
package router// apiRouter.goimport "github.com/gin-gonic/gin"func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		apiRouter.GET("/", func(ctx *gin.Context) {			ctx.String(200, "api")		})		apiRouter.GET("articles", func(ctx *gin.Context) {			ctx.String(200, "/api/articles")		})	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
package router// adminRouter.goimport (	"net/http"	"github.com/gin-gonic/gin")func AdminRouter(r *gin.Engine) {	adminRouter := r.Group("/admin")	{		adminRouter.GET("/", func(ctx *gin.Context) {			ctx.String(200, "admin")		})		adminRouter.GET("list", func(ctx *gin.Context) {			ctx.String(http.StatusOK, "admin/list")		})	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • main.go中引入路由模块
package mainimport (	"socket/router"	"github.com/gin-gonic/gin")func main() {	// 创建一个默认的路由引擎	r := gin.Default()	// 引入路由模块	router.AdminRouter(r)	router.ApiRouter(r)	// 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务	r.Run()}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

八、Gin 中自定义控制器

8.1 控制器分组

  • 当我们的项目比较大的时候有必要对我们的控制器进行分组 , 业务逻辑放在控制器中
  • 在项目文件夹下面新建controller\api\文件夹,创建userController.go
mkdir controllercd controllermkdir api
  • 1
  • 2
  • 3
  • userController.go 内容
package apiimport "github.com/gin-gonic/gin"func UserIndex(c *gin.Context) {	c.String(200, "api UserIndex")}func UserAdd(c *gin.Context)  {	c.String(200, "api UserAdd")}func UserList(c *gin.Context) {	c.String(200, "api UserList")}func UserDelete(c *gin.Context) {	c.String(200, "api UserDelete")}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • apiRouter.go中调用userController.go 的函数
import (	"socket/controller/api"	"github.com/gin-gonic/gin")func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		apiRouter.GET("/", 		)		apiRouter.GET("users", api.UserIndex)		apiRouter.GET("users/:id", api.UserList)		apiRouter.POST("users", api.UserAdd)		apiRouter.PUT("users/:id", api.UserUpdate)		apiRouter.DELETE("users/:id", api.UserDelete)	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 为了使方法能够继承,我们修改userController.go 内容
package apiimport "github.com/gin-gonic/gin"type UserController struct {}func (con UserController) Index(c *gin.Context) {	c.String(200, "api Index")}func (con UserController) Add(c *gin.Context)  {	c.String(200, "api Add")} func (con UserController) List(c *gin.Context) {	c.String(200, "api List")}func (con UserController) Update(c *gin.Context) {	c.String(200, "api update")}func (con UserController) Delete(c *gin.Context) {	c.String(200, "api Delete")}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 继续修改apiRouter.go
func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		apiRouter.GET("/")		// api.UserController{} 实例化后在可以使用结构体的方法		apiRouter.GET("users", api.UserController{}.List)		apiRouter.GET("users/:id", api.UserController{}.List)		apiRouter.POST("users", api.UserController{}.Add)		apiRouter.PUT("users/:id", api.UserController{}.Update)		apiRouter.DELETE("users/:id", api.UserController{}.Delete)	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

8.2 控制器的继承

继承后就可以调用控制器里面的公共方法

  • api目录下新建baseController.go,内容如下:
package apiimport "github.com/gin-gonic/gin"type BaseController struct{}func (con BaseController) success(c *gin.Context) {	c.String(200, "success")}func (con BaseController) error(c *gin.Context) {	c.String(200, "failed")}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 继续修改userController.go
type UserController struct {	// 继承BaseController	BaseController}func (con UserController) Index(c *gin.Context) {	// c.String(200, "api Index")	con.success(c)}func (con UserController) Add(c *gin.Context)  {	c.String(200, "api Add")}func (con UserController) List(c *gin.Context) {	c.String(200, "api List")}func (con UserController) Update(c *gin.Context) {	c.String(200, "api update")}func (con UserController) Delete(c *gin.Context) {	c.String(200, "api Delete")}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

九、Gin 中间件

Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、 记录日志、耗时统计等
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作

9.1 路由中间件

9.1.1 初识中间件

Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数。中间件要放在最后一个回调函数的前面 ,触发的方法都可以称为中间件

// 中间件函数func InitMiddleWare(c *gin.Context) {	fmt.Println("init middle ware ")}func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		// 中间件要放在最后一个回调函数的前面		apiRouter.GET("/", InitMiddleWare, api.UserController{}.Index)	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

9.1.2 ctx.Next()调用该请求的剩余处理程序

中间件里面加上 ctx.Next()后,c.Next()的语句后面先不执行,跳转到后面的中间件和回调函数中执行完后,才执行c.Next()后面的语句
可以让我们在路由匹配完成后执行一些操作。比如我们统计一个请求的执行时间

func InitMiddleWare(c *gin.Context) {	fmt.Println("1- init middle ware ")	start := time.Now().UnixNano()	// 调用c.Next()请求的剩余处理程序	// c.Next()的语句后面先不执行,先跳转路由匹配的最后一个回调函数执行后,	// 才执行c.Next()后面的语句	c.Next()	fmt.Println("3-程序执行完成 计算时间")	//  计算耗时	Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异	end := time.Now().UnixNano()	fmt.Println(end - start)}func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		// 中间件要放在最后一个回调函数的前面		apiRouter.GET("/", InitMiddleWare, func(ctx *gin.Context) {			fmt.Println("2 - 中间件")			ctx.String(200, "/api")		})	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

9.1.3 一个路由配置多个中间件的执行顺序

  • 多个中间件执行顺序
func InitMiddleWareOne(c *gin.Context) {	fmt.Println("one- init middleware start")	c.Next()	fmt.Println("one- init middleware end")}func InitMiddleWareTwo(c *gin.Context) {	fmt.Println("Two- init middleware start")	c.Next()	fmt.Println("Two- init middleware end")}func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		// 中间件要放在最后一个回调函数的前面		apiRouter.GET("/", InitMiddleWareOne, InitMiddleWareTwo, func(ctx *gin.Context) {			fmt.Println("首页")			ctx.String(200, "/api")		})	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 控制台输出的结果
one- init middleware startTwo- init middleware start首页Two- init middleware endone- init middleware end
  • 1
  • 2
  • 3
  • 4
  • 5

9.1.4 ctx.Abort()了解

Abort是终止的意思,ctx.Abort()表示终止调用该请求的剩余处理程序
Abort()后,中间件后面的回调函数(包括后面的中间件)不执行了,直接执行该中间件这里面的语句

9.2 全局中间件

  • r.Use(中间件1,中间件2…)
func InitMiddleWareOne(c *gin.Context) {	fmt.Println("one- init middleware start")	c.Next()	fmt.Println("one- init middleware end")}func InitMiddleWareTwo(c *gin.Context) {	fmt.Println("Two- init middleware start")	c.Next()	fmt.Println("Two- init middleware end")}func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	apiRouter.Use(InitMiddleWareOne, InitMiddleWareTwo)	{		// 中间件要放在最后一个回调函数的前面		apiRouter.GET("/", func(ctx *gin.Context) {			fmt.Println("首页")			ctx.String(200, "/api")		})		apiRouter.GET("users", api.UserController{}.Index)	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

9.3 在路由分组中配置中间件

9.4 中间件和对应控制器之间数据共享

  • 中间件中设置值
    ctx.Set("username", "张三")

  • 控制器或者中间件中获取值
    username, _ := ctx.Get("username")

  • username 是一个空接口类型,通过转为字符串v, ok := username.(string)

  • 举例: 中间件

func LoginMiddleWare(c *gin.Context) {	fmt.Println("login middle ware")	c.Set("username", "张三")}
  • 1
  • 2
  • 3
  • 4
  • 控制器
func (con UserController) Index(c *gin.Context) {	username, _ := c.Get("username")	fmt.Println(username) 	// c.String(200, username)		c.JSON(200, gin.H{		"username" : username,	})	// con.success(c)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

中间件注意事项

gin默认中间件

  • gin.Default()默认使用了LoggerRecovery中间件,其中:
  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic,如果有panic的话,为写入500响应码
  • 如果不想使用上面的默认中间件,可以使用gin.New()新建一个没有任何中间件的路由

gin中间件中使用goroutine

  • 当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())
func LoginMiddleWare(c *gin.Context) {	fmt.Println("login middle ware")	c.Set("username", "张三")	// 定义一个goroutine统计日志	cCp := c.Copy()	go func ()  {		time.Sleep(2 * time.Second)		// 用了c.Request.URL.Path 也没有问题?		fmt.Println("Done in path " + cCp.Request.URL.Path)	}()}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

十、Gin 中自定义

10.1 关于Model

如果我们的应用非常简单的话,我们可以在Controller 里面处理常见的业务逻辑。但是如果
我们有一个功能想在多个控制器、或者多个模板里面复用的话,那么我们就可以把公共的功
能单独抽取出来作为一个模块(Model)。Model 是逐步抽象的过程,一般我们会在Model
里面封装一些公共的方法让不同Controller 使用,也可以在Model 中实现和数据库打交道

10.2 Model 里面封装公共的方法

  • 新建model/tools.go
package modelimport (	"crypto/md5"	"fmt"	"time")//时间戳间戳转换成日期func UnixToDate(timestamp int) string {	t := time.Unix(int64(timestamp), 0)	return t.Format("2006-01-02 15:04:05")}//日期转换成时间戳2020-05-02 15:04:05func DateToUnix(str string) int64 {	template := "2006-01-02 15:04:05"	t, err := time.ParseInLocation(template, str, time.Local)	if err != nil {		return 0	}	return t.Unix()}// 获取时间戳func GetUnix() int64 {	return time.Now().Unix()}// 获取当前日期func GetDate() string {	template := "2006-01-02 15:04:05"	return time.Now().Format(template)}// 获取年月日func GetDay() string {	template := "20060102"	return time.Now().Format(template)}func Md5(str string) string {	data := []byte(str)	return fmt.Sprintf("%x\", md5.Sum(data))}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

十一、Gin 文件上传

十二、Gin 中的Cookie

12.1 Cookie 介绍

  • HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页
    面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何
    关系的。如果我们要实现多个页面之间共享数据的话我们就可以使用Cookie 或者Session 实
  • cookie 是存储于访问者计算机的浏览器中。可以让我们用同一个浏览器访问同一个域名
    的时候共享数

12.2 Cookie 能实现的功能

  1. 保持用户登录状态
  2. 保存用户浏览的历史记录
  3. 猜你喜欢,智能推荐
  4. 电商网站的加入购物车

12.3 设置和获取Cookie

  • 前面我们已经使用过ctx.Set("username")ctx.Get("username")来进行数据的保存和共享,但这个使用的只针对是单页面的数据共享,要想实现多页面的共享,就需要Cookie或者Session

12.3.1 设置Cookie

c.SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
其中:
* 第一个参数key
* 第二个参数value
* 第三个参数过期时间.如果只想设置Cookie 的保存路径而不想设置存活时间,可以在第三个参数中传递nil
* 第四个参数cookie 的路径
* 第五个参数cookie 的路径Domain 作用域本地调试配置成localhost , 正式上线配置成域名
* 第六个参数是secure ,当secure 值为true 时,cookie 在HTTP 中是无效,在HTTPS 中才有效
* 第七个参数httpOnly,是微软对COOKIE 做的扩展。如果在COOKIE 中设置了“httpOnly”属性,则通过程序(JS 脚本、applet 等)将无法读取到COOKIE 信息,防止XSS 攻击产生

12.3.2 获取Cookie

cookie, err := c.Cookie("name")

12.3.3 删除Cookie

  • 把第三个参数时间设置为-1

12.3.3 举例

func ApiRouter(r *gin.Engine) {	apiRouter := r.Group("/api")	{		apiRouter.GET("/", func(ctx *gin.Context) {			// 设置Cookie			ctx.SetCookie("username", "张三", 3600, "/", "localhost", false, false)			fmt.Println("首页")			ctx.String(200, "/api")		})		apiRouter.GET("/news", func(ctx *gin.Context) {			// 获取Cookie			username, _ := ctx.Cookie("username")			fmt.Println(username)			ctx.String(200, "/news/"+username)		})	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

12.4 多个二级域名共享cookie

  • 我们想的是用户在a.test.com 中设置Cookie 信息后在b.test.com 中获取刚才设置的cookie,也就是实现多个二级域名共享cookie, 这时候的话我们就可以这样设置cookie
    c.SetCookie("usrename", "张三", 3600, "/", ".test.com", false, true)

十三、Gin 中的

十四、Gin 中使用GORM 操作mysql 数据库

十五、原生 SQL 和 SQL 生成器

十六、Gin 中使用GORM 实现表

十七、 GORM 中使用事务

十八、Gin 中使用go-ini 加载.ini 配置文件

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发