gin框架學習手冊


前言

  gin框架是go語言的一個框架,框架的github地址是:https://github.com/gin-gonic/gin

  轉載本文,請標注原文地址:https://www.cnblogs.com/-beyond/p/9391892.html 

 

安裝gin框架

go get -u github.com/gin-gonic/gin

  

第一次使用gin框架

  創建一個文件main.go,拷貝如下內容

package main

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

func main() {
	router := gin.Default()
	router.GET("/get", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "use get method"})
	})
	router.Run()
}

  使用 go run main.go 運行程序,程序默認綁定的是8080端口,測試使用瀏覽器訪問localhost:8080/get,會有如下response:

  

 

切換綁定端口

  gin框架默認的是綁定到8080端口,但是可以切換端口,方法就是指定router.Run()的參數。

  router.Run()的聲明如下:

func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine)
	return
}

  可以看到,Run方法也只是使用了http的ListenAndServe方法而已

  現在可以將端口綁定到9000端口,可以這樣寫:

router.Run(":9000")

  

gin.H{    }

  第1個代碼例子中,有這么一行c.JSON(200, gin.H{"message": "use get method"})

  這其中有一個gin.H{ },看樣子,這像是一個結構體struct,查看gin框架的源碼,聲明如下:

// H is a shortcut for map[string]interface{}
type H map[string]interface{}

  所以,這只是一個map結構,別以為是一個struct哈

  

設置http請求方式

  gin框架封裝了http庫,提供了GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS這些http請求方式。

  使用router.method()來綁定路由

  聲明如下:

func (group *RouterGroup) METHOD(relativePath string, handlers ...HandlerFunc) IRoutes

  其中的METHOD可以是上面的7種方式。

  例子:

package main

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

func main() {
	router := gin.Default()
	router.GET("/get", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use get method"}) })
	router.POST("/post", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use post method"}) })
	router.PUT("/put", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use put method"}) })
	router.DELETE("/delete", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use delete method"}) })
	router.PATCH("/patch", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use patch method"}) })
	router.HEAD("/head", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use head method"}) })
	router.OPTIONS("/options", func(c *gin.Context) { c.JSON(200, gin.H{"message": "use options method"}) })
	router.Run()
}

  使用對應的method訪問對應的path,會的對應的response。

  但是如果使用不對應的method訪問path,就會返回404,比如使用post方法訪問localhost:8080/get會返回404。

 

切換輸出的格式

  看下面的代碼:

router.GET("/get", func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "use get method"})
})

  上面的這段代碼,就是在用戶訪問localhost:8080時,c.JSON()返回一個響應,響應中的狀態碼是200,響應內容是一個JSON格式的字符串。

  那么我們就很容易知道,不同的響應內容格式是通過調用*gin.Context類型變量的不同方法來實現的。

  gin框架提供了很多響應內容的格式,常用的方法聲明如下:

//返回json格式
func (c *Context) JSON(code int, obj interface{})

//返回xml格式
func (c *Context) XML(code int, obj interface{})

//返回yaml格式
func (c *Context) YAML(code int, obj interface{})

//返回string格式
func (c *Context) String(code int, format string, values ...interface{})

//渲染html模板后返回
func (c *Context) HTML(code int, name string, obj interface{})

  示例:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	router.GET("/json", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "return json data"})
	})
	router.GET("/string", func(c *gin.Context) {
		c.String(200, "message %s", "return string data")
	})
	router.GET("/yaml", func(c *gin.Context) {
		arr := [][]string{
			{"one", "two", "three"},
			{"four", "five", "six"},
		}
		c.YAML(200, arr)
	})
	router.GET("/xml", func(c *gin.Context) {
		person := struct { //聲明一個匿名結構體
			Name string
			Age  int
		}{"Jane", 20}
		c.XML(200, fmt.Sprintln(person))
	})
	router.Run()
}

  

狀態碼

  注意上面每一個響應中都有一個狀態碼200。

  這個狀態碼不僅可以手動指定一個數字,比如200,500,404;也可以使用http包中的狀態碼,語義化的狀態碼更好理解;

  下面解釋http協議中所有的狀態碼了。

package http

// HTTP status codes as registered with IANA.
// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
const (
	StatusContinue           = 100 // RFC 7231, 6.2.1
	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
	StatusProcessing         = 102 // RFC 2518, 10.1

	StatusOK                   = 200 // RFC 7231, 6.3.1
	StatusCreated              = 201 // RFC 7231, 6.3.2
	StatusAccepted             = 202 // RFC 7231, 6.3.3
	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
	StatusNoContent            = 204 // RFC 7231, 6.3.5
	StatusResetContent         = 205 // RFC 7231, 6.3.6
	StatusPartialContent       = 206 // RFC 7233, 4.1
	StatusMultiStatus          = 207 // RFC 4918, 11.1
	StatusAlreadyReported      = 208 // RFC 5842, 7.1
	StatusIMUsed               = 226 // RFC 3229, 10.4.1

	StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
	StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
	StatusFound             = 302 // RFC 7231, 6.4.3
	StatusSeeOther          = 303 // RFC 7231, 6.4.4
	StatusNotModified       = 304 // RFC 7232, 4.1
	StatusUseProxy          = 305 // RFC 7231, 6.4.5
	_                       = 306 // RFC 7231, 6.4.6 (Unused)
	StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
	StatusPermanentRedirect = 308 // RFC 7538, 3

	StatusBadRequest                   = 400 // RFC 7231, 6.5.1
	StatusUnauthorized                 = 401 // RFC 7235, 3.1
	StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
	StatusForbidden                    = 403 // RFC 7231, 6.5.3
	StatusNotFound                     = 404 // RFC 7231, 6.5.4
	StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
	StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
	StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
	StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
	StatusConflict                     = 409 // RFC 7231, 6.5.8
	StatusGone                         = 410 // RFC 7231, 6.5.9
	StatusLengthRequired               = 411 // RFC 7231, 6.5.10
	StatusPreconditionFailed           = 412 // RFC 7232, 4.2
	StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
	StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
	StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
	StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
	StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
	StatusTeapot                       = 418 // RFC 7168, 2.3.3
	StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
	StatusLocked                       = 423 // RFC 4918, 11.3
	StatusFailedDependency             = 424 // RFC 4918, 11.4
	StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
	StatusPreconditionRequired         = 428 // RFC 6585, 3
	StatusTooManyRequests              = 429 // RFC 6585, 4
	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3

	StatusInternalServerError           = 500 // RFC 7231, 6.6.1
	StatusNotImplemented                = 501 // RFC 7231, 6.6.2
	StatusBadGateway                    = 502 // RFC 7231, 6.6.3
	StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
	StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
	StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
	StatusLoopDetected                  = 508 // RFC 5842, 7.2
	StatusNotExtended                   = 510 // RFC 2774, 7
	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)

  測試,在使用的時候,直接作為上面響應中的參數即可。

router.GET("/json", func(c *gin.Context) {
	c.JSON(http.StatusNotFound, gin.H{"message": "not found"})
	//等價於
	//c.JSON(404, gin.H{"message": "not found"})
})

  

  

綁定路由

  gin框架和使用原生http庫綁定路由的形式很相似,但是呢,go語言支持在路由中使用類似於正則表達式的路由,注意這里是類似於正則表達式,因為這里有一定的規則。

  

獲取URL中的路徑參數  

  比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wigth=100

  上面的這個鏈接中,可以通過向上面講的使用/user/:name/:age/:addr/:sex來分別匹配jane、20、beijing、female。

 獲取URL的所有路徑參數

  gin框架中如果使用URL傳遞參數,那么在綁定的時候,是這樣的:

/user/:name/:age/:addr/:sex

  上面這個path綁定了4個URL參數,分別是name、age、addr、sex

  首先gin.Context對象的Params屬性保存着URL中的所有參數:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
	//打印URL中所有參數
	c.JSON(200, fmt.Sprintln(c.Params))
})

  如果請求http://localhost:8080/user/jane/20/beijing/female

  得到的輸出如下:

"[{name jane} {age 20} {addr beijing} {sex female}]\n"

  注意最后面的那個換行符

 

 獲取URL中的指定路徑參數 

  使用gin.Context對象的Param(key)方法獲取某一個key的值,方法聲明如下:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
	name := c.Param("name") //獲取參數的時候,不要寫name前面的冒號:
	c.JSON(200, name)
})

  訪問http://localhost:8080/user/jane/20/beijing/female,輸出"jane"

 

獲取URL中請求的參數(GET請求)

  獲取URL中路徑值和獲取參數不一樣。

  比如:http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100

  可以使用接下在的方法獲取請求參數id、height、weight的值。

獲取指定參數的只

  使用gin.Context.Query(),

//返回URL中key的值
func (c *Context) Query(key string) string 

  測試:

router.GET("/user/:name/:age/:addr/:sex", func(c *gin.Context) {
	id := c.Query("id")
	height := c.Query("height")
	weight := c.Query("weight")
	c.JSON(200, gin.H{"id": id, "height": height, "weight": weight})
})

  訪問http://localhost:8080/user/jane/20/beijing/female?id=999&height=170&wight=100,輸出內容如下:

{"height":"170","id":"999","weight":"100"}

 

獲取指定參數的值(帶有默認值的接收)

  當然,以前寫php的時候,如果要想判斷是否接收到了某個參數,如果沒有接收到,那么就設置一個默認值,最常用的就是三元運算符,比如下面這樣:

$id = empty($_POST['id']) ? 0 : $_POST['id'];

  gin框架當然也想到了這么一點,gin.Context.DefaultQuery()方法,允許你指定接收的參數名,以及沒有接收到該參數值時,設置的默認值,聲明如下:

func (c *Context) DefaultQuery(key, defaultValue string) string 

  只有當請求沒有攜帶key,那么此時的默認值就會生效。其他情況,默認值不生效。即使URL中的該key的值為空,那么也不會啟用默認值,獲取的值就是空。

  注意,這是獲取URL中的參數值。 

 

接收multipart/urlencoded form(POST表單數據)

  和獲取URL中的參數很相似:

  GET請求中的參數使用Query(key),DefaultQuery(key,default)來獲取

  POST請求中的參數使用PostForm(key),DefaultPostForm(key,default)來獲取。

package main

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

func main() {
	router := gin.Default()
	router.POST("/post", func(c *gin.Context) {
		name := c.PostForm("name")
		age := c.PostForm("age")
		sex := c.DefaultPostForm("sex", "male")
		c.JSON(200, gin.H{"name": name, "age": age, "sex": sex})
	})
	router.Run()
}

  注意要想獲取POST方式傳遞的參數,那么綁定的路由監聽方式就必須是router.POST,不能使router.GET。

 

同時獲取URL中的參數和POST的參數

  前提:請求方必須是使用POST方式傳遞,只不過URL中也包含一部分參數而已。

  同時,服務器端必須使用router.POST方式來接收,不能使用router.GET方式接收,因為router.GET只能接收GET方式傳遞的數據。

package main

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

func main() {
	router := gin.Default()
	//注意,這里必須使用router.POST
	router.POST("/post", func(c *gin.Context) {
		name := c.PostForm("name")
		age := c.PostForm("age")
		sex := c.DefaultPostForm("sex", "male")
		addr := c.Query("addr")
		hobby := c.DefaultQuery("hobby", "basketball")
		c.JSON(200, gin.H{"name": name, "age": age, "sex": sex, "addr": addr, "hobby": hobby})
	})
	router.Run()
}

  請求:localhost:8080/post?addr=beijing&hobby=football

  同時使用post方式傳遞name=abc&age=21111&sex=female

  那么得到的響應就是如下內容:

{"addr":"beijing","age":"21111","hobby":"football","name":"abc","sex":"female"}

 

接收post發送的單個文件

  首先需要注意的是:請求方的method必須是post,Content-Type或者表單中要設成multipart/form-data。

  服務器端也要使用post方式接收。

  使用gin.Context.FormFile("file")來接收上傳的文件,聲明如下:

func (c *Context) FormFile(name string) (*multipart.FileHeader, error)

  示例:

package main

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

func main() {
	router := gin.Default()
	router.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("myfile") //獲取文件
		filename := file.Filename
		size := file.Size
		header := file.Header
		c.JSON(200, gin.H{
			"filename": filename,
			"size":     size,
			"header":   header,
		})
	})
	router.Run()
}

  使用瀏覽器發送文件:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title></title>
</head>
<body>
	<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
		上傳文件:<input type="file" name="myfile"><br>
		<input type="submit">
	</form>
</body>
</html>

  發送了一個文件后,獲得的響應如下:

{
    "filename": "index.html",
    "header": {
        "Content-Disposition": [
            "form-data; name=\"myfile\"; filename=\"index.html\""
        ],
        "Content-Type": [
            "text/html"
        ]
    },
    "size": 289
}

  

 

接收post發送的多個文件

  可以按照使用接收單個文件的套路,來處理多個文件。

  比如下面這樣:

package main

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

func main() {
	router := gin.Default()
	router.POST("/upload", func(c *gin.Context) {
		file_1, _ := c.FormFile("myfile_1")
		file_2, _ := c.FormFile("myfile_2")
		file_3, _ := c.FormFile("myfile_3")
		c.IndentedJSON(200, gin.H{
			"file_1": file_1.Filename,
			"file_2": file_2.Filename,
			"file_3": file_3.Filename,
		})
	})
	router.Run()
}

  html代碼如下:

<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
	上傳文件一:<input type="file" name="myfile_1"><br>
	上傳文件二:<input type="file" name="myfile_2"><br>
	上傳文件三:<input type="file" name="myfile_3"><br>
	<input type="submit">
</form>

  發送三個文件,獲得的相應:

{
    "file_1": "fire.png",
    "file_2": "index.html",
    "file_3": "Unknown.png"
}

  

  一次性接收多個文件

  前面這種方法雖然能接收很多文件,但是,每一個文件都要有單獨的name,這就存在一個問題,如果上傳的文件有成百上千個,那么就有的忙了。

  這里就可以使用gin框架封裝的另外一個接口

 

 

組路由

  什么叫組路由?為什么要使用組路由?優點?

  首先看下面這段代碼:

func main() {
	router := gin.Default()
	router.GET("/v1/demo", func(c *gin.Context) {
		c.JSON(200, gin.H{"data": "/v1/demo"})
	})
	router.GET("/v1/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"data": "/v1/test"})
	})
	router.GET("/v2/demo", func(c *gin.Context) {
		c.JSON(200, gin.H{"data": "/v2/demo"})
	})
	router.GET("/v2/test", func(c *gin.Context) {
		c.JSON(200, gin.H{"data": "/v2/test"})
	})
	router.Run()
}

  這段代碼一點毛病都沒有,運行也很正常,但是,有個問題,如果v1,v2是API的版本,那么上面這樣寫,是不是不太直觀呀,如果幾百個接口都有v1和v2兩個版本,那這個文件的可讀性太差了。

  所以,組路由就派上大用場了。

  上面的程序可以改寫下面這樣:

func main() {
	router := gin.Default()

	v1 := router.Group("/v1")
	{
		v1.GET("/demo", func(c *gin.Context) {
			c.JSON(200, gin.H{"data": "/v1/demo"})
		})
		v1.GET("/test", func(c *gin.Context) {
			c.JSON(200, gin.H{"data": "/v1/test"})
		})
	}

	v2 := router.Group("v2")
	{
		v2.GET("/demo", func(c *gin.Context) {
			c.JSON(200, gin.H{"data": "/v2/demo"})
		})
		v2.GET("/test", func(c *gin.Context) {
			c.JSON(200, gin.H{"data": "/v2/test"})
		})
	}
	router.Run()
}

  照樣一點毛病都沒有。

 

使用中間件

  之前我們使用的都是router.Default(),其實也是使用了中間件的,比如logger、recover,這是gin框架默認附帶的。

  

 

 

指定訪問日志文件

  這個就是類似於apache的訪問日志文件

func main() {
	//取消控制台中日志的字體顏色
	gin.DisableConsoleColor()

	//創建一個日志文件
	access_log, _ := os.Create("access_log.log")
	gin.DefaultWriter = io.MultiWriter(access_log)

	router := gin.Default()
	router.GET("/log", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "hello world"})
	})

	router.Run()
}

  嘗試請求http://localhost:8080/log,然后查看當前目錄的access_log.log文件。

		請求時間					狀態碼		請求處理時間		發起請求的IP		發起請求的方法  請求的路徑
[GIN] 2018/08/02 - 19:33:28 |   200   |   273.678µs   |      ::1 	|       GET         /log

  

綁定post參數到模型中

  請看下面的代碼,完成一個登錄驗證功能:

func main() {
	router := gin.Default()
	router.POST("/login", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		if username == "abc" && password == "123" {
			c.JSON(200, gin.H{"message": "welcome"})
		} else {
			c.JSON(401, gin.H{"message": "wrong username or password"})
		}
	})
	router.Run(":8080")
}

  

  現在gin框架提供另外一種方式,如下:

type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

func main() {
	router := gin.Default()

	router.POST("/loginJSON", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindJSON(&json); err == nil {
			if json.User == "abc" && 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()})
		}
	})

	router.POST("/loginForm", func(c *gin.Context) {
		var form Login
		if err := c.ShouldBind(&form); err == nil {
			if form.User == "abc" && 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()})
		}
	})

	router.Run()
}

  首先,綁定兩個路徑分別是loginJSON、loginForm。

  對於loginJSON來說,通過gin.Context.ShouldBindJSON(&json),可以將post方式傳遞json格式的username和password分別賦值給json這個結構體中的User和Password屬性,然后在使用該結構體來完成驗證。

  比如,請求http://localhost:8080/loginJSON,通過post請求傳遞了{"user":"abc","password":"123"},那么就可以通過驗證,獲得響應結果是:{"status":"you are logged in"},其他的登錄名和密碼都不能通過驗證。

  相對於loginJSON來說,loginForm中使用的gin.Context.ShouldBind(&json)是一樣的用法。

 

綁定GET參數到模型中

  從URL中獲取參數綁定到模型使用的是gin.ShouldBindQuery(&json)方法,注意此時綁定路由時,使用GET方法

  例子:

type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

func main() {
	router := gin.Default()

	router.GET("/login", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindQuery(&json); err == nil {
			if json.User == "abc" && json.Password == "123" {
				c.JSON(200, gin.H{"message": "welcome"})
			} else {
				c.JSON(401, gin.H{"message": "wrong username or password"})
			}
		} else {
			c.JSON(400, gin.H{"error": err.Error()})
		}
	})

	router.Run()
}

  測試:

  請求http://localhost:8080/login?user=abc&password=123,攜帶了user和password,返回{"message":"welcome"}

 

gin.HandlerFunc

  為什么突然提到這個,因為,我們一直在用它,比如下面的

router.GET("/demo", func(c *gin.Context) {
	c.JSON(200, gin.H{"message": "hello world"})
})

  綁定的func(c *gin.Context){ code } 就是gin.HandlerFunc,這里是一個匿名函數的形式,其實我們完全可以將它提出來,讓多個路徑共用,比如下面這樣:

//聲明一個gin.HandlerFunc
func response(c *gin.Context) {
	c.JSON(200, gin.H{"message": "hello world"})
}
func main() {
	router := gin.Default()
	router.GET("/demo", response)
	router.GET("/test", response)
	router.GET("/aaaa", response)
	router.Run()
}

  上面這個程序執行后,不管是訪問demo、test、aaaa都是使用同一個事件處理程序。

 

從POST或者GET中獲取數據綁定到模型

  如果了解PHP的話,就知道php接收數據有$_GET、$_POST超全局數組,分別用來接收get請求和get請求傳遞的參數。同時還有$_REQUEST超全局數組既可以接收get請求和post請求中的參數(這里其實不是$_GET、$_POST、$_REQUEST來接收,而是將接收到的數據存在這些超全局數組里面)。

  其實gin框架中雖然沒有$REQUEST,但是可以使用下面這個程序替代一下,原理就是上面的多個路由綁定同一個事件處理程序:

type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

//聲明一個gin.HandlerFunc
func LoginCheck(c *gin.Context) {
	var json Login
	var err error
	if err := c.ShouldBindQuery(&json); err == nil { //嘗試從get請求中獲取參數
		if json.User == "abc" && json.Password == "123" {
			c.JSON(200, gin.H{"message": "login in success by Get method"})
		} else {
			c.JSON(200, gin.H{"message": "login in failed by Get method"})
		}
	} else if err := c.ShouldBind(&json); err == nil { //嘗試從post請求中獲取參數
		if json.User == "abc" && json.Password == "123" {
			c.JSON(200, gin.H{"message": "login in success by POST method"})
		} else {
			c.JSON(200, gin.H{"message": "login in failed by POST method"})
		}
	}
	if err != nil { //解析請求中的參數失敗
		c.JSON(400, gin.H{"message": err.Error()})
	}
}
func main() {
	router := gin.Default()
	//對於GET和POST,都嘗試使用同一個事件處理程序
	router.GET("login", LoginCheck)
	router.POST("login", LoginCheck)
	router.Run()
}

  

  

接收checkbox的請求參數,綁定到模型中

 接收checkbox數據

  前面講的使用gin.Context.PostForm(key)接收單個post請求中參數key的值,但是如果傳遞多個同名的參數(checkbox)時,如下:

<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
	<input type="checkbox" name="hobby[]" value="swimming">游泳
	<input type="checkbox" name="hobby[]" value="basketball">籃球
	<input type="checkbox" name="hobby[]" value="football">足球
	<input type="submit">
</form>

  那么使用前面的PostForm(key)只能獲取到第一個值swimming。

  如果要接受多個hobby,就必須使用gin.Context.PostFormArray("key")來接收即可,向下面這樣:

func main() {
	router := gin.Default()
	
	router.POST("/checkbox", func (c *gin.Context) {
		data := c.PostFormArray("hobby[]") //注意接收的key是一個數組(后面加[])
		c.JSON(200, gin.H{"message": data})
	})
	
	router.Run()
}

  運行測試,全選checkbox,發送post請求后,獲得的相應如下:

{
	"message": [
		"swimming",
		"basketball",
		"football"
	]
}

  

 

 綁定到模型中

  如果要將收到的checkbox數據綁定到模型中,使用gin框架提供的方法,可以向下面這么做:

type CheckBox struct {
	//注意如果是checkbox,那么標簽中的key要加[],還有就是屬性一定要大寫(可見性)
	Hobby []string `form:"hobby[]" json:"hobby[]" binding:"required"`
}

func main() {
	router := gin.Default()
	router.POST("/checkbox", func(c *gin.Context) {
		var checkbox CheckBox
		if c.ShouldBind(&checkbox) == nil {
			c.JSON(200, gin.H{"hobby": checkbox.Hobby})
		} else {
			c.JSON(400, gin.H{"message": "invalid request params"})
		}
	})
	router.Run()
}

  很容易忽略的一點就是結構體中的Hobby屬性一定要大寫,否則因為可見性的規則,小寫的hobby是不能進行模型綁定的,訪問到的也是一個null值。

 

返回靜態文件

  其實在網站訪問過程中,有很多的資源都是靜態的,也就是說,這些靜態資源是可以直接從硬盤上讀取之后返回用戶,不需要服務器端在做多余的處理。

  gin框架中提供了三個方法來實現:

func main() {
	router := gin.Default()

	//設置靜態文件目錄,如果訪問localhost:8080/assets/test.txt
	//如果./assets/test.txt文件存在,那么就返回該文件,否則返回404
	router.Static("/assets", "./assets")

	//和上面的功能一樣
	router.StaticFS("/my_file", http.Dir("my_file"))

	//為單個文件綁定路由
	//可以通過訪問localhost:8080/family.pic來獲取./pic/family.pic文件
	router.StaticFile("/family.pic", "./pic/family.pic")
	
	router.Run()
}

  注意上面的綁定path,使用的相對路徑,不是絕對路徑。

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM