Golang中Gin框架的使用入門教程
官方地址:gin-gonic.com/docs/
安裝與簡(jiǎn)單測(cè)試
下載并安裝Gin包,并導(dǎo)入引用
$ go get -u github.com/gin-gonic/gin //將gin引入到代碼中 import "github.com/gin-gonic/gin" //可選。如果使用諸如 http.StatusOK 之類的常量,則需要引入 net/http 包 import "net/http"
編寫如下測(cè)試代碼
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 監(jiān)聽并在 0.0.0.0:8080 上啟動(dòng)服務(wù)
}
然后在瀏覽器中進(jìn)行測(cè)試: http://localhost:8080 就可以訪問(wèn)到響應(yīng)的請(qǐng)求。
常見(jiàn)請(qǐng)求與分組請(qǐng)求
下面展示幾種其他 常規(guī) 請(qǐng)求方式
// 下面列出常用幾種請(qǐng)求方式
r.GET("/someGet", handle)
r.POST("/somePost", handle)
r.PUT("/somePut", handle)
r.DELETE("/someDelete", handle)
r.PATCH("/somePatch", handle)
r.HEAD("/someHead", handle)
r.OPTIONS("/someOptions", handle)
r.Any("/any", handle)
**還可以對(duì)請(qǐng)求進(jìn)行分組操作。 **
v1 := r.Group("/v1")
{
v1.GET("/login", handle)
}
v2 := r.Group("/v2")
{
v2.GET("/login", handle)
}
下面例子 是** 獲得請(qǐng)求中path**
func main() {
router := gin.Default()
// 匹配/user/john
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 匹配/user/john/和/user/john/send
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.Run(":8080")
}
/user/:name/*action : 表示對(duì)后邊路由全部模糊匹配。例如:/user/john/send/1 形式 action會(huì)匹配得到 name 是 john, action是 /send/1
獲取參數(shù) 與 參數(shù)合法性驗(yàn)證
獲得query中參數(shù)
func main() {
router := gin.Default()
// welcome?firstname=Jane&lastname=Doe
router.GET("/user", func(c *gin.Context) {
firstname := c.DefaultQuery("name", "kim") // 獲取query中的name,沒(méi)有的話就為kim
lastname := c.Query("age") // 獲取query中的age
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
獲得multipart/urlencoded form中的參數(shù)
func main() {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("age")
nick := c.DefaultPostForm("name", "kim")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
curl http://127.0.0.1:8080/form\_post -X POST -d 'name=john&age=25'
通過(guò)此curl發(fā)送post請(qǐng)求,即可獲得表單中數(shù)據(jù)。
模型綁定和參數(shù)驗(yàn)證
基本用法
我們已經(jīng)見(jiàn)識(shí)了x-www-form-urlencoded類型的參數(shù)處理,現(xiàn)在越來(lái)越多的應(yīng)用習(xí)慣使用JSON來(lái)通信,也就是無(wú)論返回的response還是提交的request,其content-type類型都是application/json的格式。而對(duì)于一些舊的web表單頁(yè)還是x-www-form-urlencoded的形式,這就需要我們的服務(wù)器能支持住這多種content-type的參數(shù)了。
由于go是靜態(tài)語(yǔ)言,需要先實(shí)現(xiàn)定義數(shù)據(jù)模型,這就需要用到gin的model bind功能了。
gin使用go-playground/validator.v8驗(yàn)證參數(shù),查看完整文檔。
需要在綁定的字段上設(shè)置tag,比如,綁定格式為json,需要這樣設(shè)置json:"fieldname" 。
此外,Gin還提供了兩套綁定方法:
1.Must bind
- Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
- Behavior - 這些方法底層使用 MustBindWith,如果存在綁定錯(cuò)誤,請(qǐng)求將被以下指令中止 .
c.AbortWithError(400, err).SetType(ErrorTypeBind),
響應(yīng)狀態(tài)代碼會(huì)被設(shè)置為400,請(qǐng)求頭Content-Type被設(shè)置為text/plain; charset=utf-8。
注意,如果你試圖在此之后設(shè)置響應(yīng)代碼,將會(huì)發(fā)出一個(gè)警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行為,請(qǐng)使用ShouldBind相關(guān)的方法
2.Should bind
- Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
- Behavior - 這些方法底層使用 ShouldBindWith,如果存在綁定錯(cuò)誤,則返回錯(cuò)誤,開發(fā)人員可以正確處理請(qǐng)求和錯(cuò)誤。
當(dāng)我們使用綁定方法時(shí),Gin會(huì)根據(jù)Content-Type推斷出使用哪種綁定器,如果你確定你綁定的是什么,你可以使用MustBindWith或者BindingWith。
你還可以給字段指定特定規(guī)則的修飾符,如果一個(gè)字段用binding:"required"修飾,并且在綁定時(shí)該字段的值為空,那么將返回一個(gè)錯(cuò)誤。
type Person struct {
Name string `json:"name" binding:"required"` // json格式從name取值,并且該值為必須的
Age int `json:"age" binding:"required,gt=20"` // json格式從age取值,并且該值為必須的,且必須大于20
}
func main() {
router := gin.Default()
router.POST("/test", func(context *gin.Context) {
var person Person
// 這里我確定傳過(guò)來(lái)的一定是JSON所以用ShouldBindJSON,否則可以用ShouldBind
if err := context.ShouldBindJSON(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":8080")
}
curl http://localhost:8080/test -X POST -d '{"name":"Rock","age": 25}'
上面是通過(guò)json映射方式 綁定 請(qǐng)求數(shù)據(jù),并且對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行驗(yàn)證。
自定義參數(shù)驗(yàn)證
驗(yàn)證包 gopkg.in/go-playground/validator.v8使用方法
package main
import (
"net/http"
"reflect"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
)
type Booking struct {
// 這里的驗(yàn)證方法為bookabledate
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield=CheckIn表示大于的字段為CheckIn
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
func bookableDate(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
// 這里有兩個(gè)知識(shí)點(diǎn),映射和斷言
// 在這里,field是一個(gè)reflect.Type的接口類型變量,通過(guò)Interface方法獲得field接口類型變量的真實(shí)類型,可以理解為reflect.Value的逆操作
// 在這里,斷言就是將一個(gè)接口類型的變量轉(zhuǎn)化為time.Time,前提是后者必須實(shí)現(xiàn)了前者的接口
// 綜上,這里就是將field進(jìn)行了類型轉(zhuǎn)換
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
}
func main() {
route := gin.Default()
// 注冊(cè)自定義驗(yàn)證器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}
route.GET("/bookable", getBookable)
route.Run(":8080")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
上面方法自定義了一種參數(shù)驗(yàn)證器。
項(xiàng)目結(jié)構(gòu)參考
├── conf #項(xiàng)目配置文件目錄 │ └── config.toml #大家可以選擇自己熟悉的配置文件管理工具包例如:toml、xml等等 ├── controllers #控制器目錄,按模塊存放控制器(或者叫控制器函數(shù)),必要的時(shí)候可以繼續(xù)劃分子目錄。 │ ├── food.go │ └── user.go ├── main.go #項(xiàng)目入口,這里負(fù)責(zé)Gin框架的初始化,注冊(cè)路由信息,關(guān)聯(lián)控制器函數(shù)等。 ├── models #模型目錄,負(fù)責(zé)項(xiàng)目的數(shù)據(jù)存儲(chǔ)部分,例如各個(gè)模塊的Mysql表的讀寫模型。 │ ├── food.go │ └── user.go ├── static #靜態(tài)資源目錄,包括Js,css,jpg等等,可以通過(guò)Gin框架配置,直接讓用戶訪問(wèn)。 │ ├── css │ ├── images │ └── js ├── logs #日志文件目錄,主要保存項(xiàng)目運(yùn)行過(guò)程中產(chǎn)生的日志。 └── views #視圖模板目錄,存放各個(gè)模塊的視圖模板,當(dāng)然有些項(xiàng)目只有api,是不需要視圖部分,可以忽略這個(gè)目錄 └── index.html
Gin框架運(yùn)行模式
為方便調(diào)試,Gin 框架在運(yùn)行的時(shí)候默認(rèn)是debug模式,在控制臺(tái)默認(rèn)會(huì)打印出很多調(diào)試日志,上線的時(shí)候我們需要關(guān)閉debug模式,改為release模式。
設(shè)置Gin框架運(yùn)行模式:
通過(guò)環(huán)境變量設(shè)置 export GIN_MODE=release
GIN_MODE環(huán)境變量,可以設(shè)置為debug或者release
通過(guò)代碼設(shè)置
在main函數(shù),初始化gin框架的時(shí)候執(zhí)行下面代碼 // 設(shè)置 release模式 gin.SetMode(gin.ReleaseMode) // 或者 設(shè)置debug模式 gin.SetMode(gin.DebugMode)
Gin如何獲取客戶ip
route.GET("/ip", func(c *gin.Context) {
// 獲取用戶IP
ip := c.ClientIP()
c.JSON(http.StatusBadRequest, gin.H{"ip": ip})
})
Gin處理請(qǐng)求結(jié)果
以String類型響應(yīng)請(qǐng)求
func (c *Context) String(code int, format string, values ...interface{})
c.String(200,"歡迎訪問(wèn)%s, 你是%s", "tizi360.com!","最靚的仔!")
以Json格式響應(yīng)請(qǐng)求
我們開發(fā)api接口的時(shí)候常用的格式就是json,下面是返回json格式數(shù)據(jù)的例子
// User 定義
type User struct {
Name string `json:"name"` // 通過(guò)json標(biāo)簽定義struct字段轉(zhuǎn)換成json字段的名字。
Email string `json:"email"`
}
// Handler 控制器
func(c *gin.Context) {
//初始化user對(duì)象
u := &User{
Name: "tizi365",
Email: "tizi@tizi365.com",
}
//返回json數(shù)據(jù)
//返回結(jié)果:{"name":"tizi365", "email":"tizi@tizi365.com"}
c.JSON(200, u)
}
以文件形式響應(yīng)請(qǐng)求
c.FileAttachment("/var/www/1.jpg", "1.jpg")
設(shè)置http響應(yīng)頭
func(c *gin.Context) {
//設(shè)置http響應(yīng) header, key/value方式,支持設(shè)置多個(gè)header
c.Header("site","tizi365")
}
Gin處理html模板
func main() {
// 初始化gin對(duì)象
router := gin.Default()
// 首先加載templates目錄下面的所有模版文件,模版文件擴(kuò)展名隨意
router.LoadHTMLGlob("templates/*")
// 綁定一個(gè)url路由 /index
router.GET("/index", func(c *gin.Context) {
// 通過(guò)HTML函數(shù)返回html代碼
// 第二個(gè)參數(shù)是模版文件名字
// 第三個(gè)參數(shù)是map類型,代表模版參數(shù)
// gin.H 是map[string]interface{}類型的別名
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "Main website",
})
})
// 啟動(dòng)http服務(wù),并且綁定在8080端口
router.Run(":8080")
}
Gin訪問(wèn)靜態(tài)資源文件
func main() {
router := gin.Default()
// 設(shè)置靜態(tài)資源文件目錄,并且綁定一個(gè)Url前綴
// 靜態(tài)資源文件目錄:/var/www/tizi365/assets
// /assets是訪問(wèn)靜態(tài)資源的url前綴
// 例如:
// /assets/images/1.jpg 這個(gè)url文件,存儲(chǔ)在/var/www/tizi365/assets/images/1.jpg
router.Static("/assets", "/var/www/tizi365/assets")
// 為單個(gè)靜態(tài)資源文件,綁定url
// 這里的意思就是將/favicon.ico這個(gè)url,綁定到./resources/favicon.ico這個(gè)文件
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
Gin處理Cookie操作
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// 設(shè)置cookie路由
router.GET("/setCookie", func(c *gin.Context) {
// 設(shè)置cookie
c.SetCookie("site_cookie", "cookievalue", 3600, "/", "localhost", false, true)
})
// 獲得cookie路由
router.GET("/cookie", func(c *gin.Context) {
data, err := c.Cookie("/cookie")
fmt.Printf(data)
if err != nil {
// 直接返回cookie值
c.String(200, data)
return
}
c.String(200, "not found!")
})
//刪除cookie路由
router.GET("/removeCookie", func(c *gin.Context) {
c.SetCookie("set_cookie", "cookievalue", -1, "/", "localhost", false, true)
c.String(200, "刪除cookie")
})
router.Run(":8080")
}
Gin文件上傳
package main
// 導(dǎo)入gin包
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
func main() {
router := gin.Default()
// 設(shè)置文件上傳大小限制,默認(rèn)是32m
router.MaxMultipartMemory = 64 << 20 // 64 MiB
router.POST("/upload", func(c *gin.Context) {
// 獲取上傳文件,返回的是multipart.FileHeader對(duì)象,代表一個(gè)文件,里面包含了文件名之類的詳細(xì)信息
// file是表單字段名字
file, _ := c.FormFile("file")
// 打印上傳的文件名
log.Println(file.Filename)
// 將上傳的文件,保存到./data/1111.jpg 文件中
c.SaveUploadedFile(file, "./data/1111.jpg")
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
Gin中間件
在Gin框架中,中間件(Middleware)指的是可以攔截http請(qǐng)求-響應(yīng)生命周期的特殊函數(shù),在請(qǐng)求-響應(yīng)生命周期中可以注冊(cè)多個(gè)中間件,每個(gè)中間件執(zhí)行不同的功能,一個(gè)中間執(zhí)行完再輪到下一個(gè)中間件執(zhí)行。
中間件的常見(jiàn)應(yīng)用場(chǎng)景如下:
- 請(qǐng)求限速
- api接口簽名處理
- 權(quán)限校驗(yàn)
- 統(tǒng)一錯(cuò)誤處理
Gin支持設(shè)置全局中間件和針對(duì)路由分組設(shè)置中間件,設(shè)置全局中間件意思就是會(huì)攔截所有請(qǐng)求,針對(duì)分組路由設(shè)置中間件,意思就是僅對(duì)這個(gè)分組下的路由起作用。
package main
// 導(dǎo)入gin包
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"time"
)
// 自定義個(gè)日志中間件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// 可以通過(guò)上下文對(duì)象,設(shè)置一些依附在上下文對(duì)象里面的鍵/值數(shù)據(jù)
c.Set("example", "12345")
fmt.Printf("1. 執(zhí)行中間件設(shè)置 \n")
// 在這里處理請(qǐng)求到達(dá)控制器函數(shù)之前的邏輯
// 調(diào)用下一個(gè)中間件,或者控制器處理函數(shù),具體得看注冊(cè)了多少個(gè)中間件。
c.Next()
fmt.Printf("4. 完成 執(zhí)行中間件設(shè)置 \n")
// 在這里可以處理請(qǐng)求返回給用戶之前的邏輯
latency := time.Since(t)
log.Print(latency)
// 例如,查詢請(qǐng)求狀態(tài)嗎
status := c.Writer.Status()
log.Println(status)
}
}
func main() {
r := gin.New()
// 注冊(cè)上面自定義的日志中間件
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
// 查詢我們之前在日志中間件,注入的鍵值數(shù)據(jù)
fmt.Printf("2. 開始執(zhí)行業(yè)務(wù)邏輯 \n")
example := c.MustGet("example").(string)
// it would print: "12345"
log.Println(example)
fmt.Printf("3. 業(yè)務(wù)邏輯執(zhí)行完成 \n")
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
其輸出結(jié)果如下:
1. 執(zhí)行中間件設(shè)置
2. 開始執(zhí)行業(yè)務(wù)邏輯
2022/10/22 16:33:29 12345
3. 業(yè)務(wù)邏輯執(zhí)行完成
4. 完成 執(zhí)行中間件設(shè)置
2022/10/22 16:33:29 658.681µs
2022/10/22 16:33:29 200
Gin 中間件類似 Node中的洋蔥模型。
以上就是Golang中Gin框架的使用入門教程的詳細(xì)內(nèi)容,更多關(guān)于Golang Gin框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語(yǔ)言單元測(cè)試模擬服務(wù)請(qǐng)求和接口返回
這篇文章主要為大家介紹了Go語(yǔ)言單元測(cè)試模擬服務(wù)請(qǐng)求和接口返回示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
深入解析快速排序算法的原理及其Go語(yǔ)言版實(shí)現(xiàn)
這篇文章主要介紹了快速排序算法的原理及其Go語(yǔ)言版實(shí)現(xiàn),文中對(duì)于快速算法的過(guò)程和效率有較為詳細(xì)的說(shuō)明,需要的朋友可以參考下2016-04-04
詳解如何使用unsafe標(biāo)準(zhǔn)庫(kù)突破Golang中的類型限制
在使用c語(yǔ)言編程時(shí),常常因?yàn)轭愋偷膯?wèn)題大傷腦筋,而,golang提供了一些方式用于喜歡hack的用戶,下面我們就來(lái)講講如何使用unsafe標(biāo)準(zhǔn)庫(kù)突破Golang中的類型限制吧2024-03-03
使用Golang如何實(shí)現(xiàn)簡(jiǎn)易的令牌桶算法
這篇文章主要介紹了使用Golang如何實(shí)現(xiàn)簡(jiǎn)易的令牌桶算法問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

