溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Gin Web Framework 中文版

發(fā)布時(shí)間:2020-08-21 04:38:20 來源:網(wǎng)絡(luò) 閱讀:3831 作者:哈哈怪1111 欄目:編程語言


Gin是用Go(Golang)編寫的一個(gè)網(wǎng)頁框架。它具有類似馬提尼的API,具有更好的性能,由于httprouter,速度提高了40倍。 烏龜運(yùn)維

1

2

#在example.go文件中假定以下代碼

$ cat example.go


1

2

3

4

5

6

7

8

9

10

11

12

13

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

}


1

2

# run example.go and visit 0.0.0.0:8080/ping on browser

$ go run example.go


Benchmarks

Gin uses a custom version of HttpRouter

See all benchmarks

Benchmark name(1)(2)(3)(4)
BenchmarkGin_GithubAll300004837500
BenchmarkAce_GithubAll1000013405913792167
BenchmarkBear_GithubAll500053444586448943
BenchmarkBeego_GithubAll300059244474705812
BenchmarkBone_GithubAll20069573086987848453
BenchmarkDenco_GithubAll1000015881920224167
BenchmarkEcho_GithubAll100001547006496203
BenchmarkGocraftWeb_GithubAll30005708061316561686
BenchmarkGoji_GithubAll200081803456112334
BenchmarkGojiv2_GithubAll200012139732747683712
BenchmarkGoJsonRest_GithubAll20007857961343712737
BenchmarkGoRestful_GithubAll30052381886896724519
BenchmarkGorillaMux_GithubAll100102577262118402272
BenchmarkHttpRouter_GithubAll2000010541413792167
BenchmarkHttpTreeMux_GithubAll1000031993465856671
BenchmarkKocha_GithubAll1000020944223304843
BenchmarkLARS_GithubAll200006256500
BenchmarkMacaron_GithubAll200011612702041942000
BenchmarkMartini_GithubAll20099917132265492325
BenchmarkPat_GithubAll2005590793149956827435
BenchmarkPossum_GithubAll1000031976884448609
BenchmarkR2router_GithubAll1000030513477328979
BenchmarkRivet_GithubAll1000013213416272167
BenchmarkTango_GithubAll3000552754638261618
BenchmarkTigerTonic_GithubAll100014394832391045374
BenchmarkTraffic_GithubAll10011383067265932921848
BenchmarkVulcan_GithubAll500039425319894609
  • (1):總重復(fù)次數(shù)達(dá)到的時(shí)間越長(zhǎng),意味著越有信心的結(jié)果

  • (2):?jiǎn)未沃貜?fù)持續(xù)時(shí)間(ns / op),越低越好

  • (3):堆內(nèi)存(B / op),越低越好

  • (4):每個(gè)重復(fù)的平均分配(分配/操作),越低越好

Gin v1. stable

  •  零分配路由器。

  • 仍然是最快的http路由器和框架。從路由到寫作。

  •  完整的單元測(cè)試套件

  •  測(cè)試戰(zhàn)斗

  •  API凍結(jié),新版本不會(huì)破壞你的代碼。

開始使用它

  1. 下載并安裝它


1

go get github.com/gin-gonic/gin


  1. 在你的代碼中導(dǎo)入它:


1

import "github.com/gin-gonic/gin"


  1. (可選)導(dǎo)入net/http。例如,如果使用常量如http.StatusOK。


1

import "net/http"


使用像Govendor這樣的供應(yīng)商工具

  1. go get govendor


1

$ go get github.com/kardianos/govendor


  1. 創(chuàng)建你的項(xiàng)目文件夾cd到里面


1

$ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_"


  1. Vendor init your project and add gin


1

2

$ govendor init

$ govendor fetch github.com/gin-gonic/gin@v1.2


  1. 在項(xiàng)目中復(fù)制起始模板


1

$ curl https://raw.githubusercontent.com/gin-gonic/gin/master/examples/basic/main.go > main.go


  1. Run your project


1

$ go run main.go


用jsoniter構(gòu)建

Ginencoding/json用作默認(rèn)的json包,但你可以通過從其他標(biāo)簽建立更改為jsoniter。

1

$ go build -tags=jsoniter .


API Examples

Using GET, POST, PUT, PATCH, DELETE and OPTIONS


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

func main() {

// Disable Console Color

// gin.DisableConsoleColor()

 

// Creates a gin router with default middleware:

// logger and recovery (crash-free) middleware

router := gin.Default()

 

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)

 

// By default it serves on :8080 unless a

// PORT environment variable was defined.

