目录
一、Gin
1、Ajax
AJAX 即“Asynchronous Javascript And XML”(异步 JavaScript和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
AJAX的最大的特点: 异步访问,局部刷新。
案例:Ajax之验证用户名是否被占用
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part02/templates/**/*")
//指定js文件:
r.Static("/s", "part02/static")
r.GET("/test1", myfunc.Test1)
r.POST("/getUserInfo", myfunc.Test2)
r.POST("/ajaxpost", myfunc.Test3)
r.Run()
}
myfunc.go
func Test1(context *gin.Context) {
context.HTML(200, "demo01/hello01.html", nil)
}
func Test3(context *gin.Context) {
//获取post-ajax请求的数据,获取对应的参数:
uname := context.PostForm("uname")
fmt.Println(uname)
fmt.Println(uname == "丽丽")
if uname == "丽丽" {
context.JSON(200, gin.H{
"msg": "用户名重复了!",
})
} else {
context.JSON(200, gin.H{
"msg": "",
})
}
}
hello01.html 注意:引入jQuery.min.js
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/s/css/mycss.css">
<script src="/s/js/jQuery.min.js"></script>
</head>
<body>
用户form表单<br>
<form action="/getUserInfo" method="post">
用户名:<input type="text" name="username" id="uname"><span id="errmsg"></span><br>
密码:<input type="password" name="pwd">
<input type="submit" value="提交">
</form>
<script>
//获取用户名的文本框
var unametext = document.getElementById("uname");
//给文本框绑定一个事件:失去焦点的时候会触发后面的函数的事件
unametext.onblur = function () {
//获取文本框的内容:
var uname = unametext.value;
//alert(uname)可以弹出数据,验证代码的正确性
//局部刷新:通过ajax技术来实现数据的校验 ---》 后台 :异步访问,局部刷新
//调用ajax方法需要传入json格式的数据: $.ajax({属性名:属性值,属性名:属性值,方法名:方法})
$.ajax({
url : "/ajaxpost",//请求路由
type : "POST",//请求类型 GET、POST
data : {//向后端发送的数据,以json格式向后传递
"uname" : uname
},
success : function (info) {//后台响应成功会调用函数,info-后台响应的数据封装到info中,info名字可以随便起
document.getElementById("errmsg").innerText = info["msg"]
},
fail : function () {后台响应失败会调用函数
}
})
}
</script>
</body>
</html>
{{end}}
测试:
2、文件上传
2.1、form表单中文件上传(单个文件)
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part03/templates/**/*")
r.GET("/userindex", myfunc.Test1)
r.POST("/savefile", myfunc.Test2)
r.Run()
}
myfunc.go
func Test1(context *gin.Context) {
context.HTML(200, "demo01/hello01.html", nil)
}
func Test2(context *gin.Context) {
//获取前端传入的文件:
file, _ := context.FormFile("myfile")
fmt.Println(file.Filename)
//加入一个时间戳:
time_int := time.Now().Unix()
time_str := strconv.FormatInt(time_int, 10) //10:十进制
//保存在我的本地:
context.SaveUploadedFile(file, "e://"+time_str+file.Filename)
//响应一个字符串:
context.String(200, "文件上传成功")
}
hello01.html
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/s/css/mycss.css">
</head>
<body>
用户form表单<br>
<form action="/savefile" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="submit" value="提交">
</form>
</body>
</html>
{{end}}
测试:
2.2、form表单中文件上传(多个文件)
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part03/templates/**/*")
r.GET("/userindex", myfunc.Test1)
r.POST("/savefile", myfunc.Test3)
r.Run()
}
myfunc.go
func Test3(context *gin.Context) {
//先获取form表单
form, _ := context.MultipartForm()
//在form表单中获取name相同的文件:
files := form.File["myfile"] //File是个Map,通过key获取value部分
//files就是name相同的多个文件:挨个处理---遍历处理:
for _, file := range files {
//加入一个时间戳:
time_int := time.Now().Unix()
time_str := strconv.FormatInt(time_int, 10) //10:十进制
//保存在我的本地:
context.SaveUploadedFile(file, "e://"+time_str+file.Filename)
}
//响应一个字符串:
context.String(200, "文件上传成功")
}
hello01.html
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/s/css/mycss.css">
</head>
<body>
用户form表单<br>
<form action="/savefile" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
<input type="file" name="myfile">
<input type="file" name="myfile">
<input type="submit" value="提交">
</form>
</body>
</html>
{{end}}
测试:
2.3、ajax上传单个文件
注意利用ajax上传文件的话,在ajax中需要加两个参数:
(1)contentType:false
默认为true,当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
(2)processData:false,
目的是防止上传文件中出现分界符导致服务器无法正确识别文件起始位置。
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part04/templates/**/*")
r.Static("/s", "part04/static")
r.GET("/userindex", myfunc.Test1)
r.POST("/savefile", myfunc.Test4)
r.Run()
}
myfunc.go
func Test4(context *gin.Context) {
//获取前端传入的文件:
file, _ := context.FormFile("file")
fmt.Println(file.Filename)
//加入一个时间戳:
time_int := time.Now().Unix()
time_str := strconv.FormatInt(time_int, 10) //10:十进制
//保存在我的本地:
context.SaveUploadedFile(file, "e://"+time_str+file.Filename)
//响应一个字符串:
context.String(200, "文件上传成功")
}
hello01.html
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/s/js/jQuery.min.js"></script>
</head>
<body>
用户form表单<br>
<form action="/savefile" method="post">
<input type="file" class="myfile" multiple="multiple">
<input type="button" value="提交按钮" id="btn">
</form>
<script>
//获取按钮
var btn = document.getElementById("btn");
//给按钮加入一个单击事件:
btn.onclick = function () {
//创建存放form表单的数据:
var form_data = new FormData();
//在form_data添加要传入到后台的元素:
form_data.append("file",$(".myfile")[0].files[0])
//利用ajax向后台传递数据:
$.ajax({
url : "/savefile",
type : "POST",
data : form_data,
contentType:false,
processData:false,
success : function () {
}
})
}
</script>
</body>
</html>
{{end}}
2.4、ajax上传多个文件
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part04/templates/**/*")
r.Static("/s", "part04/static")
r.GET("/userindex", myfunc.Test1)
r.POST("/savefile", myfunc.Test5)
r.Run()
}
myfunc.go
func Test5(context *gin.Context) {
//先获取form表单
form, _ := context.MultipartForm()
//在form表单中获取name相同的文件:
files := form.File["myfile"] //File是个Map,通过key获取value部分
//files就是name相同的多个文件:挨个处理---遍历处理:
for _, file := range files {
//加入一个时间戳:
time_int := time.Now().Unix()
time_str := strconv.FormatInt(time_int, 10) //10:十进制
//保存在我的本地:
context.SaveUploadedFile(file, "e://"+time_str+file.Filename)
}
//响应一个字符串:
context.String(200, "文件上传成功")
}
hello01.html
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/s/js/jQuery.min.js"></script>
</head>
<body>
用户form表单<br>
<form action="/savefile" method="post">
<input type="file" class="myfile">
<input type="file" class="myfile">
<input type="file" class="myfile">
<input type="button" value="提交按钮" id="btn">
</form>
<script>
//获取按钮
var btn = document.getElementById("btn");
//给按钮加入一个单击事件:
btn.onclick = function () {
//创建存放form表单的数据:
var form_data = new FormData();
//获取多个文件:
var myfiles = $(".myfile");
//对多个文件进行遍历,每一个添加到form_data中去:
for (var i = 0;i < myfiles.length;i++){
form_data.append("myfile",myfiles[i].files[0]);
}
//利用ajax向后台传递数据:
$.ajax({
url : "/savefile",
type : "POST",
data : form_data,
contentType:false,
processData:false,
success : function () {
}
})
}
</script>
</body>
</html>
{{end}}
响应重定向:是请求服务器后,服务器通知浏览器,让浏览器去自主请求其他资源的一种方式。
3、模板语法
【1】模板
在写动态页面的网站的时候,我们常常将不变的部分提出成为模板,可变部分通过后端程序的渲染来生成动态网页,golang也支持模板渲染。
【2】模板内内嵌的语法支持,全部需要加 {{}} 来标记。
【3】在模板文件内, . 代表了当前位置的上下文:
(1)在非循环体内,. 就代表了后端传过来的上下文
(2)在循环体中,. 就代表了循环的上下文
【4】在模板文件内, $ 代表了模板根级的上下文
【5】在模板文件内, $. 代表了模板根级的上下文
【6】字符串:{{ “abc,Hello World ” }}
【7】原始字符串:{{ `abc` }} {{ `a` }} 不会转义
【8】字节类型:{{ ' a' }} ---> 97 会转义
【9】打印:
打印字符串: {{ print "Hello World" }}
nil类型:{{ print nil }}
{{define "demo01/hello.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
打印字符串: {{ print "Hello World" }}
nil类型:{{ print nil }}
变量的定义:
{{$name := "XIAOMING"}}
变量的使用:
{{$name}}
</body>
</html>
{{end}}
【10】if:
方式1:
{{if .condition}}
{{end}}
方式2:
{{if .condition1}}
{{else}}
{{end}}
方式3:
{{if .condition1}}
{{else if .contition2}}
{{end}}
内置的模板函数
not 非
and 与
or 或
eq 等于
ne 不等于
lt 小于 (less than)
le 小于等于
gt 大于
ge 大于等于
【11】range循环
{{range .arr}}
{{.}}
{{$.age}}
{{end}}
<br>
{{range $i,$v := .arr}}
{{$i}}
{{$v}}
{{end}}
【12】with 关键字
{{ with pipeline }} T1 {{ end }}
{{ with pipeline }} T1 {{ else }} T0 {{ end }}
其中 pipeline 为判断条件,如满足就将 . 设为 pipeline 的值并执行 T1,不修改外面的 . 。
否则执行 T0。
{{define "demo01/hello.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<br>
获取结构体中内容:
{{.stu.Age}}
{{.stu.Name}}
<br>
启动with关键字:
{{with .stu}}
{{.Age}}
{{.Name}}
{{else}}
暂无数据
{{end}}
</body>
</html>
{{end}}
【13】template:作用:引入另一个模板文件
{{template "模板名" pipeline}}
PS:管道(传递数据的)
PS:引入的模板文件中也要用{ {define "路径"} } { {end} }包含
PS:如果想在引入的模板中也需要获取动态数据,必须使用 . 访问当前位置的上下文
hello.html
{{define "demo01/hello.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<br>
内嵌另外模板:
{{/* 如果想要传递内容到内嵌模板中,可以通过.上下文进行传递,和内嵌页面共享上下文数据*/}}
{{template "templates/demo01/hello2.html" .}}
</body>
</html>
{{end}}
hello2.html
{{define "templates/demo01/hello2.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是一个内嵌页面。。。
{{with .stu}}
{{.Age}}
{{.Name}}
{{else}}
暂无数据
{{end}}
</body>
</html>
{{end}}
模板函数
【1】print 打印字符串
【2】printf 按照格式化的字符串输出
格式:参照:Go中:fmt.Sprintf
【3】len 返回对应类型的长度(map, slice, array, string, chan)
【4】管道传递符: |
函数中使用管道传递过来的数值
【5】括号提高优先级别:()
【6】and 只要有一个为空,则整体为空;如果都不为空,则返回最后一个
【7】or 只要有一个不为空,则返回第一个不为空的;如果都是空,则返回空
【8】not 用于判断返回布尔值,如果有值则返回false,没有值则返回true
【9】index 读取指定类型对应下标的值(map, slice, array, string)
【10】eq:等于equal,返回布尔值
【11】ne:不等于 not equal,返回布尔值
【12】lt:小于 less than,返回布尔值
【13】le:小于等于less equal,返回布尔值
【14】gt:大于 greater than,返回布尔值
【15】ge:大于等于 greater equal,返回布尔值
【16】日期格式化 Format
实现了时间的格式化,返回字符串,设置时间格式比较特殊,需要固定方式,不能轻易改变
【17】自定义模板函数:
【17】自定义模板函数:
myfunc.go
// 定义一个函数:
func Add(num1 int, num2 int) int {
return num1 + num2
}
main.go注册函数
import (
"github.com/gin-gonic/gin"
"html/template"
"test_gin/part05/myfunc"
)
func main() {
r := gin.Default()
//注册函数:FuncMap是html/FuncMap
r.SetFuncMap(template.FuncMap{
//键值对的作用:key指定前端调用的名字,value指定的是后端对应的函数
"add": myfunc.Add,
})
r.LoadHTMLGlob("part05/templates/**/*")
r.Static("/s", "part05/static")
r.GET("/test1", myfunc.Test1)
r.Run()
}
hello01.html使用函数
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<br>
调用add函数:
{{add 11 82}}
</body>
</html>
{{end}}
测试:
4、数据绑定
数据绑定:能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到后端指定的结构体对象中去
4.1、表单数据的绑定
main.go
import (
"github.com/gin-gonic/gin"
"test_gin/part06/myfunc"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("part06/templates/**/*")
r.GET("/userindex", myfunc.Test1)
r.GET("/toFormBind", myfunc.Test2)
r.Run()
}
myfunc.go
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Test1(context *gin.Context) {
//获取路径中的参数值:
context.HTML(200, "demo01/hello01.html", nil)
}
type User struct {
//加入标签:绑定的时候需要指定将form表单中的username绑定到Username上
Uername string `form:"username"`
//加入标签:绑定的时候需要指定将form表单中的pwd绑定到Pwd上
Pwd string `form:"pwd"`
}
func Test2(context *gin.Context) {
//定义结构体对象:
var user User
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
hello01.html
{{define "demo01/hello01.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/toFormBind" >
用户名:<input type="text" name="username">
密码:<input type="password" name="pwd">
<input type="submit" value="提交">
</form>
</body>
</html>
{{end}}
测试:
4.2、路径中参数的绑定
路径中参数的绑定 又叫:querystring类型的绑定,页面以get方式请求数据,WEB SERVER将请求数据放入名为 QUERY_STRING的环境变量中,所以可以称作为querystring类型的绑定
方式同表单数据的绑定,标签利用的依然是form标签
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part06/templates/**/*")
r.GET("/userindex2", myfunc.Test3)
r.Run()
}
myfunc.go
func Test3(context *gin.Context) {
//定义结构体对象:
var user User
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
测试:
http://localhost:8080/userindex2?username=lwz&pwd=123123
4.3、JSON数据的绑定
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part06/templates/**/*")
//指定文件:
r.Static("/s", "part06/static")
r.GET("/userindex3", myfunc.Test4)
r.POST("/toajax", myfunc.Test5)
r.Run()
}
hello02.html
{{define "demo02/hello02.html"}}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/s/js/jQuery.min.js"></script>
</head>
<body>
<form>
用户名字:<input type="text" name="uname" id="uname">
年龄:<input type="text" name="age" id="age">
<input type="button" value="提交" id="btn">
</form>
<script>
//获取按钮
var btn = document.getElementById("btn")
//给按钮绑定单击事件:
btn.onclick = function () {
//获取表单中元素:
var uname = document.getElementById("uname").value;
var age = document.getElementById("age").value;
//通过ajax方式向后台传递:
$.ajax({
url : "/toajax",
type : "POST",
//如果向后台提交的是Json字符串需要加入这个属性:
contentType : "application/json",
//JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串。
data : JSON.stringify({
"uname" : uname,
"age" : Number(age)
}),
success : function (info) {
alert(info["msg"])
}
})
}
</script>
</body>
</html>
{{end}}
myfunc.go
type User2 struct {
Uname string `json:"uname"`
Age int `json:"age"`
}
func Test4(context *gin.Context) {
//获取路径中的参数值:
context.HTML(200, "demo02/hello02.html", nil)
}
func Test5(context *gin.Context) {
//创建结构体示例:
var user User2
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.JSON(404, gin.H{
"msg": "绑定失败",
})
} else {
context.JSON(200, gin.H{
"msg": "绑定成功",
})
}
}
测试
4.4、URI数据的绑定
URI : 统一资源标识符 a/login
URL:统一资源定位符 http://localhost:8080/a/login
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part06/templates/**/*")
r.GET("/userindex4/:uname/:age", myfunc.Test6)
r.Run()
}
myfunc.go
type User2 struct {
Uname string `json:"uname" uri:"uname" form:"uname"`
Age int `json:"age" uri:"age" form:"age"`
}
func Test6(context *gin.Context) {
//创建结构体示例:
var user User2
//数据绑定:
err := context.ShouldBindUri(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
测试:
5、路由组
路由组:将不同的路由按照版本、模块进行不同的分组,利于维护,方便管理。
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part08/templates/**/*")
//指定文件:
r.Static("/s", "part08/static")
//按照版本号对路由进行分组:
v1 := r.Group("/version01")
{
v1.GET("/userindex", myfunc.Test1)
v1.GET("/toFormBind", myfunc.Test2)
v1.GET("/userindex2", myfunc.Test3)
}
v2 := r.Group("/version02")
{
v2.GET("/userindex3", myfunc.Test4)
v2.POST("/toajax", myfunc.Test5)
v2.GET("/userindex4/:uname/:age", myfunc.Test6)
}
r.Run()
}
测试:
路由组抽取
路由抽取的规则:
1.总路由中设置路由组(总路由)
2.模块中的路由负责映射具体的业务(模块路由)
main.go
package main
import (
"github.com/gin-gonic/gin"
"test_gin/part09/router"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("part09/templates/**/*")
//指定文件:
r.Static("/s", "part09/static")
//指定总路由:
router.Router(r)
r.Run()
}
总路由
package router
import (
"github.com/gin-gonic/gin"
"test_gin/part09/bill"
"test_gin/part09/external"
)
func Router(r *gin.Engine) {
b := r.Group("/bill")
e := r.Group("/external")
//模块分组:
bill.Router(b)
external.Router(e)
}
模块路由:(bill)
package bill
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Router(r *gin.RouterGroup) {
r.GET("/userindex", Test1)
r.GET("/toFormBind", Test2)
r.GET("/userindex2", Test3)
}
func Test1(context *gin.Context) {
//获取路径中的参数值:
context.HTML(200, "demo01/hello01.html", nil)
}
type User struct {
Uername string `form:"username"`
Pwd string `form:"pwd"`
}
func Test2(context *gin.Context) {
//定义结构体对象:
var user User
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
func Test3(context *gin.Context) {
//定义结构体对象:
var user User
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
模块路由(external)
package external
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Router(r *gin.RouterGroup) {
r.GET("/userindex3", Test4)
r.POST("/toajax", Test5)
r.GET("/userindex4/:uname/:age", Test6)
}
func Test4(context *gin.Context) {
//获取路径中的参数值:
context.HTML(200, "demo02/hello02.html", nil)
}
func Test5(context *gin.Context) {
//创建结构体示例:
var user User2
//数据绑定:
err := context.ShouldBind(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.JSON(404, gin.H{
"msg": "绑定失败",
})
} else {
context.JSON(200, gin.H{
"msg": "绑定成功",
})
}
}
type User2 struct {
Uname string `json:"uname" uri:"uname" form:"uname"`
Age int `json:"age" uri:"age" form:"age"`
}
func Test6(context *gin.Context) {
//创建结构体示例:
var user User2
//数据绑定:
err := context.ShouldBindUri(&user)
//打印结构体对象的内容:
fmt.Println(user)
if err != nil {
context.String(404, "绑定失败")
} else {
context.String(200, "绑定成功")
}
}
测试:
6、中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数(中间件函数),这个钩子函数就叫中间件。
中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等等。
6.1、自定义中间件
middleware/middleware.go
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func MiddleWare01(context *gin.Context) {
fmt.Println("自定义中间件-方式1,在此处编写统一业务逻辑")
}
func MiddleWare02() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("自定义中间件-方式2,在此处编写统一业务逻辑")
}
}
main.go
package main
import (
"github.com/gin-gonic/gin"
"test_gin/part10/middleware"
"test_gin/part10/router"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("part10/templates/**/*")
//指定文件:
r.Static("/s", "part10/static")
r.Use(middleware.MiddleWare01)
r.Use(middleware.MiddleWare02())
//指定总路由:
router.Router(r)
r.Run()
}
测试:
6.2、中间件链
如果定义众多中间件,会形成一条中间件链,而通过Next方法来对后面的中间件进行执行。
Next 函数作用:继续走中间件链中的下一个中间件。
middleware/middleware.go
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func MiddleWare01(context *gin.Context) {
fmt.Println("这是自定义的中间件1-开始")
context.Next()
fmt.Println("这是自定义的中间件1-结束")
}
func MiddleWare02() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("这是自定义的中间件2-开始")
fmt.Println("这是自定义的中间件2-结束")
}
}
func MiddleWare03() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("这是自定义的中间件3-开始")
context.Next()
fmt.Println("这是自定义的中间件3-结束")
}
}
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part10/templates/**/*")
//指定文件:
r.Static("/s", "part10/static")
r.Use(middleware.MiddleWare01)
r.Use(middleware.MiddleWare03())
r.Use(middleware.MiddleWare02())
//指定总路由:
router.Router(r)
r.Run()
}
测试结果:
6.3、终止中间件
Abort()方法的作用终止调用整个链条
middleware/middleware.go
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
)
func MiddleWare01(context *gin.Context) {
fmt.Println("这是自定义的中间件1-开始")
context.Next()
fmt.Println("这是自定义的中间件1-结束")
}
func MiddleWare02() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("这是自定义的中间件2-开始")
fmt.Println("这是自定义的中间件2-结束")
}
}
func MiddleWare03() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("这是自定义的中间件3-开始")
if 4 > 2 {
//终止链条:
context.Abort()
//return
}
context.Next()
fmt.Println("这是自定义的中间件3-结束")
}
}
main.go
func main() {
r := gin.Default()
r.LoadHTMLGlob("part10/templates/**/*")
//指定文件:
r.Static("/s", "part10/static")
r.Use(middleware.MiddleWare01)
r.Use(middleware.MiddleWare03())
r.Use(middleware.MiddleWare02())
//指定总路由:
router.Router(r)
r.Run()
}
Abort()方法的作用:终止中间件链条的调用,但是逻辑代码不会被影响
测试:
如果使用return的话,就是不行的,return结束逻辑代码,但是对next的调用不起作用:
middleware/middleware.go
func MiddleWare03() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("这是自定义的中间件3-开始")
if 4 > 2 {
//终止链条:
//context.Abort()
return
}
context.Next()
fmt.Println("这是自定义的中间件3-结束")
}
}
测试:
6.4、中间件的作用域
1、全局中间件:针对所有的路由都生效
加入位置:main函数中加,例如上面例子
2、路由组中间件:针对某个路由组生效:
加入位置:在总路由分组中加入即可:
router/router.go
package router
import (
"github.com/gin-gonic/gin"
"test_gin/part09/bill"
"test_gin/part09/external"
"test_gin/part10/middleware"
)
func Router(r *gin.Engine) {
b := r.Group("/bill")
b.Use(middleware.MiddleWare01)
e := r.Group("/external")
//模块分组:
bill.Router(b)
external.Router(e)
}
3、局部中间件:针对具体的某一个路由生效
位置:在具体路由和函数之间的参数加上中间件
package bill
import (
"fmt"
"github.com/gin-gonic/gin"
"test_gin/part10/middleware"
)
func Router(r *gin.RouterGroup) {
r.GET("/userindex", middleware.MiddleWare01, Test1)
r.GET("/toFormBind", Test2)
r.GET("/userindex2", Test3)
}
...
难留少年时,总有少年来!
无论你是年轻还是年长,所有程序员都需要记住:时刻努力学习新技术,否则就会被时代抛弃!