Gin框架簡介
Gin是一個golang的微框架,封裝比較優雅,API友好,源碼注釋比較明確,已經發布了1.0版本。具有快速靈活,容錯方便等特點。其實對於golang而言,web框架的依賴要遠比Python,Java之類的要小。自身的net/http足夠簡單,性能也非常不錯。框架更像是一些常用函數或者工具的集合。借助框架開發,不僅可以省去很多常用的封裝帶來的時間,也有助於團隊的編碼風格和形成規范。
Gin特性
/*
快速: 路由不使用反射,基於Radix樹,內存占用少
中間件: HTTP請求,先經過一系列中間件和最終操作來處理,例如: Logger, Authorization,GZIP等,
這個特性和NodeJs的Koa框架很像, 中間件機制也極大的提高了框架的可擴展性.
異常處理: 服務始終可用, 不會宕機,Gin可以捕獲panic,並恢復,而且極為便利的機制處理HTTP請求過程中發生的錯誤.
JSON: Gin可以解析並驗證請求的JSON, 這個特性對於Restful API的開發尤其有用.
路由分組: 例如需要授權和不需要授權的API分組,不同版本的API分組.
而且分組可嵌套,且性能不受影響.
渲染內置: 原生支持JSON, XML和HTML的渲染.
*/
govendor包管理
govendor好處
Go從1.5版本之后,就默認優先使用vendor子目錄中的依賴庫,而不是$GOPATH/src中的依賴包,這樣我們可以把工程源碼到處復制,使用時直接go build就可以了,不需要考慮一大堆第三方依賴包的管理問題。
安裝govendor
go get -u github.com/kardianos/govendor
/*
-v:打印出被構建的代碼包的名字
-u:已存在相關的代碼包,強行更新代碼包及其依賴包
*/
常用命令
/*
命令 功能
init 初始化 vendor 目錄
list 列出所有的依賴包
add 添加包到 vendor 目錄,如 govendor add +external 添加所有外部包
add PKG_PATH 添加指定的依賴包到 vendor 目錄
update 從 $GOPATH 更新依賴包到 vendor 目錄
remove 從 vendor 管理中刪除依賴
status 列出所有缺失、過期和修改過的包
fetch 添加或更新包到本地 vendor 目錄
sync 本地存在 vendor.json 時候拉去依賴包,匹配所記錄的版本
get 類似 go get 目錄,拉取依賴包到 vendor 目錄
*/
初始化項目並添加 gin
govendor init
/*
此時會在當前工程目錄(頂層)下面生成一個子目錄vendor,里面有個文件vendor.json記錄了一些模板信息。
*/
govendor add + external
/*
它會將你之前單獨安裝到$GOPATH/src目錄下的所有依賴包都復制到本項目的vendor下面,
並且在vendor.json列出詳細的清單。如果$GOPATH/src目錄下面沒有依賴包,
會解析當前項目中的go文件來將所有的依賴庫先go get下來,存放到vendor下面。
*/
govendor fetch github.com/gin-gonic/gin@v1.6.3
// 添加或更新包到本地vendor目錄
govendor list -v fmt
// 可以更詳細地查看各種包的依賴關系,但是有些包未顯示出來。
bee熱編譯
安裝
go get -u github.com/beego/bee
使用
# 進入你的項目目錄,注意:使用bee 項目必須要在GOPATH目錄下
$ cd /your_project
# 運行程序
$ bee run
gin熱編譯
gin
是用於實時重新加載Go Web應用程序的簡單命令行實用程序。只需gin
在您的應用程序目錄中運行,您的網絡應用程序將gin
作為代理提供。gin
檢測到更改后,將自動重新編譯您的代碼。您的應用在下次收到HTTP請求時將重新啟動。
gin
堅持“沉默就是黃金”的原則,因此,只有在出現編譯器錯誤或在錯誤發生后成功進行編譯時,它才會抱怨。
安裝
go get github.com/codegangsta/gin
使用
gin run main.go
cd 項目目錄
gin -p 3000 -a 9090 -b test.bin --all run
// 表示監聽虛擬機的3000端口,將請求轉發給9000端口,生成的二進制執行文件 test.bin,所有文件的改動都會引起項目編譯
安裝Gin
go get -u github.com/gin-gonic/gin
第一個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",
})
})
// 啟動HTTP服務,默認在0.0.0.0:8080啟動服務
r.Run()
}
/*
1. 首先我們使用gin.Default()生成了一個實例,這個實例即WSGI應用程序.
2. 接下來, 我們使用r.Get("/",...)聲明了一個路由,告訴Gin什么樣的URL能觸發傳入的函數,
這個函數返回我們想要顯示在用戶瀏覽器中的信息.
3. 最后用r.Run()函數讓應用運行在本地服務器上,默認監聽端口是_8080_, 可以傳入參數,
例如: r.Run(":9999")即運行在9999端口.
*/
路由
/*
GET 參數掛在url中,uri中傳參
POST 參數在form body中或者uri
DELETE 一般情況為uri,同樣也可以用Body
PUT 參數在form body或者uri中
*/
路由方法有GET, POST, PUT, PATCH, DELETE 和 OPTIONS,還有Any,可匹配以上任意類型的請求
無參數
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK,"wunai")
})
/*
curl http://127.0.0.1:8080
wunai
*/
解析路徑參數
有時候我們需要動態的路由,如/user/:name, 通過調用不同的url來傳入不同的Name, /user/:name/*role, *代表可選
// 匹配/user/youmen
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK,"Hello %s",name)
})
/*
curl http://127.0.0.1:8080/user/youmen
Hello youmen
*/
獲取Query參數
// 匹配users?name=xxx&role=xxx, role可選
r.GET("/users", func(c *gin.Context) {
name := c.Query("name")
role := c.DefaultQuery("role","teacher")
c.String(http.StatusOK,"%s is a %s",name,role)
})
/*
curl http://127.0.0.1:8080/users?name=youmen&role=student
youmen is a student
*/
http常見傳輸格式
/*
application/json
application/x-www-form-urlencoded
application/xml
multipart/form-data
表單參數可以通過PostForm()方法獲取,該方法默認解析的是x-www-form-urlencoded或from-data格式的參數
*/
獲取POST參數
參數一般在form body或者url中
// POST
r.POST("/form", func(c *gin.Context) {
username := c.PostForm("username")
password := c.DefaultPostForm("password","123") // 可設置默認值
c.JSON(http.StatusOK,gin.H{
"username":username,
"password":password,
})
})
/*
curl http://localhost:8080/form -X POST -d 'username=youmen&password=1234'
{"password":"1234","username":"youmen"}%
*/
Example2
gin_demo1.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
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",
})
})
r.POST("/form", func(c *gin.Context) {
// 表單參數設置默認值
type1 := c.DefaultPostForm("type","alert")
// 接受其他的
username := c.PostForm("username")
password := c.PostForm("password")
// 多選框
hobbys := c.PostFormArray("hobby")
c.String(http.StatusOK,fmt.Sprintf("type is %s, username is %s, password is %s, habbys is %v",
type1,username,password,hobbys))
})
// 啟動HTTP服務,默認在0.0.0.0:8080啟動服務
r.Run()
}
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8080/form" method="post" enctype="application/x-www-form-urlencoded">
用戶名: <input type="text" name="username">
密碼: <input type="password" name="password">
<input type="checkbox" value="run" name="hobby"> 跑步
<input type="checkbox" value="Weightlifting" name="hobby"> 舉重
<input type="checkbox" value="money" name="hobby"> 金錢
<input type="submit" value="注冊">
</form>
</body>
</html>
Query和POST混合參數
// GET和POST混合
r.POST("/posts", func(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
username := c.PostForm("username")
password := c.DefaultPostForm("username", "0000")
c.JSON(http.StatusOK, gin.H{
"id": id,
"page": page,
"username": username,
"password": password,
})
})
/*
curl "http://localhost:8080/posts?id=9876&page=7" -X POST -d 'username=geektutu&password=1234'
{"id":"9876","page":"7","password":"geektutu","username":"geektutu"}%
*/
Map參數(字典參數)
// Map參數(字典參數)
r.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
c.JSON(http.StatusOK,gin.H{
"ids": ids,
"names": names,
})
})
/*
curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
{"ids":{"Jack":"001","Tom":"002"},"names":{"a":"Sam","b":"David"}}
*/
重定向(Redirect)
r.GET("/redirect", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently,"/index")
})
r.GET("/index", func(c *gin.Context) {
c.Request.URL.Path = "/"
r.HandleContext(c)
})
/*
curl http://127.0.0.1:8080/redirect -i
HTTP/1.1 301 Moved Permanently
Content-Type: text/html; charset=utf-8
Location: /index
Date: Tue, 27 Oct 2020 07:40:25 GMT
Content-Length: 41
*/
分組路由
對router創建Group就是分組,同一分組擁有同一前綴和同一中間件
為什么要分組
/*
路由結構更加清晰
更加方便管理路由
*/
如果有一組路由,前綴都是
/api/v1
開頭,是否每個路由都需要加上/api/v1
這個前綴呢?答案是不需要,分組路由可以解決這個問題。利用分組路由還可以更好地實現權限控制,例如將需要登錄鑒權的路由放到同一分組中去,簡化權限控制。
// group routes 分組路由
defaultHandler := func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"path": c.FullPath(),
})
}
// group: v1
v1 := r.Group("/v1")
{
v1.GET("/posts", defaultHandler)
v1.GET("/series", defaultHandler)
}
// group: v2
v2 := r.Group("/v2")
{
v2.GET("/posts", defaultHandler)
v2.GET("/series", defaultHandler)
}
/*
curl http://localhost:8080/v1/posts
{"path":"/v1/posts"}
curl http://localhost:8080/v2/posts
{"path":"/v2/posts"}
*/
上傳文件
multipart/form-data格式用於文件上傳
gin文件上傳與原生的net/http方法類似,不同在於gin把原生的request封裝到c.Request中
上傳單個文件
upload.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
)
func main() {
// 創建一個默認的路由引擎
r := gin.Default()
// api參數
r.POST("/upload", func(c *gin.Context) {
// 表單取文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
// 傳到項目根目錄,名字就用本身的
c.SaveUploadedFile(file,file.Filename)
// 打印信息
c.String(200,fmt.Sprintf("'%s' upload!",file.Filename))
})
// 啟動HTTP服務,默認在0.0.0.0:8080啟動服務
r.Run()
}
Example1
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
r := gin.Default()
r.POST("/upload", func(c *gin.Context) {
file,_ := c.FormFile("file")
in,_ := file.Open()
defer in.Close()
out,_ := os.Create("./"+file.Filename)
defer out.Close()
io.Copy(out,in)
c.Writer.Header().Add("Content-Disposttion",fmt.Sprintf("attachment; filename=%s",file.Filename))
c.File("./"+file.Filename)
})
r.Run(":8080")
}
upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
頭像
<input type="file" name="file">
<input type="submit" value="提交">
</form>
</body>
</html>
多個文件
upload.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 創建一個默認的路由引擎
r := gin.Default()
r.MaxMultipartMemory = 8 << 20
r.POST("/upload", func(c *gin.Context) {
form , err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest,fmt.Sprintf("get err %s",err.Error()))
}
// 獲取所有圖片
files := form.File["files"]
// 遍歷所有圖片
for _, file := range files {
// 逐個存
if err := c.SaveUploadedFile(file,file.Filename); err != nil {
c.String(http.StatusBadRequest,fmt.Sprintf("upload err %s",err.Error()))
return
}
}
c.String(200,fmt.Sprintf("upload ok %d files",len(files)))
})
// 啟動HTTP服務,默認在0.0.0.0:8080啟動服務
r.Run()
}
upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
頭像
<input type="file" name="files" multiple>
<input type="submit" value="提交">
</form>
</body>
</html>