router.Run()

// router.Run(":3000") for a hard coded port

}


路徑中的參數(shù)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

func main() {

router := gin.Default()

 

// This handler will match /user/john but will not match neither /user/ or /user

router.GET("/user/:name", func(c *gin.Context) {

name := c.Param("name")

c.String(http.StatusOK, "Hello %s", name)

})

 

// However, this one will match /user/john/ and also /user/john/send

// If no other routers match /user/john, it will redirect to /user/john/

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")

}


查詢字符串參數(shù)


1

2

3

4

5

6

7

8

9

10

11

12

13

func main() {

router := gin.Default()

 

// Query string parameters are parsed using the existing underlying request object.

// The request responds to a 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")

 

c.String(http.StatusOK, "Hello %s %s", firstname, lastname)

})

router.Run(":8080")

}


Multipart/Urlencoded Form


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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")

}


Another example: query + post form


1

2

3

4

POST /post?id=1234&page=1 HTTP/1.1

Content-Type: application/x-www-form-urlencoded

 

name=manu&message=this_is_great


1

2

3

4

5

6

7

8

9

10

11

12

13

14

func main() {

router := gin.Default()

 

router.POST("/post", func(c *gin.Context) {

 

id := c.Query("id")

page := c.DefaultQuery("page", "0")

name := c.PostForm("name")

message := c.PostForm("message")

 

fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)

})

router.Run(":8080")

}


1

id: 1234; page: 1; name: manu; message: this_is_great


Upload files

單個(gè)文件

引用問題#774和詳細(xì)示例代碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

func main() {

router := gin.Default()

// Set a lower memory limit for multipart forms (default is 32 MiB)

// router.MaxMultipartMemory = 8 << 20  // 8 MiB

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")

}

How to curl:

1

2

3

curl -X POST http://localhost:8080/upload \

  -F "file=@/Users/appleboy/test.zip" \

  -H "Content-Type: multipart/form-data"


Multiple files

查看詳細(xì)的示例代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

func main() {

router := gin.Default()

// Set a lower memory limit for multipart forms (default is 32 MiB)

// router.MaxMultipartMemory = 8 << 20  // 8 MiB

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")

}

How to curl:

1

2

3

4

curl -X POST http://localhost:8080/upload \

  -F "upload[]=@/Users/appleboy/test1.zip" \

  -F "upload[]=@/Users/appleboy/test2.zip" \

  -H "Content-Type: multipart/form-data"


Grouping routes


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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")

}


沒有中間件的默認(rèn)空白Gin

使用

1

r := gin.New()

代替

1

2

// Default With the Logger and Recovery middleware already attached

r := gin.Default()


使用中間件


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

func main() {

// Creates a router without any middleware by default

r := gin.New()

 

// Global middleware

// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.

// By default gin.DefaultWriter = os.Stdout

r.Use(gin.Logger())

 

// Recovery middleware recovers from any panics and writes a 500 if there was one.

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")

}


如何寫日志文件


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

func main() {

    // Disable Console Color, you don't need console color when writing the logs to file.

    gin.DisableConsoleColor()

 

    // Logging to a file.

    f, _ := os.Create("gin.log")

    gin.DefaultWriter = io.MultiWriter(f)

 

    // Use the following code if you need to write the logs to file and console at the same time.

    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

 

    router := gin.Default()

    router.GET("/ping", func(c *gin.Context) {

        c.String(200, "pong")

    })

 

    router.Run(":8080")

}


模型綁定和驗(yàn)證

要將請(qǐng)求主體綁定到一個(gè)類型,使用模型綁定。我們目前支持綁定JSON,XML和標(biāo)準(zhǔn)表單值(foo = bar&boo = baz)。

杜松子酒使用go-playground / validator.v8進(jìn)行驗(yàn)證。在這里查看關(guān)于標(biāo)簽使用情況的完整文檔。

請(qǐng)注意,您需要在要綁定的所有字段上設(shè)置相應(yīng)的綁定標(biāo)簽。例如,從JSON綁定時(shí),設(shè)置json:"fieldname"。

另外,杜松子提供了兩套綁定方法:

  • 類型 – 必須綁定

    • 方法 – ,,BindBindJSONBindQuery

    • 行為 – 這些方法MustBindWith在引擎蓋下使用。如果存在綁定錯(cuò)誤,則請(qǐng)求被中止c.AbortWithError(400, err).SetType(ErrorTypeBind)。這將響應(yīng)狀態(tài)碼設(shè)置為400,并將Content-Type標(biāo)題設(shè)置為text/plain; charset=utf-8。請(qǐng)注意,如果在此之后嘗試設(shè)置響應(yīng)代碼,將會(huì)導(dǎo)致警告[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。如果您希望更好地控制行為,請(qǐng)考慮使用ShouldBind等效的方法。

  • 類型 – 應(yīng)該綁定

    • 方法 – ,,ShouldBindShouldBindJSONShouldBindQuery

    • 行為 – 這些方法ShouldBindWith在引擎蓋下使用。如果發(fā)生綁定錯(cuò)誤,則返回錯(cuò)誤,開發(fā)人員有責(zé)任正確處理請(qǐng)求和錯(cuò)誤。

當(dāng)使用綁定方法時(shí),杜松子試圖根據(jù)Content-Type頭來推斷活頁夾。如果你確定你是綁定的,你可以使用MustBindWithShouldBindWith。

您也可以指定特定字段是必需的。如果一個(gè)字段裝飾binding:"required"并綁定時(shí)有一個(gè)空值,將返回一個(gè)錯(cuò)誤。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

// 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 err := c.ShouldBindJSON(&json); err == 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"})

}

} else {

c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

}

})

 

// 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 err := c.ShouldBind(&form); err == 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"})

}

} else {

c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

}

})

 

// Listen and serve on 0.0.0.0:8080

router.Run(":8080")

}

Sample request

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

$ curl -v -X POST \

  http://localhost:8080/loginJSON \

  -H 'content-type: application/json' \

  -d '{ "user": "manu" }'

> POST /loginJSON HTTP/1.1

> Host: localhost:8080

> User-Agent: curl/7.51.0

> Accept: */*

> content-type: application/json

> Content-Length: 18

>

* upload completely sent off: 18 out of 18 bytes

< HTTP/1.1 400 Bad Request

< Content-Type: application/json; charset=utf-8

< Date: Fri, 04 Aug 2017 03:51:31 GMT

< Content-Length: 100

<

{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}


自定義驗(yàn)證器

也可以注冊(cè)自定義驗(yàn)證器。請(qǐng)參閱示例代碼。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

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 {

CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`

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 {

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()

binding.Validator.RegisterValidation("bookabledate", bookableDate)

route.GET("/bookable", getBookable)

route.Run(":8085")

}

 

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()})

}

}


1

2

3

4

5

$ curl "localhost:8085/bookable?check_in=2017-08-16&check_out=2017-08-17"

{"message":"Booking dates are valid!"}

 

$ curl "localhost:8085/bookable?check_in=2017-08-15&check_out=2017-08-16"

{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}


只綁定查詢字符串

ShouldBindQuery函數(shù)只綁定查詢參數(shù),而不是發(fā)布數(shù)據(jù)。查看詳細(xì)信息。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package main

 

import (

"log"

 

"github.com/gin-gonic/gin"

)

 

type Person struct {

Name    string `form:"name"`

Address string `form:"address"`

}

 

func main() {

route := gin.Default()

route.Any("/testing", startPage)

route.Run(":8085")

}

 

func startPage(c *gin.Context) {

var person Person

if c.ShouldBindQuery(&person) == nil {

log.Println("====== Only Bind By Query String ======")

log.Println(person.Name)

log.Println(person.Address)

}

c.String(200, "Success")

}


綁定查詢字符串或發(fā)布數(shù)據(jù)

查看詳細(xì)信息。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package main

 

import "log"

import "github.com/gin-gonic/gin"

import "time"

 

type Person struct {

Name     string    `form:"name"`

Address  string    `form:"address"`

Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`

}

 

func main() {

route := gin.Default()

route.GET("/testing", startPage)

route.Run(":8085")

}

 

func startPage(c *gin.Context) {

var person Person

// 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.ShouldBind(&person) == nil {

log.Println(person.Name)

log.Println(person.Address)

log.Println(person.Birthday)

}

 

c.String(200, "Success")

}

Test it with:

1

$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"


綁定HTML復(fù)選框

查看詳細(xì)信息

main.go

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

...

 

type myForm struct {

    Colors []string `form:"colors[]"`

}

 

...

 

func formHandler(c *gin.Context) {

    var fakeForm myForm

    c.ShouldBind(&fakeForm)

    c.JSON(200, gin.H{"color": fakeForm.Colors})

}

 

...

form.html

1

2

3

4

5

6

7

8

9

10

<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>

result:

1

{"color":["red","green","blue"]}


Multipart/Urlencoded binding


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package main

 

import (

"github.com/gin-gonic/gin"

)

 

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.ShouldBindWith(&form, binding.Form)

// or you can simply use autobinding with ShouldBind method:

var form LoginForm

// in this case proper binding will be automatically selected

if c.ShouldBind(&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")

}

Test it with:

1

$ curl -v --form user=user --form password=password http://localhost:8080/login


XML,JSON和YAML (rendering)渲染


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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, gin.H{"message": "hey", "status": http.StatusOK})

})

 

r.GET("/someYAML", func(c *gin.Context) {

c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})

})

 

// Listen and serve on 0.0.0.0:8080

r.Run(":8080")

}


SecureJSON

使用SecureJSON來防止json劫持。"while(1),"如果給定的結(jié)構(gòu)體是數(shù)組值,那么缺省前置于響應(yīng)主體。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

func main() {

r := gin.Default()

 

// You can also use your own secure json prefix

// r.SecureJsonPrefix(")]}',\n")

 

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")

}


提供靜態(tài)文件


1

2

3

4

5

6

7

8

9

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 (rendering)渲染

使用LoadHTMLGlob()或LoadHTMLFiles()

1

2

3

4

5

6

7

8

9

10

11

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")

}

templates/index.tmpl

1

2

3

4

5

<html>

<h2>

{{ .title }}

</h2>

</html>

在不同的目錄中使用同名的模板

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

func main() {

router := gin.Default()

router.LoadHTMLGlob("templates/**/*")

router.GET("/posts/index", func(c *gin.Context) {

c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{

"title": "Posts",

})

})

router.GET("/users/index", func(c *gin.Context) {

c.HTML(http.StatusOK, "users/index.tmpl", gin.H{

"title": "Users",

})

})

router.Run(":8080")

}

templates/posts/index.tmpl

1

2

3

4

5

6

7

{{ define "posts/index.tmpl" }}

<html><h2>

{{ .title }}

</h2>

<p>Using posts/index.tmpl</p>

</html>

{{ end }}


1

templates/users/index.tmpl


1

2

3

4

5

6

7

{{ define "users/index.tmpl" }}

<html><h2>

{{ .title }}

</h2>

<p>Using users/index.tmpl</p>

</html>

{{ end }}


自定義模板渲染器

你也可以使用你自己的html模板渲染

1

2

3

4

5

6

7

8

import "html/template"

 

func main() {

router := gin.Default()

html := template.Must(template.ParseFiles("file1", "file2"))

router.SetHTMLTemplate(html)

router.Run(":8080")

}


自定義分隔符

您可以使用自定義分隔符

1

2

3

r := gin.Default()

r.Delims("{[{", "}]}")

r.LoadHTMLGlob("/path/to/templates"))


自定義模板功能

查看詳細(xì)的示例代碼。

main.go

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

import (

    "fmt"

    "html/template"

    "net/http"

    "time"

 

    "github.com/gin-gonic/gin"

)

 

func formatAsDate(t time.Time) string {

    year, month, day := t.Date()

    return fmt.Sprintf("%d%02d/%02d", year, month, day)

}

 

func main() {

    router := gin.Default()

    router.Delims("{[{", "}]}")

    router.SetFuncMap(template.FuncMap{

        "formatAsDate": formatAsDate,

    })

    router.LoadHTMLFiles("./fixtures/basic/raw.tmpl")

 

    router.GET("/raw", func(c *gin.Context) {

        c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{

            "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),

        })

    })

 

    router.Run(":8080")

}

raw.tmpl

1

Date: {[{.now | formatAsDate}]}

Result:

1

Date: 2017/07/01


Multitemplate

Gin允許默認(rèn)只使用一個(gè)html.Template。檢查使用功能的多模板渲染,如go 1.6 block template。

重定向

發(fā)出HTTP重定向很簡(jiǎn)單:

1

2

3

r.GET("/test", func(c *gin.Context) {

c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")

})

內(nèi)部和外部位置均受支持。

自定義中間件


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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")

}


使用BasicAuth()中間件


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

//模擬一些私人數(shù)據(jù)

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, gin.H{"user": user, "secret": "NO SECRET :("})

}

})

 

// Listen and serve on 0.0.0.0:8080

r.Run(":8080")

}


Goroutines在一個(gè)中間件里面

在中間件或處理程序中啟動(dòng)新的Goroutine時(shí),不應(yīng)使用其中的原始上下文,而必須使用只讀副本。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

func main() {

r := gin.Default()

 

r.GET("/long_async", func(c *gin.Context) {

// create copy to be used inside the goroutine

cCp := c.Copy()

go func() {

// simulate a long task with time.Sleep(). 5 seconds

time.Sleep(5 * time.Second)

 

// note that you are using the copied context "cCp", IMPORTANT

log.Println("Done! in path " + cCp.Request.URL.Path)

}()

})

 

r.GET("/long_sync", func(c *gin.Context) {

// simulate a long task with time.Sleep(). 5 seconds

time.Sleep(5 * time.Second)

 

// since we are NOT using a goroutine, we do not have to copy the context

log.Println("Done! in path " + c.Request.URL.Path)

})

 

// Listen and serve on 0.0.0.0:8080

r.Run(":8080")

}


自定義HTTP配置

http.ListenAndServe()直接使用,如下所示:

1

2

3

4

func main() {

router := gin.Default()

http.ListenAndServe(":8080", router)

}

1

2

3

4

5

6

7

8

9

10

11

12

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()

}


支持讓我們加密

1行LetsEncrypt HTTPS服務(wù)器的示例。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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"))

}

自定義autocert管理器的例子。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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", func(c *gin.Context) {

c.String(200, "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))

}


使用Gin運(yùn)行多個(gè)服務(wù)

查看問題并嘗試以下示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

package main

 

import (

"log"

"net/http"

"time"

 

"github.com/gin-gonic/gin"

"golang.org/x/sync/errgroup"

)

 

var (

g errgroup.Group

)

 

func router01() http.Handler {

e := gin.New()

e.Use(gin.Recovery())

e.GET("/", func(c *gin.Context) {

c.JSON(

http.StatusOK,

gin.H{

"code":  http.StatusOK,

"error": "Welcome server 01",

},

)

})

 

return e

}

 

func router02() http.Handler {

e := gin.New()

e.Use(gin.Recovery())

e.GET("/", func(c *gin.Context) {

c.JSON(

http.StatusOK,

gin.H{

"code":  http.StatusOK,

"error": "Welcome server 02",

},

)

})

 

return e

}

 

func main() {

server01 := &http.Server{

Addr:         ":8080",

Handler:      router01(),

ReadTimeout:  5 * time.Second,

WriteTimeout: 10 * time.Second,

}

 

server02 := &http.Server{

Addr:         ":8081",

Handler:      router02(),

ReadTimeout:  5 * time.Second,

WriteTimeout: 10 * time.Second,

}

 

g.Go(func() error {

return server01.ListenAndServe()

})

 

g.Go(func() error {

return server02.ListenAndServe()

})

 

if err := g.Wait(); err != nil {

log.Fatal(err)

}

}


優(yōu)雅的重啟或停止

你想優(yōu)雅地重新啟動(dòng)或停止你的網(wǎng)絡(luò)服務(wù)器?有一些辦法可以做到。

我們可以使用fvbock / endless來替換默認(rèn)值ListenAndServe。有關(guān)更多詳細(xì)信息,請(qǐng)參閱問題#296。

1

2

3

4

router := gin.Default()

router.GET("/", handler)

// [...]

endless.ListenAndServe(":4242", router)

無止境的替代:

  • 禮貌:禮貌的Go HTTP服務(wù)器,優(yōu)雅地關(guān)閉。

  • 優(yōu)雅:優(yōu)雅是一個(gè)Go包,可以正常關(guān)閉http.Handler服務(wù)器。

  • 寬限期:Go服務(wù)器的平穩(wěn)重啟和零宕機(jī)部署。

如果你使用的是Go 1.8,你可能不需要使用這個(gè)庫??紤]使用http.Server內(nèi)置的Shutdown()方法來正常關(guān)閉。用杜松子酒查看完整的關(guān)機(jī)示例。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

// +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 exiting")

}


測(cè)試

net/http/httptest包是HTTP測(cè)試的首選方式。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package main

 

func setupRouter() *gin.Engine {

r := gin.Default()

r.GET("/ping", func(c *gin.Context) {

c.String(200, "pong")

})

return r

}

 

func main() {

r := setupRouter()

r.Run(":8080")

}

測(cè)試上面的代碼示例:

Go

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package main

 

import (

"net/http"

"net/http/httptest"

"testing"

 

"github.com/stretchr/testify/assert"

)

 

func TestPingRoute(t *testing.T) {

router := setupRouter()

 

w := httptest.NewRecorder()

req, _ := http.NewRequest("GET", "/ping", nil)

router.ServeHTTP(w, req)

 

assert.Equal(t, 200, w.Code)

assert.Equal(t, "pong", w.Body.String())

}


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI