Loading

04 Gin框架基础

1 01gin基础

1.1 代码列表

image-20220602135847503

1.2 01gin基础.go
package main

import (
"fmt"

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

// 01gin基础
/*说明:
(1) Gin 是一个用 Go (Golang) 编写的 HTTP Web 框架。它具有类似 Martini 的 API,但性能比 Martini 快 40 倍。如果你需要极好的性能,使用 Gin 吧。
(2) Gin 是Go世界里最流行的Web框架,Github上有59K+star。基于httprouter开发的Web框架。中文文档齐全,简单易用的轻量级框架。
(3) github项目地址
https://github.com/gin-gonic/gin
(4) 中文文档地址
https://gin-gonic.com/zh-cn/docs/
*/

/*1.Gin框架安装*/
// go get -u github.com/gin-gonic/gin

/*3.RESTful API*/
/*说明:
(1) REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
(2) 简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
1) GET用来获取资源
2) POST用来新建资源
3) PUT用来更新资源
4) DELETE用来删除资源
(3) 只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
(4) 例如,我们现在要编写一个管理书籍的系统,我们可以对一本书进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏
览器与我们Web服务端交互的方式和路径。我们按照RESTful API设计如下。
请求方法     URL     含义
GET         /book     查询书籍信息
POST     /book     创建书籍记录
PUT         /book     更新书籍信息
DELETE     /book     删除书籍信息
(5) 开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具
https://www.getpostman.com/
*/

func main() {
/*2.Gin示例*/
//// 创建一个默认的路由引擎
//r := gin.Default()
//// GET: 请求方式,/hello: 请求的路径
//// 当客户端以GET方法请求/hello路径时会执行后面的匿名函数
//r.GET("/hello", func(c *gin.Context) {
//// c.JSON: 返回JSON格式的数据
//c.JSON(200, gin.H{
// "message": "Hello World",
//})
//})
//// 启动HTTP服务,默认在0.0.0.0:8080启动服务
//err := r.Run()
//if err != nil {
//fmt.Println(err)
//return
//}

/*3.RESTful API*/
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",
})
})

err := r.Run()
if err != nil {
fmt.Println(err)
return
}
}

2 02gin渲染

2.1 代码列表

image-20220602140223435

2.2 02gin渲染/templates/

posts/index.tmpl

{{ define "posts/index.tmpl" }}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>posts/index</title>
    </head>
    <body>
   {{ .title }}
    </body>
    </html>
{{ end }}

users/index.tmpl

{{ define "users/index.tmpl" }}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>users/index</title>
    </head>
    <body>
   {{ .title }}
    </body>
    </html>
{{ end }}

index.tmpl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>修改模板引擎的标识符</title>
</head>
<body>
{{/*不将html转义为文本*/}}
<div>{{ .URL | safe }}</div>
</body>
</html>

static.tmpl

<!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>Document</title>
</head>
<body>
   {{ .static }}
    <hr>
    <img src="/static/cat.jpg" alt="cat.jpg" width="50%" height="50%"/>
</body>
</html>
2.3 02gin渲染/TemplatesInherit/

includes/home.tmpl

{{/*继承根模板,"."表示base根模板接收数据*/}}
{{ template "base.tmpl" . }}
{{/*重新定义块模板*/}}
{{ define "content" }}
    <p> 这是home页面 </p>
   {{/* "."表示传入的渲染数据 */}}
    <p> hello {{ .message }}</p>
{{ end }}

includes/index.tmpl

{{/*继承根模板,"."表示base根模板接收数据*/}}
{{ template "base.tmpl" . }}
{{/*重新定义块模板*/}}
{{ define "content" }}
    <p> 这是index页面 </p>
   {{/* "."表示传入的渲染数据 */}}
    <p> hello {{ .message }}</p>
{{ end }}

layouts/base.tmpl

