前言

  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. 什么是JDK?什么是JRE?JDK与JRE的区别和用途

    一.编程环境与运行环境 JDK(Java Development Kit)称为Java开发包或Java开发工具.是一个编写Java的Applet小程序和应用程序的程序开发环境.JDK是整个Java的核 ...

  2. 【PAT】B1007 素数对猜想

    素数筛筛出规定范围内的所有素数存入数组 遍历数组,如果满足于后边的差2,计数器加加 #include <cstdio> const int maxn = 10000001; int pri ...

  3. 关于LVS负载均衡tcp长连接分发的解决思路

    虽然应用keepalived搞定了后端服务负载均衡和高可用性问题,但是在具体应用的时候,还是要注意很多问题.很多应用都用tcp或者http的长连接,因为建立tcp连接或者http连接开销比较大,而应用 ...

  4. CF369E Valera and Queries

    嘟嘟嘟 这题刚开始以为是一个简单题,后来越想越不对劲,然后就卡住了. 瞅了一眼网上的题解(真的只瞅了一眼),几个大字令人为之一振:正难则反! 没错,把点看成区间,比如2, 5, 6, 9就是[1, 1 ...

  5. GDB 命令回顾

    0) 为使用 GDB, 编译时需要加入调试信息 -g 选项,例如, $ gcc -g test.c -o test 1) 使用 GDB 开始调试 $ gdb test 也可以, $ gdb $ fil ...

  6. package-info.java的使用

    一.引入 上文中,提到了注解类JyzTargetPackage可以定义为@Target(ElementType.PACKAGE),可是在被注解类里我无论怎么加,编译器都报错,于是引入了package- ...

  7. Luogu5021 [NOIP2018]赛道修建

    Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...

  8. 【Windows】+ windows下在某一文件夹下按“shift+鼠标右键”打开CMD窗口

    前言:没更改之前是打开的powershell窗口,超不习惯 新建一个txt文件,然后复制以下代码,然后保存为OpenCmdHere.reg 格式,然后双击即可 Windows Registry Edi ...

  9. 用java语言写一个简易版本的登录页面,包含用户注册、用户登录、用户注销、修改密码等功能

    package com.Summer_0421.cn; import java.util.Arrays; import java.util.Scanner; /** * @author Summer ...

  10. nginx指定配置文件启动

    /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf