目录
1、 Gin框架简介
Gin是一个用Go语言编写的Web框架。Gin框架拥有很好的性能,其借助高性能的HttpRouter包,运行速度得到了极大提升。目前的Gin框架是1.x版本。
2、Gin框架安装与第一个Gin示例
2.1、安装
下载并安装Gin:
go get -u github.com/gin-gonic/gin
2.2、第一个Gin示例
import (
"github.com/gin-gonic/gin"
)
func main() {
//创建一个默认的路由引擎
r := gin.Default()
//GET:请求方式;/hello:请求的路径
//当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
r.GET("/hello", func(c *gin.Context) {
//c.JSON:返回JSON格式的数据
c.JSON(200, gin.H{
"messages": "Hello world!",
})
})
//启动HTTP服务,默认在0.0.0.0:8080启动服务
r.Run()
}
运行以上代码,然后使用浏览器打开“127.0.0.1:8080/hello”即可看到一串JS0N字符串。
3、Gin路由和控制器
路由是指:一个HTTP请求找到对应的处理器函数的过程。处理器函数主要负责执行HTTP请求和响应任务。如下代码中的goLogin(0函数就是Gin的处理器函数:
r := gin.Default()
r.POST("/usr/login", goLogin)
//处理器函数
func goLogin(c *gin.Context) {
name := c.PostForm("name")
password := c.POstForm("password")
//通过请求上下文对象Context,直接给客户端返回一个字符串
c.String(200, "username=%s,password=%s", name, password)
}
3.1、路由规则
一条路由规则由HTTP请求方法、URL路径、处理器函数这3部分组成。
3.1.1、HTTP请求方法
常用的HTTP请求方法有GET、POST、PUT、DELETE这4种。
3.1.2、URL路径
Gin框架的URL路径有以下3种写法。
1.静态URL路径,即不带任何参数的URL路径。形如:
/users/shirdon
/user/1
/article/6
2.带路径参数的URL路径,URL路径中带有参数,参数由英文冒号“:”跟着1个字符串定义。形如:
定义参数:id
以上形式可以匹配user/1、/article/6这类的URL路径。
3.带星号(*)模糊匹配参数的URL路径
星号(*
)代表匹配任意路径的意思。必须在*
号后面指定一个参数名,之后可以通过这个参数获取*
号匹配的内容。例如“/user/*path
”可以通过path
参数获取*
号匹配的内容,例如/user/1
、user/shirdon/comment/1
等。
3.1.3、处理器函数
Gin框架的处理器函数的定义如下:
func HandlerFunc(c *gin.Context)
处理器函数接受1个上下文参数。可以通过上下文参数获取HTTP的请求参数,返回HTTP请求的响应。
3.2、分组路由
在做API开发时,如果要支持多个API版本,则可以通过分组路由来处理API版本。Gi的分组路由示例如下:
import "github.com/gin-gonic/gin"
func main() {
router := gin.Default()
//创建v1组
v1 := router.Group("/v1")
{
v1.GET("/login", login1)
}
//创建v2组
v2 := router.Group("v2")
{
v2.GET("/login", login2)
}
router.Run()
}
func login1(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello1",
})
}
func login2(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello2",
})
}
上面的例子将会注册下面的路由信息:
/v1/login
/v2/login
4、Gin处理请求参数
4.1、获取GET请求参数
Gin获取GET请求参数的常用方法如下:
func (c *Context)Query(key string)string
func (c *Context)DefaultQuery(key,defaultvalue string)string
func (c *Context)GetQuery(key string)(string,bool)
4.2、获取POST请求参数
Gin获取POST请求参数的常用方法如下:
func (c *Context)PostForm(key string)string
func (c.*Context)DefaultPostForm(key,defaultValue string)string
func (c *Context)GetPostForm(key string)(string,bool)
其使用方法示例如下:
func Handler(c *gin.Context){
//获取name参数,通过PostForm获取的参数值是String类型
name := c.PostForm("name")
//跟PostForm的区别是:可以通过第二个参数设置参数默认值
name := c.DefaultPostForm("name", "shirdon")
//获取id参数,通过GetPostForm获取的参数值也是String类型
id, ok := c.GetPostForm("id")
if !ok {
//。。。参数不存在
}
}
4.3、获取URL路径参数
Gin获取URL路径参数是指,获取user:id这类路由绑定的参数。user:id绑定了1个参数id。获取URL路径参数的函数如下:
func (c *Context)Param(key string)string
其使用示例如下:
r := gin.Default()
r.GET("/user/:id",func(c.*gin.Context){
//获取URL参数id
id:=c.Param("id")
})
4.4、请求参数绑定到结构体
前面获取参数的方式都是逐个进行参数的读取,比较麻烦。G支持将请求参数自动绑定到一个结构体对象,这种方式支持GET/POST请求,也支持HTTP请求体中内容为JSON或XML格式的参数。下面例子是将请求参数绑定到User结构体:
type User struct {
Phone string `json:"phone" form:"phone"`
Age string `json:"age" form:"age"`
}
在上面代码中,通过定义结构体字段的标签,来定义请求参数和结构体字段的关系。下面对User结构体的Phone字段的标签进行说明:
标签 | 说明 |
---|---|
json:“phone” | 数据为JSON格式,并且json字段名为phone |
form:“phone” | 表单参数名为phone |
在实际开发中,可以根据自己的需要选择支持的数据类型。下面看一下控制器代码如何使用User结构体:
r.POST("/user/:id", func(c *gin.Context) {
u := User{}
if c.ShouldBind(&u) != nil {
log.Println("bind params fail...")
}
c.JSON(200, u)
})
5、Gin生成HTTP请求响应
接下来探究一下如何在Gin中生成HTTP请求响应。Gin支持以字符串、JSON、XML、文件等格式生成HTTP请求响应。gin.Context上下文对象支持多种返回处理结果。下面分别介绍不同的响应方式。
5.1、以字符串方式生成HTTP请求响应
通过String()方法生成字符串方式的HTTP请求响应。
String()方法的定义如下:
func (c *Context)String(code int,format string,values...interface(})
该方法的使用示例如下:
func stringResponse(c *gin.Context) {
c.String(200, "hello")
c.String(200, "hello %s, good %s", "tom", "job")
}
5.2、以JSON格式生成HTTP请求响应
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func jsonResponse(c *gin.Context) {
u := &User{
Name: "Shirdon",
Email: "2342342@qq.com",
}
c.JSON(200, u)
}
5.3、以XML格式生成HTTP请求响应
定义一个User结构体,默认结构体的名字就是XML的根节点名字。以XML格式生成HTTP请求响应的示例如下:
type XmlUser struct {
Name string `xml:"name"`
Email string `xml:"email"`
}
func xmlResponse(c *gin.Context) {
u := &XmlUser{
Name: "Shirdon",
Email: "234235@qq.com",
}
c.XML(200, u)
}
5.4、以文件格式生成HTTP请求响应
接下来介绍Gin如何直接返回一个文件,这可以用来做文件下载。通过File()方法直接返回本地文件,参数为本地文件地址。其示例如下:
func fileResponse(c *gin.Context) {
//通过File()方法直接返回本地文件,参数为本地文件地址
c.File("./a.txt")
}
5.5、设置HTTP响应头
Gin中提供了Header()方法来设置HTTP响应头。默认采用key/value方式,支持设置多个Header。其使用示例如下:
func headerResponse(c *gin.Context) {
c.Header("Content-Type", "text/html;charset=utf-8")
c.Header("site", "tom")
}
6、渲染HTML模板
6.1、使用 LoadHTMLGlob () 或者 LoadHTMLFiles ()
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("./src/bookWebPro/chapter8/gin/templates/*")
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
})
router.Run(":8080")
}
index.tmpl
<html>
<h1>
{{ .title }}
</h1>
</html>
6.2、使用不同目录下名称相同的模板
func main() {
router := gin.Default()
router.LoadHTMLGlob("./src/bookWebPro/chapter8/gin/templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "Posts",
})
})
router.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "Users",
})
})
router.Run(":8080")
}
templates/posts/index.tmpl:
{{ define "posts/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
<p>Using posts/index.tmpl</p>
</html>
{{ end }}
templates/users/index.tmpl
<html>
<h1>
{{ .title }}
</h1>
</html>
6.3、自定义模板渲染器
import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}
6.4、自定义分隔符
r := gin.Default()
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates")
6.5、自定义模板功能
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
func main() {
router := gin.Default()
router.Delims("{[{", "}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
router.LoadHTMLFiles("./testdata/template/raw.tmpl")
router.GET("/raw", func(c *gin.Context) {
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":8080")
}
raw.tmpl
Date: {[{.now | formatAsDate}]}
结果:
Date: 2017/07/01
7、处理静态文件
在Gin中,如果项目中包含JS、CSS、JPG之类的静态文件,可以通过Static()方法配置路径映射:
func main() {
r := gin.Default()
r.Static("/assets", "./")
r.StaticFile("/facicon.ico", "./static/facivon.ico")
r.Run(":8080")
}
8、处理cookie
在Go语言net/http包中内置了cookie处理机制。Gin主要通过上下文对象提供的SetCookie()和Cookie()两个方法操作cookie,这两个函数都是对Go语言net/http包中http.SetCookie()方法的重新封装而已,其实质是一样的。
8.1、设置cookie
Gin使用SetCookie()方法设置cookie。SetCookie()方法的定义如下:
func (c *Context).SetCookie(name,value string,maxAge int, path, domain string, secure, httponly bool)
SetCookie()方法的使用示例如下:
func main() {
r := gin.Default()
r.GET("/hello", setCookie)
r.Run(":8080")
}
func setCookie(c *gin.Context) {
c.SetCookie("my_cookie", "cookievalue", 3600, "/", "localhost", false, true)
}
8.2、读取Cookie
func main() {
r := gin.Default()
r.GET("/hello", getCookie)
r.Run(":8080")
}
func getCookie(c *gin.Context) {
//根据cookie名字读取cookie值
data, err := c.Cookie("my_cookie")
if err != nil {
c.String(200, data)
}
c.String(200, "not found!")
}
8.3、删除cookie
通过将SetCookie()方法的MaxAge参数设置为-1,以达到删除cookie的目的。示例如下:
func delCookie(c *gin.Context) {
//设置cookie,将MaxAge设置为-1标识删除cookie
c.SetCookie("my_cookie", "cookievalue", -1, "/", "localhost", false, true)
}
9、文件上传
Gin使用SaveUploadedFile()方法实现文件上传。其使用示例代码如下。
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func main() {
router := gin.Default()
//设置文件上传大小限制,默认是32MB
router.MaxMultipartMemory = 64 << 20 //64mb
router.StaticFile("/upload.html", "./src/bookWebPro/chapter8/gin/templates/upload.html")
router.POST("/upload", func(c *gin.Context) {
//file是表单字段名字
file, _ := c.FormFile("file")
//打印上传的文件名
log.Println(file.Filename)
//将上传的文件保存到./data/shirdon.jpg
c.SaveUploadedFile(file, "./data/shirdon.jpg")
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
upload.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Gin文件上传</title>
</head>
<body>
<h1>上传文件示例</h1>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
文件:
<input type="file" name="file"><br><br>
<input type="submit" value="上传">
</form>
</body>
</html>
10、中间件
在Gin中,中间件(Middleware)是指可以拦截HTTP请求-响应生命周期的特殊函数。在请求-响应生命周期中可以注册多个中间件。每个中间件执行不同的功能,一个中间件执行完,才轮到下一个中间件执行。中间件的常见应用场景如下:
- 请求限速;
- API接口签名处理;
- 权限校验;
- 统一错误处理。
如果想拦截所有请求,则可以开发一个中间件函数来实现。Gin支持设置全局中间件和针对路由分组的中间件。在设置全局中间件后,会拦截所有请求。通过分组路由设置的中间件,仅对这个分组下的路由起作用。
10.1、使用中间件
func main() {
r := gin.Default()
//通过Use()方法设置全局中间件
//设置日志中间件,主要用于打印请求日志
r.Use(gin.Logger())
//设置Recovery中间件,主要用于拦截panic错误,不知与宕机
r.Use(gin.Recovery())
r.Run()
}
10.2、自定义中间件
import (
"github.com/gin-gonic/gin"
"log"
"time"
)
func main() {
r := gin.Default()
//注册中间件
r.Use(Logger())
r.GET("/hi", func(c *gin.Context) {
//在查询之前在日志中间件中注入的键值数据
example := c.MustGet("example").(string)
//打印
log.Println(example)
})
r.Run(":8080")
}
//自定义一个日志中间件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
//可以通过上下文对象,设置一些依附在上下文对象里面的键/值数据
c.Set("example", "hello")
//在这里处理请求到达处理器函数之前的逻辑
//调用下一个中间件,或者处理器的处理函数,具体需要看注册了多少个中间件
c.Next()
//在这里嗯可以处理返回给客户但之前的响应罗i几
latency := time.Since(t)
log.Print(latency)
//例如,查询请求状态码
status := c.Writer.Status()
log.Println(status)
}
}
11、Gin处理Session
在Gin中,可以依赖“github.com/gin-contrib/sessions
”包中的中间件处理session。“github.com/gin-contrib/sessions”包中的中间件支持cookie、MemStore、Redis、Memcached、MongoDB等存储引擎。
下面介绍session的常见用法。
11.1、基于Cookie存储引擎
- 安装“github.com/gin-contrib/sessions”包:
go get github.com/gin-contrib/sessions
2、“github.com/gin-contrib/sessions”包中session的用法:
import "github.com/gin-gonic/gin" //gin框架
import "github.com/gin-contrib/sessions/cookie" //session存储引擎
import "github.com/gin-contrib/sessions" //session
func main() {
r := gin.Default()
//创建基于cookie的存储引擎,password123456参数是用于加密的密钥
store := cookie.NewStore([]byte("password123456"))
//这只session中间件,参数my_session指的是session的名字,也是cookie的名字
//store是前面创建的存储引擎,可以将其替换成其他存储引擎
r.Use(sessions.Sessions("my_session", store))
r.GET("/hello", func(c *gin.Context) {
//初始化session对象
session := sessions.Default(c)
//通过session.Get()函数读取session值
//session是键值对格式数据,因此需要通过key查询数据
if session.Get("hello") != "world" {
//设置session数据
session.Set("hello", "world")
//删除session数据
session.Delete("shirdon")
//保存session数据
session.Save()
//删除整个session
//session.Clear()
}
c.JSON(200, gin.H{
"hello": session.Get("hello"),
})
})
r.Run(":8080")
}
11.2、基于Redis存储引擎
- 安装Giin的Redis存储引擎包
go get github.com/gin-contrib/sessions/redis
2.基于Redis存储引擎的示例
import "github.com/gin-gonic/gin" //gin框架
import "github.com/gin-contrib/sessions/redis" //redis存储引擎
import "github.com/gin-contrib/sessions" //session
func main() {
r := gin.Default()
//初始化基于Redis的存储引擎
store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("123456"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count++
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8080")
}