{{/*定义根模板*/}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>Go Templates</title>
    <style>
        * {
            margin: 0;
       }
        .nav {
            top: 0;
            height: 70px;
            width: 100%;
            position: fixed;
            background-color: bisque;
       }
        .main {
            margin-top: 70px;
       }
        .menu {
            left: 0;
            height: 100%;
            width20%;
            position: fixed;
            background-color: lightcoral;
       }
        .content {
            text-align: center;
       }

    </style>
</head>
<body>

<div class="nav"></div>
<div class="main">
    <div class="menu"></div>
    <div class="content">
       {{/*定义块模板,用于被替换, "."表示将base根模板接收的数据传入当前定义的块 */}}
       {{ block "content" . }} {{ end }}
    </div>
</div>

</body>
</html>
2.4 02gin渲染/02gin渲染.go
package main

import (
"net/http"
"os"
"path/filepath"

"github.com/gin-gonic/gin"

"github.com/gin-contrib/multitemplate"
)

// 02gin渲染

/*1.HTML渲染*/
// 首先定义一个存放模板文件的templates文件夹,然后在其内部按照业务分别定义一个posts文件夹和一个users文件夹。

/*3.静态文件处理*/
// 关于模板文件和静态文件的路径,我们需要根据公司/项目的要求进行设置,可以使用下面的函数获取当前执行程序的路径
func getCurrentPath() string {
if ex, err := os.Executable(); err == nil {
return filepath.Dir(ex)
}
return "./"
}

/*注意运行方式:
go build .\02gin渲染.go
.\02gin渲染.exe
*/

/*4.使用模板继承*/
// go get -u github.com/gin-contrib/multitemplate
// 定义loadTemplates函数
func loadTemplates(templatesDir string) multitemplate.Renderer {
r := multitemplate.NewRenderer()
layouts, err := filepath.Glob(templatesDir + "/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob(templatesDir + "/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
// 为layouts/和includes/目录生成 templates map
for _, include := range includes {
layoutCopy := make([]string, len(layouts))
copy(layoutCopy, layouts)
files := append(layoutCopy, include)
r.AddFromFiles(filepath.Base(include), files...)
}
return r
}

func indexFunc(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"message": "this is index 页面",
})
}

func homeFunc(c *gin.Context) {
c.HTML(http.StatusOK, "home.tmpl", gin.H{
"message": "this is home 页面",
})
}

func main() {

/*1.HTML渲染*/
// Gin框架中使用LoadHTMLGlob()或者LoadHTMLFiles()方法进行HTML模板渲染
//r := gin.Default()
//r.LoadHTMLGlob("./templates/**/*")
////r.LoadHTMLFiles("./templates/posts/index.tmpl", "./templates/users/index.tmpl")
//r.GET("/posts/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
// "title": "posts/index",
// })
//})
//
//r.GET("/users/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
// "title": "users/index",
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*2.自定义模板函数*/
//router := gin.Default()
//
//// 定义一个不转义相应内容的safe模板函数
//router.SetFuncMap(template.FuncMap{
// "safe": func(str string) template.HTML {
// return template.HTML(str)
// },
//})
//
//router.LoadHTMLFiles("./templates/index.tmpl")
//router.GET("/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "index.tmpl", gin.H{
// "URL": "<a href='https://www.baidu.com'>点我跳转到百度</a>",
// })
//})
//
//err := router.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*3.静态文件处理*/
// 当我们渲染的HTML文件中引用了静态文件时,我们只需要按照以下方式在渲染页面前调用gin.Static方法即可
//r := gin.Default()
//// 加载静态文件
//fmt.Println(getCurrentPath()) // D:\Code\GoCode\02Gin\02gin渲染
//r.Static("/static", getCurrentPath()+"/static")
////r.Static("/static", "./static")
//// 加载模板文件
//r.LoadHTMLFiles(getCurrentPath() + "/templates/static.tmpl")
//// 使用GET方法获取资源
//r.GET("/static", func(c *gin.Context) {
// c.HTML(http.StatusOK, "static.tmpl", gin.H{
// "static": "static file",
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*4.使用模板继承*/
// Gin框架默认都是使用单模板,如果需要使用block template功能,可以通过"github.com/gin-contrib/multitemplate"库实现。
// 假设我们项目TemplatesInherit目录下home.tmpl和index.tmpl继承了base.tmpl,我们需要定义一个loadTemplates函数。
//r := gin.Default()
//
//r.HTMLRender = loadTemplates("./TemplatesInherit")
//
//r.GET("/index", indexFunc)
//r.GET("/home", homeFunc)
//
//err := r.Run()
//if err != nil {
// fmt.Println(err)
// return
//}

/*5.JSON渲染*/
//r := gin.Default()
//
//// gin.H 是map[string]interface{}的缩写
//r.GET("/someJSON", func(c *gin.Context) {
// // 方式一: 自己拼接JSON
// c.JSON(http.StatusOK, gin.H{
// "message": "hello world",
// })
//})
///*结果:
//{
//"message": "hello world"
//}
//*/
//
//r.GET("/moreJSON", func(c *gin.Context) {
// // 方法二: 使用结构体(结构体字段名必须大写,不然json包无法访问)
// type messageRecord struct {
// Name string `json:"user"`
// Message string
// Age int
// }
// msg := &messageRecord{
// Name: "liuChang",
// Message: "hello world",
// Age: 18,
// }
// c.JSON(http.StatusOK, msg)
//})
///*结果:
//{
//"user": "liuChang",
//"Message": "hello world",
//"Age": 18
//}
//*/
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*6.XML渲染*/
//r := gin.Default()
//
//// gin.H 是map[string]interface{}的缩写
//r.GET("/someXML", func(c *gin.Context) {
// // 方式一: 自己拼接XML
// c.XML(http.StatusOK, gin.H{"message": "Hello world!"})
//})
///*结果:
//This XML file does not appear to have any style information associated with it. The document tree is shown below.
//<map>
//<message>Hello world!</message>
//</map>
//*/
//
//r.GET("/moreXML", func(c *gin.Context) {
// // 方法二: 使用结构体(结构体字段名必须大写,不然xml包无法访问)
// type messageRecord struct {
// Name string
// Message string
// Age int
// }
// msg := &messageRecord{
// Name: "liuChang",
// Message: "Hello world!",
// Age: 18,
// }
// c.XML(http.StatusOK, msg)
//})
///*结果:
//This XML file does not appear to have any style information associated with it. The document tree is shown below.
//<messageRecord>
//<Name>liuChang</Name>
//<Message>Hello world!</Message>
//<Age>18</Age>
//</messageRecord>
//*/
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*7.YAML渲染*/
//r := gin.Default()
//
//r.GET("/someYAML", func(c *gin.Context) {
// c.YAML(http.StatusOK, gin.H{
// "message": "ok",
// "status": http.StatusOK,
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
//
///*输出结果:
//在浏览器中输入http://127.0.0.1:8080/someYAML后会自动下载一个someYAML文件,文件内容如下:
//message: ok
//status: 200
//*/

}

