前言

  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,使用的相对路径,不是绝对路径。

gin框架学习手册的更多相关文章

  1. Golang gin框架学习

    今天开始学习gin框架,在Github上找的示例的go-gin-example, 进度 日期 进展 疑惑 进展 1.30 下拉代码,初步了解gin的介绍.搭建 .mod文件 module原理.使用方法 ...

  2. [Golang] Gin框架学习笔记

    0x0 Gin简介 1.Gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架. 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httpr ...

  3. 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin

    原文链接 我是一名五六年经验的前端程序员,现在准备学习一下 Golang 的后端框架 gin. 以下是我的学习实战经验,记录下来,供大家参考. https://github.com/gin-gonic ...

  4. 基于gin框架和jwt-go中间件实现小程序用户登陆和token验证

    本文核心内容是利用jwt-go中间件来开发golang webapi用户登陆模块的token下发和验证,小程序登陆功能只是一个切入点,这套逻辑同样适用于其他客户端的登陆处理. 小程序登陆逻辑 小程序的 ...

  5. Gin框架源码解析

    Gin框架源码解析 Gin框架是golang的一个常用的web框架,最近一个项目中需要使用到它,所以对这个框架进行了学习.gin包非常短小精悍,不过主要包含的路由,中间件,日志都有了.我们可以追着代码 ...

  6. Spring.NET依赖注入框架学习--简介

    Spring.NET依赖注入框架学习--Spring.NET简介 概述 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入. ...

  7. MyBean 框架入门手册<感谢[青铜]整理的如此细致和系统>

    MyBean 框架入门手册 2014/9/15 by lighttop 目 录 MyBean 框架学习笔记............................................... ...

  8. Gin 框架 - 使用 logrus 进行日志记录

    目录 概述 日志格式 Logrus 使用 推荐阅读 概述 上篇文章分享了 Gin 框架的路由配置,这篇文章分享日志记录. 查了很多资料,Go 的日志记录用的最多的还是 github.com/sirup ...

  9. 01-Spring Security框架学习

    目录 01-Spring Security框架学习 简介 Spring Security 是什么 Spring Security 解决那些问题 Spring Security 的优点 历史背景 Spr ...

随机推荐

  1. Linux: 软件包管理之rpm与yum [转]

    软件包的安装和卸载时很平常的事,但在Linux上面却不简单..Linux的其中一个哲学就是一个程序只做一件事,并且做好.组合小程序来完成复杂的任务,这样做有很多好处,但是各个小程序之间往往会存在着复杂 ...

  2. Shell按行读取文件的3种方法

    Shell按行读取文件的方法有很多,常见的三种方法如下: 要读取的文件: [root@mini05 -]# cat file.info 写法一: [root@mini05 -]# cat read1. ...

  3. JavaScript数据类型之布尔类型

    引言 布尔值指代真或假.开或关.是或否.这个类型只有两个值,保留字true和false.JavaScript程序中的比较语句的结果通常都是布尔值.布尔值通常用于JavaScript中的控制结构中. 真 ...

  4. 《Java大学教程》—第16章 二维数组

    多维(Multi-dimensional)数组维数由索引个数决定.常用的数组:一维(one-dimensional)数组.二维(two-dimensional)数组 16.2    创建二维数组索引从 ...

  5. C#基础知识之List和数组之间的转换

    1,从System.String[]转到List<System.String> System.String[] str={"str","string" ...

  6. mac下安装redis详细步骤

    Linux下安装redis也可以参照下面的步骤哦!!!! 1.到官网上下载redis,我下载的版本是redis-3.2.5.tar 官网地址:http://redis.io/ 2.将下载下来的tar. ...

  7. java 关于打断点

    比如:前台传过来参数中文乱码,需要decode才可以使用, 判断问题. debug 在 DispatcherServlet OncePerRequestFilter 打断点, 查看前台过来的中文在哪里 ...

  8. UVA116-Unidirectional TSP(动态规划基础)

    Problem UVA116-Unidirectional TSP Accept: 7167  Submit: 56893Time Limit: 3000 mSec Problem Descripti ...

  9. 有时间研究一下Maven打包插件细节

    Maven工作分为多个阶段,具体阶段参考:https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html ...

  10. html 传递参数中文乱码 js获取参数乱码

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code HTML传递中文参数时,有乱码导致接收不到正确的数据.JS中可以使用encodeURI ...