[TOC]
## Gin安裝
配置代理:[https://goproxy.cn,direct](https://goproxy.cn,direct)
> go get -u github.com/gin-gonic/gin
## 入門案例
~~~
package main
?
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{
"message": "Hello world!",
})
})
// 默認8080,括號里的參數是端口號
r.Run()
}
~~~
## Gin渲染
### HTML渲染
我們首先定義一個存放模板文件的`templates`文件夾,然后在其內部按照業務分別定義一個`posts`文件夾和一個`users`文件夾。 `posts/index.html`文件的內容如下:
~~~
{{define "posts/index.html"}}
<!DOCTYPE html>
<html lang="en">
?
<head>
? ?<meta charset="UTF-8">
? ?<meta name="viewport" content="width=device-width, initial-scale=1.0">
? ?<meta http-equiv="X-UA-Compatible" content="ie=edge">
? ?<title>posts/index</title>
</head>
<body>
? {{.title}}
</body>
</html>
{{end}}
~~~
`users/index.html`文件的內容如下:
~~~
{{define "users/index.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
? ?<meta charset="UTF-8">
? ?<meta name="viewport" content="width=device-width, initial-scale=1.0">
? ?<meta http-equiv="X-UA-Compatible" content="ie=edge">
? ?<title>users/index</title>
</head>
<body>
? {{.title}}
</body>
</html>
{{end}}
~~~
Gin框架中使用`LoadHTMLGlob()`或者`LoadHTMLFiles()`方法進行HTML模板渲染。
~~~
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/*")
//r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")
r.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.html", gin.H{
"title": "posts/index",
})
})
?
r.GET("users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.html", gin.H{
"title": "users/index",
})
})
?
r.Run(":8080")
}
~~~
## 獲取參數
### 獲取querystring參數
`querystring`指的是URL中`?`后面攜帶的參數,例如:`/user/search?username=小王子&address=沙河`。 獲取請求的querystring參數的方法如下:
~~~
func main() {
//Default返回一個默認的路由引擎
r := gin.Default()
r.GET("/user/search", func(c *gin.Context) {
? ? ? ?// 可以添加默認值
username := c.DefaultQuery("username", "小王子")
//username := c.Query("username")
address := c.Query("address")
//輸出json結果給調用方
c.JSON(http.StatusOK, gin.H{
"message": ?"ok",
"username": username,
"address": ?address,
})
})
r.Run()
}
~~~
~~~
http://localhost:8080/user/search?username=小王子&address=沙河
~~~
### 獲取path參數
請求的參數通過URL路徑傳遞,例如:`/user/search/小王子/沙河`。 獲取請求URL路徑中的參數的方式如下。
~~~
func main() {
//Default返回一個默認的路由引擎
r := gin.Default()
r.GET("/user/search/:username/:address", func(c *gin.Context) {
username := c.Param("username")
address := c.Param("address")
//輸出json結果給調用方
c.JSON(http.StatusOK, gin.H{
"message": ?"ok",
"username": username,
"address": ?address,
})
})
?
r.Run(":8080")
}
~~~
## 文件上傳
### 單文件上傳
寫個前端頁面,結構

~~~
{{define "uploads/upload.html"}}
? ?<!DOCTYPE html>
? ?<html lang="zh-CN">
? ?<head>
? ? ? ?<title>上傳文件示例</title>
? ?</head>
? ?<body>
? ?<form action="/uploads/upload" method="post" enctype="multipart/form-data">
? ? ? ?<input type="file" name="f1">
? ? ? ?<input type="submit" value="上傳">
? ?</form>
? ?</body>
? ?</html>
{{end}}
~~~
后臺:
~~~
func main() {
router := gin.Default()
? ?//模板路徑
? ?router.LoadHTMLGlob("templates/**/*")
? ?
? ?r.GET("/upload/index", func(c *gin.Context) {
? ? ? ?//頁面路徑
c.HTML(200,"uploads/upload.html",nil)
})
// 處理multipart forms提交文件時默認的內存限制是32 MiB
// 可以通過下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 單個文件
file, err := c.FormFile("f1")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
?
log.Println(file.Filename)
dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
// 上傳文件到指定的目錄
c.SaveUploadedFile(file, dst)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
})
})
router.Run()
}
~~~
### 多文件上傳
input選擇框里面加個`multiple`表示你可以多選
~~~
{{define "uploads/uploads.html"}}
? ?<!DOCTYPE html>
? ?<html lang="zh-CN">
? ?<head>
? ? ? ?<title>上傳文件示例</title>
? ?</head>
? ?<body>
? ?<form action="/uploads" method="post" enctype="multipart/form-data">
? ? ? ?<input type="file" name="files" multiple>
? ? ? ?<input type="submit" value="上傳">
? ?</form>
? ?</body>
? ?</html>
{{end}}
~~~
后臺:
~~~
r.POST("/uploads", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["files"]
?
for _, file := range files {
log.Println(file.Filename)
dst := fmt.Sprintf("D:/%s", file.Filename)
// 上傳文件到指定的目錄
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
~~~
這次只有核心代碼,可以結合前面單文件稍加修改,這并不難!
## 重定向
### HTTP重定向
HTTP 重定向很容易。 內部、外部重定向均支持。
~~~
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
~~~
### 路由重定向
路由重定向,使用`HandleContext`:
~~~
r.GET("/test", func(c *gin.Context) {
? ?// 指定重定向的URL
? ?c.Request.URL.Path = "/test2"
? ?r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
? ?c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
~~~
## 中間件
Gin框架允許開發者在處理請求的過程中,加入用戶自己的鉤子(Hook)函數。這個鉤子函數就叫中間件,中間件適合處理一些公共的業務邏輯,比如登錄認證、權限校驗、數據分頁、記錄日志、耗時統計等。
### 定義中間件
Gin中的中間件必須是一個`gin.HandlerFunc`類型。例如我們像下面的代碼一樣定義一個統計請求耗時的中間件。
~~~
// StatCost 是一個統計耗時請求耗時的中間件
func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Set("name", "小王子") // 可以通過c.Set在請求上下文中設置值,后續的處理函數能夠取到該值
// 調用該請求的剩余處理程序
c.Next()
// 不調用該請求的剩余處理程序
// c.Abort()
// 計算耗時
cost := time.Since(start)
log.Println(cost)
}
}
~~~
### 注冊中間件
~~~
func main() {
// 新建一個沒有任何默認中間件的路由
r := gin.New()
// 注冊一個全局中間件
r.Use(StatCost())
r.GET("/test", func(c *gin.Context) {
name := c.MustGet("name").(string) // 從上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
r.Run()
}
~~~
### 為某個路由單獨注冊
~~~
// 給/test2路由單獨注冊中間件(可注冊多個)
r.GET("/test2", StatCost(), func(c *gin.Context) {
name := c.MustGet("name").(string) // 從上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
~~~
### 為路由組注冊中間件
寫法1:
~~~
shopGroup := r.Group("/shop", StatCost())
{
? ?shopGroup.GET("/index", func(c *gin.Context) {...})
? ?...
}
~~~
寫法2:
~~~
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
? ?shopGroup.GET("/index", func(c *gin.Context) {...})
? ?...
}
~~~
# Gorm框架
## 安裝
~~~
go get -u github.com/jinzhu/gorm
~~~
## 連接MySQL
~~~
import (
?"github.com/jinzhu/gorm"
?_ "github.com/jinzhu/gorm/dialects/mysql"
)
?
func main() {
?db, err := gorm.Open("mysql", "user:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local")
?defer db.Close()
}
~~~
## GORM操作MySQL
### 創建數據庫
~~~
CREATE DATABASE db1;
?
CREATE TABLE `user_info` (
`id` int(30) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL COMMENT 'Gender ',
`hobby` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
~~~
~~~
表與實體映射的問題搞了我很久,我今天第一次建表的時候字段的首字母都是大寫,為了和實體名保持一致。結果反而讓自己查不到數據,一直以為是代碼的問題,直到把一個配置去了之后,go自動建表對比了下字段名才發現問題!
~~~
### 基本操作
~~~
package main
?
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
?
// UserInfo 用戶信息
type UserInfo struct {
ID int
Name string
Gender string
Hobby string
}
?
?
func main() {
db, err := gorm.Open("mysql", "root:123456@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
if err!= nil{
panic(err)
}
defer db.Close()
?
// 自動遷移
db.AutoMigrate(&UserInfo{})
?
u1 := UserInfo{1, "七米", "男", "籃球"}
u2 := UserInfo{2, "沙河娜扎", "女", "足球"}
// 創建記錄
db.Create(&u1)
db.Create(&u2)
// 查詢
var u = new(UserInfo)
db.First(u)
fmt.Printf("%#v\n", u)
?
var uu UserInfo
db.Find(&uu, "hobby=?", "足球")
fmt.Printf("%#v\n", uu)
?
// 更新
db.Model(&u).Update("hobby", "雙色球")
// 刪除
db.Delete(&u)
}
~~~