3 03gin获取参数

3.1 代码列表

image-20220602141508315

3.2 03gin获取参数/templates/

index.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>获取form参数 — POST</title>
</head>
<body>
<form action="http://127.0.0.1:8080/user/search" enctype="application/x-www-form-urlencoded" method="post">
<label>
姓名: <input type="text" name="username">
</label>
<label>
<br/>
地址: <input type="text" name="address">
</label>
<br/>
提交:<input type="submit">
</form>
</body>
</html>
3.3 03gin获取参数/03gin参数获取.go
package main

// 03gin获取参数

/*
(1) get请求和post请求的区别
1) GET请求的数据会暴露在地址栏中,而POST请求则不会;
2) Get请求传输数据量小,Post请求传输数据量大;
3) POST的安全性比GET的高。

(2) POST与GET两种请求方式的区别
1) GET请求
请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是unicode,
即是说所有的非ASCII字符都要编码之后再传输。GET请求的数据会暴露在地址栏中。
2) POST请求
POST请求会把请求的数据放置在HTTP请求包的包体中。POST请求的数据不会暴露在地址栏中。

(3) 传输数据的大小
1) GET请求
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的
长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。
2) POST请求
由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的
配置。

(4) 安全性
POST的安全性比GET的高。这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服
务器的数据。比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人
查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。除此之外,GET请求提交的数据还可能会造成
Cross-site request forgery攻击。

(5) Postman是一个遵循 restful api 的http API请求工具。
*/

/*5.参数绑定*/

// Login 结构体(浏览器的get或post请求都使用 form: )
/*
ShouldBind会按照下面的顺序解析请求中的数据完成绑定:
(1) 如果是 GET 请求,只使用 Form 绑定引擎(query)。
(2) 如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用 Form(form-data)。
*/
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}

