Gin学习
1 基础环境
首先配置好go环境,设置好GOPATH环境变量。我的为例,输入go env
,输出如下:
C:\Users\Xin>go env
set GOARCH=386
set GOBIN=C:\go\bin // 必须设置
set GOEXE=.exe
set GOHOSTARCH=386
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\go // 必须设置
set GORACE=
set GOROOT=C:\go // 必须设置
set GOTOOLDIR=C:\go\pkg\tool\windows_386
set GCCGO=gccgo
set GO386=
set CC=gcc
set GOGCCFLAGS=-m32 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
set PKG_CONFIG=pkg-config
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
然后,使用go下载gin库,go get github.com/gin-gonic/gin
,一般使用需要的依赖:
import "github.com/gin-gonic/gin"
import "net/http"
2 Demo和学习记录
2.1 最简单的Demo
首先官方最简单的一个Demo:
// 文件名为 t1.go
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() // listen and serve on 0.0.0.0:8080
}
- 在
%GOPATH%\src
文件夹中创建一个自己的项目文件夹,比如 Demo1,然后新建一个go文件,如t1.go。将上述代码复制进入,保存。 - 新建CMD窗口,进入Demo1所在文件夹,然后输入
go run t1.go
将我们的代码运行起来。 - 在浏览器中输入
http://127.0.0.1:8080/ping
,浏览器如果有回复,则说明我们代码成功!
2.2 路由与参数获取
路由的几种方式:
- 绝对匹配。对/xxx
的GET/POST/DELETE/HEAD…请求的路由。
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)
route.Any("/testing",startPage)
/user/:name
对二级名称关联name变量。/user/:name/*action
与上一个的区别是*
部分可以有,也可以没有,即可匹配/user/:name/
url。
现代化路由形式:
分组的路由:
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login",loginEndpoint)
v1.POST("/submit",submitEndpoint)
v1.POST("/read",readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login",loginEndpoint)
v2.POST("/submit",submitEndpoint)
v2.POST("/read",readEndpoint)
}
router.Run(":8080")
}
func main() {
router := gin.Default()
// url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome",func(c *gin.Context) {
firstname := c.DefaultQuery("firstname","Guest") // 带有默认返回值的获取方式
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname"),可能为nil
c.String(http.StatusOK,"Hello %s %s",firstname,lastname)
})
router.Run(":8080")
}
Multipart/UrlEncode表单:
func main() {
router := gin.Default()
router.POST("/form_post",func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick","anonymous")
c.JSON(200,gin.H{
"status": "posted","message": message,"nick": nick,})
})
router.Run(":8080")
}
当两种方式都存在时,可以混合使用。
func main() {
router := gin.Default()
router.POST("/upload",func(c *gin.Context) {
// single file
file,_ := c.FormFile("file")
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file,dst)
c.String(http.StatusOK,fmt.Sprintf("'%s' uploaded!",file.Filename))
})
router.Run(":8080")
}
多个文件:
func main() {
router := gin.Default()
router.POST("/upload",func(c *gin.Context) {
// Multipart form
form,_ := c.MultipartForm()
files := form.File["upload[]"]
for _,file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file,dst)
}
c.String(http.StatusOK,fmt.Sprintf("%d files uploaded!",len(files)))
})
router.Run(":8080")
}
2.3 中间件
在上面的代码中,我们创建gin的实例使用的是r := gin.Default()
里面开启了很多默认的中间件。如果你想使用一个干净的,没有任何中间件的gin实例,可以使用r := gin.New()
。
func main() {
// Creates a router without any middleware by default
r := gin.New()
// Global middleware
r.Use(gin.Logger())
r.Use(gin.Recovery())
// Per route middleware,you can add as many as you desire.
r.GET("/benchmark",MyBenchLogger(),benchEndpoint)
// Authorization group
// authorized := r.Group("/",Authrequired())
// exactly the same as:
authorized := r.Group("/")
// per group middleware! in this case we use the custom created
// Authrequired() middleware just in the "authorized" group.
authorized.Use(Authrequired())
{
authorized.POST("/login",loginEndpoint)
authorized.POST("/submit",submitEndpoint)
authorized.POST("/read",readEndpoint)
// nested group
testing := authorized.Group("testing")
testing.GET("/analytics",analyticsEndpoint)
}
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
2.4 数据绑定与验证
Gin支持绑定的数据格式为Json,XML,Url Param(foo=bar&boo=baz)。数据验证使用的是go-playground/validator.v8
。
// 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()
// Example for binding 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"})
}
}
})
// Example for binding a HTML form (user=manu&password=123)
router.POST("/loginForm",func(c *gin.Context) {
var form Login
// This will infer what binder to use depending on the content-type header.
if c.Bind(&form) == nil {
if form.User == "manu" && form.Password == "123" {
c.JSON(http.StatusOK,gin.H{"status": "unauthorized"})
}
}
})
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
如果只绑定URL的字段,可以使用如下:
if c.BindQuery(&person) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
}
如果POST和URL数据都需要:
// If `GET`,only `Form` binding engine (`query`) used.
// If `POST`,first checks the `content-type` for `JSON` or `XML`,then uses `Form` (`form-data`).
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
if c.Bind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
}
绑定HTML中的checkBoxes:
// HTML部分
<form action="/" method="POST">
<p>Check some colors</p>
<label for="red">Red</label>
<input type="checkBox" name="colors[]" value="red" id="red" />
<label for="green">Green</label>
<input type="checkBox" name="colors[]" value="green" id="green" />
<label for="blue">Blue</label>
<input type="checkBox" name="colors[]" value="blue" id="blue" />
<input type="submit" />
</form>
// go语言部分
type myForm struct {
Colors []string `form:"colors[]"`
}
...
func formHandler(c *gin.Context) {
var fakeForm myForm
c.Bind(&fakeForm)
c.JSON(200,gin.H{"color": fakeForm.Colors})
}
Multipart/Urlencoded的绑定:
type LoginForm struct {
User string `form:"user" binding:"required"`
Password string `form:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/login",func(c *gin.Context) {
// you can bind multipart form with explicit binding declaration:
// c.MustBindWith(&form,binding.Form)
// or you can simply use autobinding with Bind method:
var form LoginForm
// in this case proper binding will be automatically selected
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"})
}
}
})
router.Run(":8080")
}
2.5 数据渲染
XML/JSON/YAML类型数据:
func main() {
r := gin.Default()
// gin.H is a shortcut for map[string]interface{}
r.GET("/someJSON",func(c *gin.Context) {
c.JSON(http.StatusOK,gin.H{"message": "hey","status": http.StatusOK})
})
r.GET("/moreJSON",func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// Note that msg.Name becomes "user" in the JSON
// Will output : {"user": "Lena","Message": "hey","Number": 123}
c.JSON(http.StatusOK,msg)
})
r.GET("/someXML",func(c *gin.Context) {
c.XML(http.StatusOK,"status": http.StatusOK})
})
r.GET("/someYAML",func(c *gin.Context) {
c.YAML(http.StatusOK,"status": http.StatusOK})
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
SecureJSON:(原始JSON加前缀)
func main() {
r := gin.Default()
// You can also use your own secure json prefix
r.SecureJsonPrefix("myTitle") // 默认是while(1);
r.GET("/someJSON",func(c *gin.Context) {
names := []string{"lena","austin","foo"}
// Will output : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK,names)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
静态文件渲染:
func main() {
router := gin.Default()
router.Static("/assets","./assets")
router.StaticFS("/more_static",http.Dir("my_file_system"))
router.StaticFile("/favicon.ico","./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}
HTML页面渲染:
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")
}
2.6 自定义
自定义模版渲染:
import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1","file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}
自定义分隔符:
r := gin.Default()
r.Delims("{[{","}]}")
r.LoadHTMLGlob("/path/to/templates"))
func formatAsDate(t time.Time) string {
year,month,day := t.Date()
return fmt.Sprintf("%d%02d/%02d",year,day)
}
func main() {
router := gin.Default()
router.Delims("{[{","}]}")
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,})
router.LoadHTMLFiles("./fixtures/basic/raw.tmpl")
router.GET("/raw","raw.tmpl",map[string]interface{}{
"now": time.Date(2017, 07, 01, 0,time.UTC),})
})
router.Run(":8080")
}
// taw.tmpl
Date: {[{.now | formatAsDate}]}
重定向:
r.GET("/test",func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently,"http://www.google.com/")
})
2.7 自定义中间件
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// Set example variable
c.Set("example","12345")
// before request
c.Next()
// after request
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) {
example := c.MustGet("example").(string)
// it would print: "12345"
log.Println(example)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
在定制中间件时,不要直接使用原始context,而要使用一个read-only的copy。
授权验证中间件:
// simulate some private data
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()
// Group using gin.BasicAuth() middleware
// gin.Accounts is a shortcut for map[string]string
authorized := r.Group("/admin",gin.BasicAuth(gin.Accounts{
"foo": "bar","austin": "1234","lena": "hello2","manu": "4321",}))
// /admin/secrets endpoint
// hit "localhost:8080/admin/secrets
authorized.GET("/secrets",func(c *gin.Context) {
// get user,it was set by the BasicAuth middleware
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,"secret": "NO SECRET :("})
}
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
2.8 定制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()
}
2.10 支持加密HTTPS
最简单的方式:
package main
import (
"log"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping",func(c *gin.Context) {
c.String(200,"pong")
})
log.Fatal(autotls.Run(r,"example1.com","example2.com"))
}
自己管理证书的方式:
package main
import (
"log"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping","pong")
})
m := autocert.Manager{
Prompt: autocert.AcceptTOS,HostPolicy: autocert.HostWhitelist("example1.com","example2.com"),Cache: autocert.DirCache("/var/www/.cache"),}
log.Fatal(autotls.RunWithManager(r,&m))
}
2.11 优雅地关闭服务
// +build go1.8
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/",func(c *gin.Context) {
time.Sleep(5 * time.Second)
c.String(http.StatusOK,"Welcome Gin Server")
})
srv := &http.Server{
Addr: ":8080",Handler: router,}
go func() {
// service connections
if err := srv.ListenAndServe(); err != nil {
log.Printf("listen: %s\n",err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal)
signal.Notify(quit,os.Interrupt)
<-quit
log.Println("Shutdown Server ...")
ctx,cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:",err)
}
log.Println("Server exist")
}
原文链接:https://www.f2er.com/go/187765.html