Gin框架快速入门实战

gin 框架快速入门

工具

测试工具:

  • 浏览器拓展 postwoman
  • apipost

gin路由,gin 程序的热加载

gin官网文档: https://gin-gonic.com/zh-cn/docs/

fresh安装 : go get github.com/pilu/fresh

package main

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

// gin 程序的热加载, gin路由  get  post  put  delete
func main() {
	//创建一个默认的路由引擎
	r := gin.Default()
	//配置路由
	r.GET("/", func(c *gin.Context) {
		c.String(200, "你好gin")
	})

	r.GET("/news", func(c *gin.Context) {
		//http.StatusOK go  自带  http包中的常量  表示200  状态码
		c.String(http.StatusOK, "这是%v", "新闻页面")
	})

	//post 请求
	r.POST("/post", func(c *gin.Context) {
		c.String(200, "这是一个post请求")

	})

	//put请求
	r.PUT("/put", func(c *gin.Context) {
		c.String(200, "这是一个put数据主要用于编辑数据")

	})

	r.DELETE("/delete", func(c *gin.Context) {
		c.String(200, "这是一个delete请求主要用于删除数据")

	})

	//启动 服务 , 默认8080端口
	r.Run()
}

gin 路由中响应数据

注意: 配置模板再goland 中默认的 是从项目根路径开始查找的

修改位置为:
Gin框架快速入门实战_第1张图片

go 代码

// gin 路由响应数据  string,   json ,  jsonp , html
func ginDemo02() {
	type Article struct {
		//`json:"title"`  将前面大写的自动替换为后面小写的
		Title   string `json:"title"`
		Desc    string `json:"desc"`
		Content string `json:"content"`
	}
	//创建路由引擎
	r := gin.Default()
	//加载模板 注意:  默认是以 项目 为根目录,
	r.LoadHTMLGlob("templates/*")
	r.GET("/json", func(c *gin.Context) {
		c.JSON(200, map[string]interface{}{
			"success": true,
			"message": "你好gin",
		})

	})

	r.GET("/json2", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		c.JSON(200, gin.H{
			"success": true,
			"message": "你好gin gin.H",
		})

	})

	//返回自定义结构体
	r.GET("/json3", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个json请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.JSON(200, a)
	})

	//jsonp 请求  主要用于跨域请求
	//加上回调函数callback: http://127.0.0.1:8080/jsonp?callback=xxx
	//xxx({"title":"这是一个jsonp请求","desc":"描述","content":"内容"});
	r.GET("/jsonp", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个jsonp请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.JSONP(200, a)
	})

	//返回XML数据
	r.GET("/xml", func(c *gin.Context) {
		//gin.H  等价于 type H map[string]any
		a := &Article{
			Title:   "这是一个xml请求",
			Desc:    "描述",
			Content: "内容",
		}
		c.XML(200, a)
	})

	//响应 html数据
	r.GET("/news", func(c *gin.Context) {
		c.HTML(200, "goods.html", gin.H{
			"title": "这是商品页面",
		})
	})
	r.Run()
}

html模板文件

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>goodstitle>
head>
<body>



<h2>{{.title}}h2>
body>
html>

gin 模板渲染

go 代码


// gin   html  模板渲染
func gindemo03() {
	r := gin.Default()
	//**  表示文件夹
	r.LoadHTMLGlob("templates/**/*")

	//注意页面的名字 为 模板文件 define 的名字
	//{{ define "admin/news.html" }}

	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"news": &Article{
				Title:   "新闻标题",
				Desc:    "描述",
				Content: "with结构结构体使用",
			},
		})
	})

	r.GET("/news", func(c *gin.Context) {
		c.HTML(http.StatusOK, "default/news.html", gin.H{
			"title": "hello 前台新闻页面",
		})

	})

	r.GET("/admin", func(c *gin.Context) {
		c.HTML(http.StatusOK, "admin/index.html", gin.H{
			"title": "hello  admin",
		})

	})

	r.GET("/admin/news", func(c *gin.Context) {
		c.HTML(http.StatusOK, "admin/news.html", gin.H{
			"title": "hello  admin news ",
		})

	})

	r.Run()
}

html模板文件


{{ define "admin/index.html" }}

doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Documenttitle>
head>
<body>


{{  .title }}
<h2>这是后台首页h2>
body>
html>

{{ end }}

gin模板的基本语法

演示go代码


	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"news": &Article{
				Title:   "新闻标题",
				Desc:    "描述",
				Content: "with结构结构体使用",
			},
		})
	})

输出数据

{{}} 输出数据

{{.title}}

变量

变量


{{ $t := .title }}

<h4> {{$t}}h4>

比较函数

比较函数

Gin框架快速入门实战_第2张图片

条件判断

条件判断

注意 {{end}} 结尾

Gin框架快速入门实战_第3张图片

range 遍历

range 遍历


<ul>
    {{range $key, $value := .hobby}}
    <li>{{$key}} --- {{$value}}li>
    
    {{else}}
    <li>列表为空li>
    {{end}}

ul>

with 解构结构体

with 解构结构体


{{ with .news}}
{{.Title}}
{{.Desc}}
{{.Content}}
{{end}}

自定义模板函数

模板自带 函数

Gin框架快速入门实战_第4张图片

自定义模板函数

在utils 啊包中定义一个时间戳转换函数

// 将时间戳转换为 常见时间格式
func UnixToTime(timestamp int) string {
	t := time.Unix(int64(timestamp), 0)
	return t.Format("2006-01-02 15:04:05")
}

注入自定义函数

//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	r.SetFuncMap(template.FuncMap{
		"UnixToTime": utils.UnixToTime,
	})
	// 加载模板文件 **  表示文件夹
	r.LoadHTMLGlob("templates/**/*")

使用


	r.GET("/", func(c *gin.Context) {
		c.HTML(200, "default/index.html", gin.H{
			"title":          "hello  gin",
			"score":          92,
			"hobby":          []string{"吃饭", "睡觉", "coding"},
			"testEmptySlice": []string{},
			"timeStamp":      2953130242,

		})
	})

在html文件中调用函数



{{ len .title}}

{{ UnixToTime .timeStamp }}

嵌套 template

模板文件嵌套其他模板文件

注意后面需要有一个点 ,才能将当前 页面的数据传递到引入 的模板中



{{ template "public/page_header.html" .}}

引入静态文件

gin 中 html 加载 css js 图片 等静态文件不能直接加载, 需要先配置静态文件目录

// 当访问  /static/*这个 url 的时候。  就会引入这个 ./static 文件夹内的内容, 这样才能正常加载  static 文件夹内的文件
	r.Static("/static", "./static")

可以正常加载对应文件

-- 访问了 /static/*  这个 url  就会触发 从而自动导入文件夹内的内容,
 <link rel="stylesheet"  href="/static/css/base.css">
 <img src="/static/images/node.jpg">
 

不能正常加载对应文件

 <link rel="stylesheet"  href="/aaa/css/base.css">

即使 文件夹内存在 对应文件也不能正常加载,需要配置 , 这样访问 aaa开头的url, 就会加载 当前 aaa 目录内的文件

r.Static("/aaa", "./aaa")

gin 路由传值

get传值

	//get请求传值
	r.GET("/get", func(c *gin.Context) {
		//获取参数的值
		username := c.Query("u")
		age := c.Query("age")
		//没有接收到参数  使用默认值 1
		page := c.DefaultQuery("page", "1")

		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"age":      age,
			"page":     page,
		})
	})

将接收的值绑定到结构体上

 // form  表示 接收form  表单中的数据 
type UserInfo struct {
	Username string `json:"username" form:"username"`
	Password string `json:"password"  form:"password"`
}

	//将传入的数据绑定到结构体上
	r.GET("/getUser", func(c *gin.Context) {
		user := &UserInfo{}
		//将数据解析到结构体
		err := c.ShouldBind(user)
		if err == nil {
			c.JSON(http.StatusOK, user)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		}

	})

post传值

	// 接收传递的数据
	r.POST("/doAddUser", func(c *gin.Context) {
		//接收数据
		username := c.PostForm("username")
		password := c.PostForm("pwd")
		//没有传入  使用默认值
		age := c.DefaultPostForm("age", "12")
		c.JSON(http.StatusOK, gin.H{
			"code":    true,
			"message": "成功获取数据",
			"u":       username,
			"p":       password,
			"age":     age,
		})
	})

post 传递xml数据解析到结构体


// 将xml中的数据解析到 结构体中
type News struct {
	Title   string `json:"title" xml:"title"`
	Content string `json:"content" xml:"content"`
}

//获取post xml 数据
	r.POST("/xml", func(c *gin.Context) {
	
		news := &News{}
		//获取数据, 返回一个  []byte
		xmlSliceData, _ := c.GetRawData()
		//将数据 解析到 结构体中
		err := xml.Unmarshal(xmlSliceData, news)
		if err == nil {
			c.JSON(http.StatusOK, news)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		}
	})

xml测试数据


		
			<news>
			<content type="string">张三content>
			<title  type="string">姓名title>
			news>

动态路由

//动态路由传值
	//访问  list/xxx  会替换 id变成  list/xxx  可以用于传值
	r.GET("/list/:id", func(c *gin.Context) {
		//获取传入的值
		param := c.Param("id")
		c.String(200, "%v", param)
	})


gin路由文件抽离

将不同 功能 的 模块进行分组 抽离成单独的go文件, 方便管理和 协同开发

defaultRoutes.go

package routes

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

func DefaultRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	defaultRoutes := r.Group("/")
	{
		defaultRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "首页主页面")

		})

		defaultRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "首页新闻页面")

		})
	}

}

apiRoutes.go

package routes

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

func ApiRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级

	apiRoutes := r.Group("/api")
	{
		apiRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "api首页面")

		})

		apiRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "api新闻页面")

		})
	}
}

adminRoutes.go

package routes

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

func AdminRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	adminRoutes := r.Group("/admin")
	{
		adminRoutes.GET("/", func(c *gin.Context) {
			c.String(200, "后台首页面")

		})

		adminRoutes.GET("/news", func(c *gin.Context) {
			c.String(200, "后天新闻页面")

		})
	}
}

在main 文件中调用


// 路由文件抽离
func ginDemo05() {
	r := gin.Default()
	//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	//自定义模板函数, 注入自己定义的函数, 注意这个 需要在加载模板之前
	r.SetFuncMap(template.FuncMap{
		"UnixToTime": utils.UnixToTime,
	})
	//加载模板
	r.LoadHTMLGlob("templates/**/*")

	//加载路由页面
	routes.DefaultRoutesInit(r)
	routes.ApiRoutesInit(r)
	routes.AdminRoutesInit(r)

	r.Run()
}

gin 控制器

路由只负责配置路由, 业务代码由控制器实现

目录参考
Gin框架快速入门实战_第5张图片

UserController.go

package admin

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

// 将其 挂载到结构体上 可以实现继承关系
type UserController struct {
}

func (conn UserController) Index(c *gin.Context) {
	c.String(200, "后台管理用户首页 --  index")
}

func (conn UserController) News(c *gin.Context) {
	c.String(200, "后天管理新闻页面  --  news")
}

进行路由处理

package routes

import (
	"GinProject/src/demo01/controllers/admin"
	"github.com/gin-gonic/gin"
)

func AdminRoutesInit(r *gin.Engine) {
	//进行路由分组,下面设置的url都是 这个的子层级
	adminRoutes := r.Group("/admin")
	{
		adminRoutes.GET("/", admin.UserController{}.Index)

		adminRoutes.GET("/news", admin.UserController{}.News)
	}
}

gin 中间件

中间件: 匹配路由前和匹配路由完成后 执行的一系列操作

一个简单的中间件

func MiddlewareOne(c *gin.Context) {
	fmt.Println("1-我是一个中间件 --  One")
}

	//func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes
	//可以传入 多可回调函数, 中间的回调函数可以理解为一个简单的中间件
	// 从前往后开始执行 回调函数
	r.GET("/mid", MiddlewareOne, func(c *gin.Context) {
		fmt.Println("这是一个首页...")
		c.String(200, "这是一个首页")

	})

gin 启动引擎默认也是 使用了 中间件的

//Default()  默认使用了中间件  engine.Use(Logger(), Recovery())
	//gin.New():  不使用任何中间件
	r := gin.Default()

中间件中的方法

c.Next() : 跳转执行后续的回调方法, 其他回调函数执行完毕,再继续向后执行

func MiddlewareOne(c *gin.Context) {
	
	//执行后续的 回调方法
	c.Next()
	fmt.Println("1-我是一个中间件 --  One")
}

** c.Abort()** : 中止后续回调函数的执行

func MiddlewareTwo(c *gin.Context) {
	fmt.Println("1-我是一个中间件(全局) -- Two")
	c.Abort()
}

使用中间件计算程序执行时间

// 计算程序运行时间中间件
func TimeMiddleware(c *gin.Context) {
	start := time.Now().UnixNano()
	fmt.Println("我是一个 计算执行时间的中间件")
	c.Next()
	end := time.Now().UnixNano()
	fmt.Println("执行时间", end-start)
}

// 应用
	r.GET("/time", TimeMiddleware, func(c *gin.Context) {
		time.Sleep(1 * time.Second)
		c.String(200, "执行时间")

	})

全局中间件

全局中间件 会配置到 每一个路由上

//配置全局中间件, 可以配置多个
	r.Use(MiddlewareTwo)

路由分组中使用中间件


 //法一:
adminRoutes := r.Group("/admin", middlewares.Initmiddlewares)
//法二
 adminRoutes.Use(middlewares.Initmiddlewares)

中间价和控制器之间共享数据

在 中间件中存储数据

//在中间件中将数据存储到  域中
c.Set("username", "ggbo")

在控制器存储数据

注意:取出数据的类型转换

// 在控制器 取出数据
func (conn UserController) Index(c *gin.Context) {
	//中间价和 控制器之间传递数据
	//  返回  any : type any = interface{}  是空接口的类型  需要转换成 具体的类型
	username, _ := c.Get("username")
	fmt.Println(username)

	//类型转换
	s, ok := username.(string)
	if ok {
		c.String(200, "用户:"+s)

	} else {
		c.String(200, "用户获取失败")
	}

}

中间件中使用 go 协程

注意不能直接 在 go 程中使用原有的 Context操作

func Initmiddlewares(c *gin.Context) {
	
	//使用协程 不能直接使用  原有 Context, 需要复制,然后操作复制的Context
	Copycontext := c.Copy()
	//使用 go 程统计日志
	go func() {
		time.Sleep(5 * time.Second)
		fmt.Println("Done!!!" + Copycontext.Request.URL.Path)
	}()
}

gin 中自定义model

关于Model

只是不同 的命名而已, 就是将公共的方法 抽离 出来组成一个工具 库

gin 实现文件上传

单文件上传

form表单

<form action="/fileupload" method="post" enctype="multipart/form-data">
    选择文件1: <input type="file" name="face">
    <input type="submit" value="提交">
form>

go 代码

	r.POST("/fileupload", func(c *gin.Context) {
		file, err := c.FormFile("face")
		if err == nil {
			dst := path.Join("./static/upload", file.Filename)
			//保存文件
			c.SaveUploadedFile(file, dst)
			c.String(200, "success")
		} else {
			c.String(200, "error")
		}
	})

多文件不同名

html

<form action="/filesupload" method="post" enctype="multipart/form-data">
    选择文件1: <input type="file" name="face1">
    选择文件2: <input type="file" name="face2">
    <input type="submit" value="提交">
form>

go 代码

//参考  单文件上传,  其他的相同, 分别获取 不同的文件即可
file1, err := c.FormFile("face1")
file2, err := c.FormFile("face2")

多文件上传相同名

html

<form action="/filesupload" method="post" enctype="multipart/form-data">
	
    选择文件1: <input type="file" name="face[]">
    选择文件2: <input type="file" name="face[]">
    <input type="submit" value="提交">
form>

go

	//多文件上传
	r.POST("/filesupload", func(c *gin.Context) {
		form, _ := c.MultipartForm()
		files := form.File["face[]"]
		
		//遍历保存文件
		for _, file := range files {
			dst := path.Join("./static/upload", file.Filename)
			c.SaveUploadedFile(file, dst)
		}

		c.String(200, "success uploadFiles")

	})

按照日期保存文件

将 上传的文件自动保存到 以当前日期命名的文件夹内

go 工具类

import "time"

// 获取时间戳
func GetUnix() int64 {
	return time.Now().Unix()
}

// 获取当前日期
func GetNowDate() string {
	date := "2006-01-01 15:04:05"
	return time.Now().Format(date)
}

// 获取年月日
func GetDay() string {
	template := "20060102"
	return time.Now().Format(template)

}

逻辑代码


	//按照日期保存文件
	r.POST("/fileupload3", func(c *gin.Context) {
		//1.获取上传的文件
		file, err := c.FormFile("face")
		if err == nil {
			//2.创建当前日期的文件目录
			// 获取当前 日期
			day := models.GetDay()
			dir := "./static/upload/" + day
			err := os.MkdirAll(dir, 0666)
			if err != nil {
				fmt.Println(err)
				c.String(200, "创建文件失败")
				return
			}
			// 3.对文件使用时间戳格式命名
			// 获取当前时间戳
			date := models.GetUnix()
			//注意文件名不能有空格, 否则不能正常保存
			filename := strconv.FormatInt(date, 10) + path.Ext(file.Filename)
			dst := path.Join(dir, filename)
			fmt.Println(dst)
			c.SaveUploadedFile(file, dst)
			c.String(200, "success")
		} else {
			c.String(200, "error")
		}
	})

gin中的Cookie

设置cookie 字段解析

SameSite详细解析

	http.SetCookie(c.Writer, &http.Cookie{
		Name:     name,  
		Value:    url.QueryEscape(value),  
		MaxAge:   maxAge,  // 最大存活时间,单位秒小于0 的时候浏览器将会删除该cookie
		Path:     path,  //  路径
		Domain:   domain,   // 域名
		SameSite: c.sameSite, // 限制第三方 Cookie,从而减少安全风险。
		Secure:   secure,     //  是否只能在https 下操作
		HttpOnly: httpOnly,   //  是否允许前端访问
	}

cookie 使用

//设置cookie
c.SetCookie("username", "张三", 120, "/", "localhost", false, true)
//获取cookie		
c.Cookie("username")

多个二级域名共享cookie

假设域名: ddac.com

二级域名 a.ddac.com

setcookie域名: a.ddac.com

c.SetCookie("username", "张三", 120, "a.ddac.com", ".", false, true)

多个 域名共享 cookie

setcookie域名: .ddac.com

[xxx].ddac.com 类型的域名都能够共享cookie

c.SetCookie("username", "张三", 120, "/", ".ddac.com", false, true)

gin中的session 中间件

session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而session保存在服务器上。

session工作流程

当客户端浏览器第–次访问服务器并发送请求时,服务器端会创建一一个session对象,生成一个类似于key,value 的键值对,然后将value保存到服务器将key(cookie)返回到浏览器(客户)端。浏览器下次访问时会携带key(cookie),找到对应的session(value)。

安装

go get github.com/gin-contrib/sessions

**官方地址: **

//配置session 中间件
	//创建基于cookie 的存储引擎, secret1111: 适用于加密的秘钥
	//store := cookie.NewStore([]byte("secret1111"))
	store, _ := redis.NewStore(10, "tcp", "43.139.17.97:13654", "45928ddacQWE", []byte("secret1111"))
	//store 存储引擎
	r.Use(sessions.Sessions("hello", store))

	//将数据保存到 redis

	//设置/获取sessions
	r.GET("/hello", func(c *gin.Context) {
		//设置session
		session := sessions.Default(c)
		//设置session过期时间
		session.Options(sessions.Options{
			MaxAge: 3600*6, // 单位秒
		})
		if session.Get("hello") != "world" {
			session.Set("hello", "redis")
			//注意需要保存
			session.Save()
		}

		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})

session 简单使用

//配置session 中间件
	//创建基于cookie 的存储引擎, secret1111: 适用于加密的秘钥
	store := cookie.NewStore([]byte("secret1111"))
	//store 存储引擎
	r.Use(sessions.Sessions("hello", store))

	//将数据保存到 redis

	//设置/获取sessions
	r.GET("/hello", func(c *gin.Context) {
		//设置session
		session := sessions.Default(c)
		//设置session过期时间
		session.Options(sessions.Options{
			MaxAge: 3600 * 6, // 单位秒
		})
		if session.Get("hello") != "world" {
			session.Set("hello", "redis")
			//注意需要保存
			session.Save()
		}

		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})

将session 保存到redis中

将 store := cookie.NewStore([]byte(“secret1111”)) 替换为

	store, _ := redis.NewStore(10, "tcp", "43.139.17.97:13654", "45928ddacQWE", []byte("secret1111"))

你可能感兴趣的:(Golang,gin,golang)