func main() {
/*1.获取querystring参数 - 浏览器GET请求,请求参数写在URL中*/
// querystring指的是URL中 ? 后面携带的参数,例如:/user/search?username=liuChang&address=上海。

// Default 返回一个默认的路由引擎
//r := gin.Default()
//
//r.GET("/user/search", func(c *gin.Context) {
// // DefaultQuery取不到值时会返回指定的默认值
// 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,
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*在浏览器中访问: http://127.0.0.1:8080/user/search?username=liuChang&address=上海
//返回结果:
//{
// "address": "上海",
// "message": "ok",
// "username": "liuChang"
//}
//*/

/*2.获取form参数 - 浏览器POST请求,请求参数写在Body中*/
// 当前端请求的数据通过form表单提交时,例如向/user/search发送一个POST请求。

// 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,
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 在浏览器中打开templates/index.html表单文件,填写信息后提交。
//(2) 使用Postman工具进行访问,见图templates/form-post.png 。
//(3) 输出结果
//{
// "address": "青青草原",
// "message": "ok",
// "username": "喜洋洋"
//}
//*/

/*3.获取json参数 - 浏览器POST请求,请求参数写在Body中*/
// 当前端请求的数据通过JSON提交时,例如向/json发送一个POST请求。

//r := gin.Default()
//r.POST("/json", func(c *gin.Context) {
// // 从c.Request.Body读取请求数据
// b, err := c.GetRawData()
// if err != nil {
// fmt.Println(err)
// return
// }
// // 定义map或结构体
// var m map[string]interface{}
// // 反序列化
// err = json.Unmarshal(b, &m)
// if err != nil {
// fmt.Println(err)
// return
// }
// fmt.Printf("%#v\n", m) // map[string]interface {}{"address":"青青草原", "message":"ok", "username":"喜洋洋"}
// c.JSON(http.StatusOK, m)
//})
//
//err := r.Run()
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 使用Postman工具进行访问,见图templates/json-post.png 。
//(2) 输出结果
//{
// "address": "青青草原",
// "message": "ok",
// "username": "喜洋洋"
//}
//*/

/*4.获取path参数 - 浏览器GET请求,请求参数写在URL中*/
// 请求的参数通过URL路径传递,例如: /user/search/喜洋洋/青青草原

// 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,
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 在浏览器中访问地址: http://127.0.0.1:8080/user/search/喜洋洋/青青草原
//(2) 返回结果
//{
// "address": "青青草原",
// "message": "ok",
// "username": "喜洋洋"
//}
//*/

/*5.参数绑定*/
/*说明:
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动
提取请求中QueryString、form表单、JSON、XML等参数到结构体中。下面的示例代码演示了.ShouldBind()强大的功能,它
能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
*/

/*5.1 绑定JSON的示例({"user": "liuChang", "password": "123456"})*/
//router := gin.Default()
//
//router.POST("/loginJSON", func(c *gin.Context) {
// var login Login
//
// if err := c.ShouldBind(&login); err == nil {
// fmt.Printf("login info:%#v\n", login) // login info:main.Login{User:"liuChang", Password:"123456"}
// c.JSON(http.StatusOK, gin.H{
// "user": login.User,
// "password": login.Password,
// })
// } else {
// c.JSON(http.StatusBadRequest, gin.H{
// "error": err.Error(),
// })
// }
//})
//
//err := router.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 使用Postman工具进行访问,见图templates/json-post-ShouldBind.png 。
//(2) 输出结果
//{
// "password": "123456",
// "user": "liuChang"
//}
//*/

/*5.2 绑定form表单示例(user=liuChang&password=123456)*/
//router := gin.Default()
//
//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(),
// })
// }
//})
//
//err := router.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 使用Postman工具进行访问,见图templates/form-post-ShouldBind.png 。
//(2) 输出结果
//{
// "password": "123456",
// "user": "liuChang"
//}
//*/

/*5.3 绑定QueryString示例(/loginQuery?user=liuChang&password=123456)*/
//router := gin.Default()
//
//router.GET("/loginQuery", 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(),
// })
// }
//})
//
//err := router.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*访问方式:
//(1) 在浏览器中访问地址: http://127.0.0.1:8080/loginQuery?user=liuChang&password=123456
//(2) 返回结果
//{
// "password": "123456",
// "user": "liuChang"
//}
//*/

}

3.4 Postman

03gin获取参数/templates/form-post.png form-post

03gin获取参数/templates/form-post-ShouldBind.png form-post-ShouldBind

03gin获取参数/templates/json-post.png json-post

03gin获取参数/templates/json-post-ShouldBind.png json-post-ShouldBind

4 04gin文件上传

4.1 代码列表

image-20220602142409250

4.2 04gin文件上传/upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
<label>
<!--单个文件上传-->
<span style="color:red;display:block;">单个文件上传</span>
<input type="file" name="f1">
</label>

<hr>
<label>
<!--多个文件上传-->
<span style="color:red;display:block;">多个文件上传</span>
<input multiple="multiple" type="file" name="files">
</label>

<hr>
<label>
<input type="submit" value="上传">
</label>
</form>
</body>
</html>
4.3 04gin文件上传/04gin文件上传.go
package main

// 04gin文件上传

func main() {

/*1.单个文件上传*/
//router := gin.Default()
//
//// 处理multipart/form-data提交文件时默认的内存限制是32MiB,可以通过下面的方式修改
//router.MaxMultipartMemory = 8 // 8MiB
//
//router.POST("/upload", func(c *gin.Context) {
// // 获取单个文件
// file, err := c.FormFile("f1")
// // 获取文件失败时返回JSON格式的数据
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{
// "message": fmt.Sprintf("get '%s' falied!", file.Filename),
// })
// fmt.Println(err)
// return
// }
//
// // 打印上传文件的日志
// log.Println(file.Filename)
// // 上传文件存储的路径
// dst := fmt.Sprintf("./uploadDir/%s", file.Filename)
// // 上传文件到指定的目录
// err = c.SaveUploadedFile(file, dst)
//
// // 文件上传失败后返回JSON格式的数据
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{
// "message": fmt.Sprintf("'%s' upload falied!", file.Filename),
// })
// fmt.Println(err)
// return
// }
// // 文件上传成功后返回JSON格式的数据
// c.JSON(http.StatusOK, gin.H{
// "message": fmt.Sprintf("'%s' upload success!", file.Filename),
// })
//})
//
//err := router.Run()
//if err != nil {
// fmt.Println(err)
// return
//}

/*2.多个文件上传*/
//router := gin.Default()
//
//// 处理multipart/form-data提交文件时默认的内存限制是32MiB,可以通过下面的方式修改
//router.MaxMultipartMemory = 8 // 8MiB
//
//router.POST("/upload", func(c *gin.Context) {
// // 获取表单
// form, err := c.MultipartForm()
// // 获取表单失败时返回JSON格式的数据
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{
// "message": fmt.Sprintf("get '%s' falied!", "表单"),
// })
// fmt.Println(err)
// return
// }
//
// // 循环获取表单中的每个文件并上传
// files := form.File["files"]
// for index, file := range files {
// // 打印上传文件的日志
// log.Println(strconv.Itoa(index) + "_" + file.Filename)
// // 上传文件存储的路径
// dst := fmt.Sprintf("./uploadDir/%d_%s", index, file.Filename)
// // 上传文件到指定的目录
// err := c.SaveUploadedFile(file, dst)
// // 文件上传失败后返回JSON格式的数据
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{
// "message": fmt.Sprintf("'%s' upload falied!", file.Filename),
// })
// fmt.Println(err)
// return
// }
// }
//
// // 所有文件都上传成功后返回JSON格式的数据
// c.JSON(http.StatusOK, gin.H{
// "message": fmt.Sprintf("'%d' files upload success!", len(files)),
// })
//})
//
//err := router.Run()
//if err != nil {
// fmt.Println(err)
// return
//}

}

5 05gin重定向

5.1 代码列表

image-20220602143052289

5.2 05gin重定向.go
package main

// 05gin重定向

func main() {
// 1.HTTP重定向
// HTTP 重定向,内部、外部重定向均支持,浏览器地址栏的URL会发生改变。
//r := gin.Default()
//
//// http内部重定向
//r.GET("/testIn", func(c *gin.Context) {
// c.Redirect(http.StatusMovedPermanently, "/test")
//})
//
//r.GET("/test", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "hello world",
// })
//})
//
//// http外部重定向
//r.GET("/testOut", func(c *gin.Context) {
// c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

// 2.路由重定向
// 路由重定向,使用HandleContext,浏览器地址栏的URL不会发生改变。
//r := gin.Default()
//
//r.GET("/testRoute", func(c *gin.Context) {
// // 指定路由重定向的URL
// c.Request.URL.Path = "/test"
// r.HandleContext(c)
//})
//
//r.GET("/test", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "hello world",
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

}

6 06gin路由

6.1 代码列表

image-20220602143208384

6.2 06gin路由/views/

404.tmpl

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<span style="color:greenyellow;font-size:100px;">
404
</span>
</body>
</html>
6.3 06gin路由/06gin路由.go
package main

import (
"fmt"
"net/http"

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

// 06gin路由
/*
路由原理:
(1) Gin框架中的路由使用的是httprouter(https://github.com/julienschmidt/httprouter)这个库。
(2) 其基本原理就是构造一个路由地址的前缀树。
*/

func main() {

/*1.普通路由*/
//r := gin.Default()
//
//// 路由: 匹配GET方法和/get路径
//r.GET("/get", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//})
//
//// 路由: 匹配所有请求方法的Any方法和/any路径
//r.Any("/any", func(c *gin.Context) {
// switch c.Request.Method {
// case http.MethodGet:
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
// case http.MethodPost:
// c.JSON(http.StatusOK, gin.H{
// "method": "POST",
// })
// default:
// c.JSON(http.StatusOK, gin.H{
// "method": "Any",
// })
// }
//})
//
//// 为没有配置处理函数的路由添加处理程序,默认情况下它返回 404 代码。
//// 下面的代码为没有匹配到路由的请求都返回views/404.html页面。
//r.LoadHTMLFiles("./views/404.tmpl")
//// 路由: 匹配不到的方法和路径
//r.NoRoute(func(c *gin.Context) {
// c.HTML(http.StatusNotFound, "404.tmpl", nil)
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*2.路由组*/
// 我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性用一对{}包裹同组的路由,这只是为了看着清晰,你用不用{}包裹功能上没什么区别。
// 通常我们将路由分组用在划分业务逻辑或划分API版本时

/*2.1常规路由组*/
//r := gin.Default()
//// 路由组
//userGroup := r.Group("/user")
//{
// userGroup.GET("/index", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "index",
// })
// })
//
// userGroup.POST("/login", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "login",
// })
// })
//}
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}

/*2.2嵌套路由组*/
r := gin.Default()
// 路由组(使用GET方法访问URL地址 http://127.0.0.1:8080/user/index)
userGroup := r.Group("/user")
{
userGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "index",
})
})

// 嵌套路由组(使用POST方法访问URL地址 http://127.0.0.1:8080/user/vip/login)
vipGroup := userGroup.Group("/vip")
{
vipGroup.POST("/login", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "login",
})
})
}
}

err := r.Run(":8080")
if err != nil {
fmt.Println(err)
return
}

}

7 07gin中间件

7.1 代码列表

image-20220602143510820

7.2 07gin中间件/7.1gin注册中间件.go
package main

// 7.1gin注册中间件

import (
"fmt"
"log"
"net/http"
"time"

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

/*说明:
(1) Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间
件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
(2) Gin中的中间件必须是一个gin.HandlerFunc类型。
(3) 例如Gin框架中的GET方法
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
handlers ...HandlerFunc: 表示传递的中间件类型为gin.HandlerFunc,传递的类型值为func(c *gin.Context){},
且一次可以传递多个该类型值。
(4) 在gin框架中,我们可以为每个路由添加任意数量的中间件。
*/

/*中间件注意事项:
(1) gin.Default() 默认使用了Logger和Recovery中间件
Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
Recovery中间件会recover任何panic,如果有panic的话,会写入500响应码。
(2) 如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。
(3) gin中间件中使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。
*/

// statCost 是一个统计请求耗时的中间件函数
func statCost() gin.HandlerFunc {
return func(c *gin.Context) {
// 开始计时
start := time.Now()
c.Next()
// 计算耗时
cost := time.Since(start)
log.Println(cost)
}
}

func main() {
/*7.1.1为某个路由单独注册中间件*/
//r := gin.Default()
//
//// 为"/test1"路由单独注册中间件(可以注册多个)
//r.GET("/test1", statCost(), func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//})
//
//err := r.Run(":8080")
//if err != nil {
// fmt.Println(err)
// return
//}
///*输出结果:
//(1) 浏览器访问 http://127.0.0.1:8080/test1,输出结果如下
//{
// "method": "GET"
//}
//(2) 终端输出内容
//2022/06/01 14:02:54 996.4µs
//*/

/*7.1.2为全局路由注册中间件*/
//r := gin.Default()
//// 注册一个全局中间件
//r.Use(statCost())
//
//r.GET("/test1", func(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//})
//
//err := r.Run()
//if err != nil {
// fmt.Println(err)
// return
//}
///*输出结果:
//(1) 浏览器访问 http://127.0.0.1:8080/test1,输出结果如下
//{
// "method": "GET"
//}
//(2) 终端输出内容
//2022/06/01 14:10:56 95µs
//*/

/*7.1.3为路由组注册中间件*/
// 为路由组注册中间件有两种写法
r := gin.Default()

// 方法一
userGroup := r.Group("/user", statCost())
{
userGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "userIndex",
})
})
}
/*输出结果:
(1) 浏览器访问 http://127.0.0.1:8080/user/index,输出结果如下
{
"msg": "userIndex"
}
(2) 终端输出内容
2022/06/01 14:21:20 413.8µs
*/

// 方法二
shopGroup := r.Group("/shop")
shopGroup.Use(statCost())
{
shopGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "shopIndex",
})
})
}
/*输出结果:
(1) 浏览器访问 http://127.0.0.1:8080/shop/index,输出结果如下
{
"msg": "shopIndex"
}
(2) 终端输出内容
2022/06/01 14:29:41 282.5µs
*/

err := r.Run(":8080")
if err != nil {
fmt.Println(err)
return
}

}

7.3 07gin中间件/7.2gin中间件next.go
package main

// 7.2gin中间件next

//import (
// "fmt"
// "github.com/gin-gonic/gin"
// "net/http"
//)
//
//// m1中间件函数
//func m1(c *gin.Context) {
// fmt.Println("m1 in")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m1 out")
//}
//
//// m2中间件函数
//func m2(c *gin.Context) {
// fmt.Println("m2 in")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m2 out")
//}
//
//// index函数(中间件)
//func index(c *gin.Context) {
// fmt.Println("index")
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//}
//
//func main() {
// r := gin.Default()
//
// // 全局注册中间件函数m1,m2
// r.Use(m1, m2)
//
// r.GET("/index", index)
//
// err := r.Run(":8080")
// if err != nil {
// fmt.Println(err)
// return
// }
// /*输出结果:
// (1) 浏览器访问 http://127.0.0.1:8080/index,输出结果如下
// {
// "method": "GET"
// }
// (2) 终端输出内容
// m1 in
// m2 in
// index
// m2 out
// m1 out
// */
//}

7.4 07gin中间件/7.3gin中间件abort.go
package main

// 7.3gin中间件abort

//import (
// "fmt"
// "net/http"
//
// "github.com/gin-gonic/gin"
//)
//
//// m1中间件函数
//func m1(c *gin.Context) {
// fmt.Println("m1 in")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m1 out")
//}
//
//// m2中间件函数
//func m2(c *gin.Context) {
// fmt.Println("m2 in")
// // 阻止调用后续的处理函数
// c.Abort()
// fmt.Println("m2 out")
//}
//
//// index函数(中间件)
//func index(c *gin.Context) {
// fmt.Println("index")
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//}
//
//func main() {
// r := gin.Default()
//
// // 全局注册中间件函数m1,m2
// r.Use(m1, m2)
//
// r.GET("/index", index)
//
// err := r.Run(":8080")
// if err != nil {
// fmt.Println(err)
// return
// }
// /*输出结果:
// (1) 浏览器访问 http://127.0.0.1:8080/index,得不到任何结果
// (2) 终端输出内容
// m1 in
// m2 in
// m2 out
// m1 out
// */
//}

7.5 07gin中间件/7.4gin中间件abort return.go
package main

// 7.4gin中间件abort return

//import (
// "fmt"
// "net/http"
//
// "github.com/gin-gonic/gin"
//)
//
//// m1中间件函数
//func m1(c *gin.Context) {
// fmt.Println("m1 in")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m1 out")
//}
//
//// m2中间件函数
//func m2(c *gin.Context) {
// fmt.Println("m2 in")
// // 阻止调用后续的处理函数
// c.Abort()
// // 返回结果
// return
// fmt.Println("m2 out")
//}
//
//// index函数(中间件)
//func index(c *gin.Context) {
// fmt.Println("index")
// c.JSON(http.StatusOK, gin.H{
// "method": "GET",
// })
//}
//
//func main() {
// r := gin.Default()
//
// // 全局注册中间件函数m1,m2
// r.Use(m1, m2)
//
// r.GET("/index", index)
//
// err := r.Run(":8080")
// if err != nil {
// fmt.Println(err)
// return
// }
// /*输出结果:
// (1) 浏览器访问 http://127.0.0.1:8080/index,得不到任何结果
// (2) 终端输出内容
// m1 in
// m2 in
// m1 out
// */
//}

7.6 07gin中间件/7.5gin中间件set.go
package main

// 7.5gin中间件set

//import (
// "fmt"
// "net/http"
//
// "github.com/gin-gonic/gin"
//)
//
//// m1中间件函数
//func m1(c *gin.Context) {
// fmt.Println("m1 in")
// // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// c.Set("name", "liuChang")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m1 out")
//}
//
//func m2(c *gin.Context) {
// fmt.Println("m2 in")
// // 调用后续的处理函数
// c.Next()
// fmt.Println("m2 out")
//}
//
//// index函数(中间件)
//func index(c *gin.Context) {
// fmt.Println("index")
// // 从上下文中取值(跨中间件存取值)
// name, ok := c.Get("name")
// if !ok {
// name = "匿名用户"
// }
// c.JSON(http.StatusOK, gin.H{
// "message": name,
// })
//}
//
//func main() {
// r := gin.Default()
//
// // 全局注册中间件函数m1,m2
// r.Use(m1, m2)
//
// r.GET("/index", index)
//
// err := r.Run(":8080")
// if err != nil {
// fmt.Println(err)
// return
// }
// /*输出结果:
// (1) 浏览器访问 http://127.0.0.1:8080/index,得到如下结果
// {
// "message": "liuChang"
// }
// (2) 终端输出内容
// m1 in
// m2 in
// index
// m2 out
// m1 out
// */
//}

7.7 07gin中间件/7.6gin中间件登录示例.go
package main

// 7.6gin中间件登录示例

import (
"fmt"
"net/http"

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

// 登录认证中间件函数
func authMiddleware(doCheck bool) gin.HandlerFunc {
// 连接数据库或者其它一些准备工作
return func(c *gin.Context) {
if doCheck {
// 存放具体的逻辑
// 是否登录的判断
// if 是登录用户
c.Next()
// else
// c.Abort()
} else {
c.Next()
}
}
}

// index函数(中间件)
func index(c *gin.Context) {
fmt.Println("index")
c.JSON(http.StatusOK, gin.H{
"method": "GET",
})
}

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

// 全局注册中间件函数authMiddleware
r.Use(authMiddleware(true))

r.GET("/index", index)

err := r.Run(":8080")
if err != nil {
fmt.Println(err)
return
}
/*输出结果:
(1) 浏览器访问 http://127.0.0.1:8080/index,得到如下结果
{
"method": "GET"
}
(2) 终端输出内容
index
*/
}

7.8 gin中间件原理图示

07gin中间件/image/01gin中间件.png

01gin中间件

07gin中间件/image/02gin中间件next.png

02gin中间件next

07gin中间件/image/03gin中间件next.png

03gin中间件next

07gin中间件/image/04gin中间件abort.png

04gin中间件abort

07gin中间件/image/05gin中间件abort return.png

05gin中间件abort return

8 路由拆分和注册

8.1 代码列表

image-20220602144411139

8.2 08ginRouterSplitRegister/routerSplit01/

routers/routers.go

package routers

// 我们在routers.go文件中定义并注册路由信息

import (
"net/http"

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

// 中间件处理函数
func helloHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "hello world",
})
}

// SetupRouter 配置路由信息
func SetupRouter() *gin.Engine {
r := gin.Default()
r.GET("/hello", helloHandler)
return r
}

main.go

package main

import (
"02Gin/08ginRouterSplitRegister/routerSplit01/routers"
"fmt"
)

// 8.1路由拆分成单独文件或包
/*说明:
(1) 基础的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo。
(2) 当项目的规模增大后就不太适合继续在项目的main.go文件中去实现路由注册相关逻辑了,我们
会倾向于把路由部分的代码都拆分出来,形成一个单独的文件或包。
*/

func main() {
// 在main.go中调用定义好的SetupRouter函数
r := routers.SetupRouter()

if err := r.Run(":8080"); err != nil {
fmt.Printf("startup service failed: %v\n", err)
return
}
/*输出结果:
(1) 在浏览器中访问 http://127.0.0.1:8080/hello,得到如下结果
{
"msg": "hello world"
}
*/
}

8.3 08ginRouterSplitRegister/routerSplit02/

routers/blog.go

package routers

import (
"net/http"

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

// 中间件函数
func blogHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"mgs": "blog",
})
}

// LoadBlog 函数,将blog相关的路由注册到指定的路由器
func LoadBlog(e *gin.Engine) {
e.GET("/blog", blogHandler)
}

routers/shop.go

package routers

import (
"net/http"

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

// 中间件函数
func shopHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "shop",
})
}

// LoadShop 函数,将shop相关的路由注册到指定的路由器
func LoadShop(e *gin.Engine) {
e.GET("/shop", shopHandler)
}

main.go

package main

import (
"02Gin/08ginRouterSplitRegister/routerSplit02/routers"
"fmt"

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

// 8.2路由拆分成多个文件
// 当我们的业务规模继续膨胀,单独的一个routers文件或包已经满足不了我们的需求了。

func main() {
// 在main函数中实现最终的注册逻辑
r := gin.Default()

routers.LoadBlog(r)
routers.LoadShop(r)

if err := r.Run(":8080"); err != nil {
fmt.Printf("startup service failed: %v\n", err)
return
}
}

/*输出结果:
(1) 在浏览器中访问 http://127.0.0.1:8080/blog,显示结果如下
{
"mgs": "blog"
}
(2) 在浏览器中访问 http://127.0.0.1:8080/shop,显示结果如下
{
"msg": "shop"
}
*/

8.4 08ginRouterSplitRegister/routerSplit03/

app/blog/handler.go

package blog

import (
"net/http"

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

// 中间件函数
func blogHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"mgs": "blog",
})
}

app/blog/router.go

package blog

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

// 定义blog相关的路由信息

func Routers(e *gin.Engine) {
e.GET("/blog", blogHandler)
}

app/shop/handler.go

package shop

import (
"net/http"

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

// 中间件函数
func shopHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "shop",
})
}

app/shop/router.go

package shop

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

// 定义shop相关路由信息

func Routers(e *gin.Engine) {
e.GET("/shop", shopHandler)
}

routers/routers.go

package routers

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

// 根据需要定义Include函数用来注册子app中定义的路由,Init函数用来进行路由的初始化操作。

type Option func(*gin.Engine)

var options = []Option{}

// Include 函数注册app的路由配置
func Include(opts ...Option) {
options = append(options, opts...)
}

// Init 函数初始化
func Init() *gin.Engine {
r := gin.Default()

for _, opt := range options {
opt(r)
}

return r
}

main.go

package main

import (
"02Gin/08ginRouterSplitRegister/routerSplit03/app/blog"
"02Gin/08ginRouterSplitRegister/routerSplit03/app/shop"
"02Gin/08ginRouterSplitRegister/routerSplit03/routers"
"fmt"
)

// 8.3路由拆分到不同的APP
// 有时候项目规模实在太大,那么我们就更倾向于把业务拆分的更详细一些,例如把不同的业务代码拆分成不同的APP。
// 因此我们在项目目录下单独定义一个app目录,用来存放我们不同业务线的代码文件,这样就很容易进行横向扩展。

func main() {
// 先注册子app中的路由,然后再进行路由的初始化

// 加载多个APP的路由配置
routers.Include(shop.Routers, blog.Routers)

// 初始化路由
r := routers.Init()

if err := r.Run(":8080"); err != nil {
fmt.Printf("startup service failed, err:%v\n", err)
}

}

/*输出结果:
(1) 在浏览器中访问 http://127.0.0.1:8080/blog,显示结果如下
{
"mgs": "blog"
}
(2) 在浏览器中访问 http://127.0.0.1:8080/shop,显示结果如下
{
"msg": "shop"
}
*/


posted @ 2022-06-03 23:39  云起时。  阅读(106)  评论(0编辑  收藏  举报