前言

  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. Windows 版本说明,Enterprise、Ultimate、Home、Professional知多少

    关于Windows 的安装光盘版本很多种,很多人不知道选择哪些. Ultimate 旗舰版,VISTA开始有了这个级别,是最全最高级的,一般程序开发的电脑,玩游戏的电脑,建议用它,不过对配置稍有一些要 ...

  2. SAP LOGON 快捷登陆方式如何保存密码

    默认情况下,快捷方式密码是不能输入的. 解决方法:修改注册表: 计算机\HKEY_CURRENT_USER\Software\SAP\SAPShortcut\Security EnablePasswo ...

  3. PXE 自动安装物理机 (DHCP服务由路由提供, 不能再配置)

    目录 1. PXE 自动安装物理机 (DHCP服务由路由提供, 不能再配置) 1.1. 需要的软件 1.2. 启动 proxy dhcp 服务 1.3. 关键的几个配置文件 PXE 自动安装物理机 ( ...

  4. Vue+Webpack常见问题(持续更新)

    常识 1.computed计算属性,使用的属性必需在data里面声明. computed: { canLogin: function(){ //注意这里的依赖的属性必需在data里面声明 return ...

  5. ABAP 7.40, SP08 中的 Open SQL 新特性

    1,使用 data_source~*指定列 在7.40, SP08中,可以在SELECT语句中使用data_source~*来指定选取不同的数据库表.视图的全部列来作为结果集.它也可以和单独指定的列c ...

  6. 洛谷P1904

    法一,数字太大,可能通过不了   #include <iostream>#include <algorithm>#include <cstdio>using nam ...

  7. ASP.NET 应用程序遭遇Server Application Unavailable问题的解决的方法

    公司服务器有.NET2的应用在执行,而我使用了.NET4平台开发,本机測试没问题,扔服务器发现要么我的新平台不好使,要么.NET2的旧平台不好使,各种重新启动IIS服务和WWW服务都无济于事 当我意识 ...

  8. centos7下安装docker(26如何配置Health Check)

    Docker只能从容器启动进程的返回代码判断其状态,而对于容器内部应用的运行状况基本没有了解 执行docker run命令时,通常根据dockerfile中的CMD或ENTRYPOINT启动一个进程, ...

  9. C++ 星号* 与 引用&

    星号 * 1. 声明的时候有*, 表示指针变量 int *p=&a;// '&'的作用就是把a变量在内存中的地址给提取出来 2. * +地址, 表示地址操作符 3. 数字*数字, 表示 ...

  10. WPF ListView点击删除某一行并获取绑定数据

    最近在开发WPF程序时遇到一个问题,在gridview中希望实现在每一行最后添加一个删除的按钮,但是发现点击每行的button时只会触发button的点击事件,并没有选中这一行,此时调用list.Se ...