介绍
01【Gin框架】Gin环境搭建 Gin程序的热加载 Gin路由 GET POST PUT DELETE | 55 Gin是一个Golang编写的轻量级http web框架,运行速度非常快,如果你是性能和搞笑的追求者,我们推荐你使用Gin框架。 Gin最擅长的就是Api接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用Gin。 https://gin-gonic.com/zh-cn https://github.com/gin-gonic/gin
Gin环境
01【Gin框架】Gin环境搭建 Gin程序的热加载 Gin路由 GET POST PUT DELETE | 05:59
package main
import (
"github.com/gin-gonic/gin" // 引入gin
"net/http"
)
func main() {
r := gin.Default() // 创建默认路由引擎
// GET /test
r.GET("/test", func(context *gin.Context) {
context.String(http.StatusOK, "测试")
})
r.Run(":8000") // 启动一个web服务,监听8000端口
}默认路由
r.GET("/", func(context *gin.Context) {
context.String(http.StatusOK, "首页")
})返回JSON数据
02 【Gin框架】Gin路由中响应数据 c.String() c.JSON() c.JSONP() c.XML() c.HTML() | 04:25
// 返回json数据
r.GET("/json", func(context *gin.Context) {
context.JSON(http.StatusOK, map[string]interface{}{
"success": true,
"msg": "你好",
})
})
// 返回json数据
r.GET("/json2", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
"msg": "你好",
})
})使用结构体
02 【Gin框架】Gin路由中响应数据 c.String() c.JSON() c.JSONP() c.XML() c.HTML() | 07:21
type Article struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
r := gin.Default()
a := Article{
Name: "张三",
Age: 18,
}
// 返回json数据
r.GET("/json", func(context *gin.Context) {
context.JSON(http.StatusOK, a)
})
}JSONP
02 【Gin框架】Gin路由中响应数据 c.String() c.JSON() c.JSONP() c.XML() c.HTML() | 10:38 jsonp主要用来解决跨域问题,url后加?callback=xxx,可以处理回调函数
r.GET("/jsonp", func(context *gin.Context) {
context.JSONP(http.StatusOK, Article{
Name: "张三",
Age: 20,
})
})XML数据
02 【Gin框架】Gin路由中响应数据 c.String() c.JSON() c.JSONP() c.XML() c.HTML() | 13:17
r.GET("/xml", func(context *gin.Context) {
context.XML(http.StatusOK, Article{
Name: "张三",
Age: 20,
})
})渲染模板
02 【Gin框架】Gin路由中响应数据 c.String() c.JSON() c.JSONP() c.XML() c.HTML() | 15:38
模板(template/goods.html)
使用{{.xxx}}获取数据变量
头尾增加:{{ define "default/index.html" }},{{ end }} 赋予模板自定义名称
{{ define "default/index.html" }}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>商品页面</title>
</head>
<body>
<h2>{{.title}}</h2>
</body>
</html>
{{ end }}go代码:
使用r.LoadHTMLGlob("template/*")加载静态资源
/**/**/* 表示多层
r := gin.Default()
r.LoadHTMLGlob("template/*") // 加载静态资源 /**/**/* 表示多层
r.GET("/goods", func(context *gin.Context) {
context.HTML(http.StatusOK, "default/index.html", gin.H{ //default/index.html html{{define}}中配置的模板名称
"title": "我是后台数据",
})
})
r.Run(":8000") // 启动一个web服务模板语法
变量
03【Gin框架】Gin HTML模板渲染以及模板语法 (上) | 14:49 在模板中把数据赋值给变量
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>商品页面</title>
</head>
<body>
{{$t := .title}} //定义变量
<h2>{{$t}}</h2>// 使用变量
</body>
</html>条件判断、比较函数
03【Gin框架】Gin HTML模板渲染以及模板语法 (上) | 16:38
| 条件 | 解释 |
|---|---|
| eq | 如果 arg1 == arg2 则返回真 |
| ne | 如果 arg1 != arg2 则返回真 |
| lt | 如果 arg1 < arg2 则返回真 |
| le | 如果 arg1 ⇐ arg2 则返回真 |
| gt | 如果 arg1 > arg2 则返回真 |
| ge | 如果 arg1 >= arg2 则返回真 |
{{if ge .a 60}}
<p>及格</p>
{{else if ge .a 90}}
<p>优秀</p>
{{else}}
<p>不及格</p>循环range
03【Gin框架】Gin HTML模板渲染以及模板语法 (上) | 21:23
{{range $key,$value := .xList}}
<li>{{$key}}=={{$value}}</li>
{{range}}Width赋值结构体
03【Gin框架】Gin HTML模板渲染以及模板语法 (上) | 27:19
{{with .object}}
{{.name}}
{{.age}}
{{end}}预设函数
04【Gin框架】Gin HTML模板渲染以及模板语法 自定义模板函数 静态文件服务(下) | 50
- and 函数返回它的第一个empty参数或者最后一个参数; 就是说”and x y”等价于”if x then y else x”;所有参数都会执行;
- or 返回第一个非empty参数或者最后一个参数; 亦即”or x y”等价于”if x then x else y”;所有参数都会执行;
- not 返回它的单个参数的布尔值的否定
- len 返回它的参数的整数类型长度
- index 执行结果为第一个参数以剩下的参数为索引/键指向的值;
{{len .xxx}}自定义模板函数
04【Gin框架】Gin HTML模板渲染以及模板语法 自定义模板函数 静态文件服务(下) | 03:21 注册函数:
func UnixToTime (timestamp int) string{
t := time.Unix(int64(timestamp),0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
}使用函数:
{{UnixToTime .date}}嵌套模板
04【Gin框架】Gin HTML模板渲染以及模板语法 自定义模板函数 静态文件服务(下) | 13:18 在某个模板中加载其他模板:
{{template "xxx/xxxx.html" .}}静态文件服务
r := gin.Default()
r.LoadHTMLGlob("template/*")
r.Static("/staticUrl", "./static") // static目录,通过/staticUrl/xxx.xx访问路由传值
05【Gin框架】Get Post以及动态路由传值、Get Post数据解析到结构体、Post Xml数据解析到结构体 | 02:41
GET
context.Query获取参数值context.DefaultQuery获取参数值,为空则使用默认值
func main() {
r := gin.Default()
r.GET("/", func(context *gin.Context) {
username := context.Query("username")
age := context.Query("age")
page := context.DefaultQuery("page", "1")
context.JSONP(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
r.Run(":8000") // 启动一个web服务
}POST
使用context.PostForm
r.POST("/addUser", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
age := context.DefaultPostForm("age", "20")
context.JSONP(http.StatusOK, gin.H{
"username": username,
"password": password,
"age": age,
})
})绑定到结构体
结构体需要使用form标签指定参数名
type User struct {
Username string `form:"username" json:"username"`
Password string `form:"password" json:"password"`
Age int `form:"age" json:"age"`
}
func main() {
r := gin.Default()
r.POST("/addUser", func(context *gin.Context) {
user := &User{}
err := context.ShouldBind(user)
if err != nil {
context.JSON(http.StatusOK, gin.H{
"error": err.Error(),
})
} else {
context.JSON(http.StatusOK, user)
}
})
r.Run(":8000") // 启动一个web服务
}路由分组
06 【Gin框架】Gin路由分组 Gin路由文件抽离 | 38
使用r.Group()来分组路由,即加url前缀
r := gin.Default()
defaultRouters := r.Group("/")
{
defaultRouters.GET("/", func(context *gin.Context) {
context.String(http.StatusOK, "首页")
})
}
apiRouters := r.Group("/api")
{
apiRouters.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"test": "api",
})
})
}
r.Run(":8000") // 启动一个web服务路由抽离
新建包用来存放路由代码,调用方法加载路由
func main() {
r := gin.Default()
routers.DefaultRouters(r)
routers.Test(r)
r.Run(":8000") // 启动一个web服务
}
routers/apiRouters.go
package routers
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Test(r *gin.Engine) {
apiRouters := r.Group("/api")
{
apiRouters.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"test": "api",
})
})
}
}自定义控制器
07 【Gin框架】Gin中自定义控制器以及实现控制器的继承 | 32 另建Controller包,定义结构体,写入业务函数,路由时指向结构体函数 controller/admin/userController.go
package admin
import (
"github.com/gin-gonic/gin"
"net/http"
)
type UserController struct {
}
func (con UserController) Index(context *gin.Context) {
context.String(http.StatusOK, "用户index")
}
func (con UserController) Add(context *gin.Context) {
context.String(http.StatusOK, "用户ADD")
}
routers/userRouters.go
package routers
import (
"github.com/gin-gonic/gin"
"go_demo/controller/admin"
)
func UserRoutersInit(r *gin.Engine) {
apiRouters := r.Group("/api")
{
apiRouters.GET("/userIndex", admin.UserController{}.Index)
apiRouters.POST("/userAdd", admin.UserController{}.Add)
}
}
中间件
08【Gin框架】Gin中间件详解 路由中间件 全局中间件 路由分组中间件 | 15 GET、POST等方法可以传入多个回调函数,执行最终函数之前执行的函数就称为中间件
- context.Next() 函数中context.Next()可以先执行后面的函数,按顺序或规则执行
- context.Abort() 终止请求剩余处理程序 只执行完当前的函数
全局中间件
默认路由
r := gin.Default()
routers.DefaultRouters(r)
routers.Test(r)
r.GET("/test", func(context *gin.Context) {
fmt.Println("test")
})
r.Use(func(context *gin.Context) {
fmt.Println("全局中间件")
})
路由分组
两种方式:1和2 定义group全局中间件必须在使用路由之前
func Test(r *gin.Engine) {
apiRouters := r.Group("/api", func(context *gin.Context) {
fmt.Println("group全局中间件1")
})
apiRouters.Use(func(context *gin.Context) {
fmt.Println("group全局中间件2")
})
{
apiRouters.GET("/userIndex", func(context *gin.Context) {
fmt.Println("1-我是一个中间件")
fmt.Println("2-我是一个中间件")
}, func(context *gin.Context) {
fmt.Println("下一个方法")
}, admin.UserController{}.Index)
apiRouters.POST("/userAdd", admin.UserController{}.Add)
}
}
中间件和对应控制器之间共享数据
context.get(“xxx”)返回(数据,是否有数据(t/f)) 返回的数据是空接口类型,需要类型断言
apiRouters := r.Group("/api", func(context *gin.Context) {
context.Set("username", "张三")
fmt.Println("group全局中间件1")
})
apiRouters.Use(func(context *gin.Context) {
fmt.Println("group全局中间件2")
fmt.Println(context.Get("username")) // 张三 true
})默认中间件
gin.Default()默认使用了Logger和Recovery中间件
- Logger将日志写入gin.DefaultWrtier,即使配置了GIN_MODE=release
- Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码 如果不想使用上面两个默认中间件,可以使用gin.new()新建一个没有任何默认中间件的路由
使用goroutine
当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(context *gin.context) 必须使用其只读副本(c.Copy())
自定义Model
09【Gin框架】Gin框架中自定义Model | 16 把公共的功能单独抽取出来作为一个模块(Model)。Model是逐步抽象的过程,一般我们会在Model里面封装一些公共的方法让不同的Controller使用,也可以在Model中实现与数据库交互
- 新建包(models)
- 使用的地方引入models
文件上传
10【Gin框架】Gin中实现单文件上传 多文件上传 | 16
file,err := c.FromFile()接收文件
c.SaveUploadedFile(file,"./static/upload/xxx.xxx")保存文件
同名多文件上传
from,_ := c.MultipartForm() 获取相同名字文件表单
然后循环遍历files := form.File("upload[]")
Cookie
11【Gin框架】Gin中的Cookie 多个二级域名共享 Cookie | 24
用于多个页面数据共享:Cookie、Seesion
是存储在浏览器中的,同一个浏览器中访问同一个域实现共享
c.SetCookie(name,value string,maxAge int,path domain string,secure,httpOnly bool) 设置cookie
- key
- value
- 过期时间
- cookie保存路劲
- 作用域(域名)
- secure:为true时仅https有效
- 扩展httpOnly,为true时js脚本、applet等无法读取cookie
c.Cookie(name)获取cookie
二级域名共享cookie
设置1级作用域,点开头(.baidu.com)
Session
12【Gin框架】Gin Session 设置获取 以及分布式Session | 32 session是另一种记录客户状态的机制,不同的是cookie保存在浏览器,session保存在服务 客户端访问服务器时,服务器会创建session对象,生成类似key-value的键值对,将value保存到服务器,key保存到浏览器作为cookie 浏览器下次访问会携带key,找到对应的session
gin中使用session
gin-contrib/session
store := cookie.newStore([]byte("secret111")) // 创建基于cookie的存储引擎,secret111是用于加密的密钥
r.Use(sessions.Sessions("mysession",store)) //store是前面的存储引擎,配置session的中间键,mysession就是上文的key,用于保存在浏览器的cookie中
在控制器中:
session := sessions.Default(context)
session.set("key","value") // 设置session
session.Save() // 保存session,设置session必须调用
// 获取session
a := session.Get("key")redis配置session
store,_ redis.NewStore(10,"tcp","localhost:6379","password",[]byte("secret"))
r.Use(sessions.Sessions("myredissession",store))修改session过期时间
session.Options(sessions.Options{
MaxAge: 3600 * 6,// 6hrs
})
session.Set("xx","xx")