github上格式更美些:ningskyer/gin-doc-cn
安裝與配置
安裝:
$ go get gopkg.in/gin-gonic/gin.v1
注意:確保 GOPATH GOROOT 已經配置
導入:
import "gopkg.in/gin-gonic/gin.v1"
框架架構
- HTTP 服務器
1.默認服務器
router.Run()
2.HTTP 服務器
除了默認服務器中 router.Run()
的方式外,還可以用 http.ListenAndServe()
,比如
func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
或者自定義 HTTP 服務器的配置:
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
3.HTTP 服務器替換方案
想無縫重啟、停機嗎? 以下有幾種方式:
我們可以使用 fvbock/endless 來替換默認的 ListenAndServe
。但是 windows 不能使用。
router := gin.Default()
router.GET("/", handler)
// [...]
endless.ListenAndServe(":4242", router)
除了 endless 還可以用manners:
manners 兼容windows
manners.ListenAndServe(":8888", r)
- 生命周期
- Context
路由
- 基本路由
gin 框架中采用的路由庫是 httprouter。
// 創建帶有默認中間件的路由:
// 日志與恢復中間件
router := gin.Default()
//創建不帶中間件的路由:
//r := gin.New()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
- 路由參數
api 參數通過Context的Param方法來獲取
router.GET("/string/:name", func(c *gin.Context) {
name := c.Param("name")
fmt.Println("Hello %s", name)
})
URL 參數通過 DefaultQuery 或 Query 方法獲取
// url 為 http://localhost:8080/welcome?name=ningskyer時
// 輸出 Hello ningskyer
// url 為 http://localhost:8080/welcome時
// 輸出 Hello Guest
router.GET("/welcome", func(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可設置默認值
// 是 c.Request.URL.Query().Get("lastname") 的簡寫
lastname := c.Query("lastname")
fmt.Println("Hello %s", name)
})
表單參數通過 PostForm 方法獲取
//form
router.POST("/form", func(c *gin.Context) {
type := c.DefaultPostForm("type", "alert")//可設置默認值
msg := c.PostForm("msg")
title := c.PostForm("title")
fmt.Println("type is %s, msg is %s, title is %s", type, msg, title)
})
- 路由群組
someGroup := router.Group("/someGroup")
{
someGroup.GET("/someGet", getting)
someGroup.POST("/somePost", posting)
}
控制器
- 數據解析綁定
模型綁定可以將請求體綁定給一個類型,目前支持綁定的類型有 JSON, XML 和標准表單數據 (foo=bar&boo=baz)。
要注意的是綁定時需要給字段設置綁定類型的標簽。比如綁定 JSON 數據時,設置 json:"fieldname"
。
使用綁定方法時,Gin 會根據請求頭中 Content-Type 來自動判斷需要解析的類型。如果你明確綁定的類型,你可以不用自動推斷,而用 BindWith 方法。
你也可以指定某字段是必需的。如果一個字段被 binding:"required"
修飾而值卻是空的,請求會失敗並返回錯誤。
// Binding from JSON
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 綁定JSON的例子 ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if c.BindJSON(&json) == nil {
if json.User == "manu" && json.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
}
})
// 綁定普通表單的例子 (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// 根據請求頭中 content-type 自動推斷.
if c.Bind(&form) == nil {
if form.User == "manu" && form.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
}
})
// 綁定多媒體表單的例子 (user=manu&password=123)
router.POST("/login", func(c *gin.Context) {
var form LoginForm
// 你可以顯式聲明來綁定多媒體表單:
// c.BindWith(&form, binding.Form)
// 或者使用自動推斷:
if c.Bind(&form) == nil {
if form.User == "user" && form.Password == "password" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
}
})
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
請求
- 請求頭
- 請求參數
- Cookies
- 上傳文件
router.POST("/upload", func(c *gin.Context) {
file, header , err := c.Request.FormFile("upload")
filename := header.Filename
fmt.Println(header.Filename)
out, err := os.Create("./tmp/"+filename+".png")
if err != nil {
log.Fatal(err)
}
defer out.Close()
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
})
響應
- 響應頭
- 附加Cookie
- 字符串響應
c.String(http.StatusOK, "some string")
- JSON/XML/YAML響應
r.GET("/moreJSON", func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string `json:"user" xml:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// 注意 msg.Name 變成了 "user" 字段
// 以下方式都會輸出 : {"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.XML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.YAML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.JSON(http.StatusOK, msg)
c.XML(http.StatusOK, msg)
c.YAML(http.StatusOK, msg)
})
- 視圖響應
先要使用 LoadHTMLTemplates() 方法來加載模板文件
func main() {
router := gin.Default()
//加載模板
router.LoadHTMLGlob("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")
}
模板結構定義
<html>
<h1>
{{ .title }}
</h1>
</html>
不同文件夾下模板名字可以相同,此時需要 LoadHTMLGlob() 加載兩層模板路徑
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "Posts",
})
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "Users",
})
}
templates/posts/index.tmpl
<!-- 注意開頭 define 與結尾 end 不可少 -->
{{ define "posts/index.tmpl" }}
<html><h1>
{{ .title }}
</h1>
</html>
{{ end }}
gin也可以使用自定義的模板引擎,如下
```go
import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}
- 文件響應
//獲取當前文件的相對路徑
router.Static("/assets", "./assets")
//
router.StaticFS("/more_static", http.Dir("my_file_system"))
//獲取相對路徑下的文件
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
- 重定向
r.GET("/redirect", func(c *gin.Context) {
//支持內部和外部的重定向
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
- 同步異步
goroutine 機制可以方便地實現異步處理
func main() {
r := gin.Default()
//1. 異步
r.GET("/long_async", func(c *gin.Context) {
// goroutine 中只能使用只讀的上下文 c.Copy()
cCp := c.Copy()
go func() {
time.Sleep(5 * time.Second)
// 注意使用只讀上下文
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
//2. 同步
r.GET("/long_sync", func(c *gin.Context) {
time.Sleep(5 * time.Second)
// 注意可以使用原始上下文
log.Println("Done! in path " + c.Request.URL.Path)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
視圖
- 傳參
- 視圖組件
中間件
- 分類使用方式
// 1.全局中間件
router.Use(gin.Logger())
router.Use(gin.Recovery())
// 2.單路由的中間件,可以加任意多個
router.GET("/benchmark", MyMiddelware(), benchEndpoint)
// 3.群組路由的中間件
authorized := router.Group("/", MyMiddelware())
// 或者這樣用:
authorized := router.Group("/")
authorized.Use(MyMiddelware())
{
authorized.POST("/login", loginEndpoint)
}
- 自定義中間件
//定義
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// 在gin上下文中定義變量
c.Set("example", "12345")
// 請求前
c.Next()//處理請求
// 請求后
latency := time.Since(t)
log.Print(latency)
// access the status we are sending
status := c.Writer.Status()
log.Println(status)
}
}
//使用
func main() {
r := gin.New()
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
//獲取gin上下文中的變量
example := c.MustGet("example").(string)
// 會打印: "12345"
log.Println(example)
})
// 監聽運行於 0.0.0.0:8080
r.Run(":8080")
}
-
中間件參數
-
內置中間件
1.簡單認證BasicAuth
// 模擬私有數據
var secrets = gin.H{
"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
"austin": gin.H{"email": "austin@example.com", "phone": "666"},
"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
}
func main() {
r := gin.Default()
// 使用 gin.BasicAuth 中間件,設置授權用戶
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
}))
// 定義路由
authorized.GET("/secrets", func(c *gin.Context) {
// 獲取提交的用戶名(AuthUserKey)
user := c.MustGet(gin.AuthUserKey).(string)
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
}
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
數據庫
- Mongodb
Golang常用的Mongodb驅動為 mgo.v2, 查看文檔
mgo 使用方式如下:
//定義 Person 結構,字段須為首字母大寫
type Person struct {
Name string
Phone string
}
router.GET("/mongo", func(context *gin.Context){
//可本地可遠程,不指定協議時默認為http協議訪問,此時需要設置 mongodb 的nohttpinterface=false來打開httpinterface。
//也可以指定mongodb協議,如 "mongodb://127.0.0.1:27017"
var MOGODB_URI = "127.0.0.1:27017"
//連接
session, err := mgo.Dial(MOGODB_URI)
//連接失敗時終止
if err != nil {
panic(err)
}
//延遲關閉,釋放資源
defer session.Close()
//設置模式
session.SetMode(mgo.Monotonic, true)
//選擇數據庫與集合
c := session.DB("adatabase").C("acollection")
//插入文檔
err = c.Insert(&Person{Name:"Ale", Phone:"+55 53 8116 9639"},
&Person{Name:"Cla", Phone:"+55 53 8402 8510"})
//出錯判斷
if err != nil {
log.Fatal(err)
}
//查詢文檔
result := Person{}
//注意mongodb存儲后的字段大小寫問題
err = c.Find(bson.M{"name": "Ale"}).One(&result)
//出錯判斷
if err != nil {
log.Fatal(err)
}
fmt.Println("Phone:", result.Phone)
})
- Mysql
- ORM
擴展包
常用方法
- gin
- Context