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,使用的相对路径,不是绝对路径。
gin框架学习手册的更多相关文章
- Golang gin框架学习
今天开始学习gin框架,在Github上找的示例的go-gin-example, 进度 日期 进展 疑惑 进展 1.30 下拉代码,初步了解gin的介绍.搭建 .mod文件 module原理.使用方法 ...
- [Golang] Gin框架学习笔记
0x0 Gin简介 1.Gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架. 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httpr ...
- 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
原文链接 我是一名五六年经验的前端程序员,现在准备学习一下 Golang 的后端框架 gin. 以下是我的学习实战经验,记录下来,供大家参考. https://github.com/gin-gonic ...
- 基于gin框架和jwt-go中间件实现小程序用户登陆和token验证
本文核心内容是利用jwt-go中间件来开发golang webapi用户登陆模块的token下发和验证,小程序登陆功能只是一个切入点,这套逻辑同样适用于其他客户端的登陆处理. 小程序登陆逻辑 小程序的 ...
- Gin框架源码解析
Gin框架源码解析 Gin框架是golang的一个常用的web框架,最近一个项目中需要使用到它,所以对这个框架进行了学习.gin包非常短小精悍,不过主要包含的路由,中间件,日志都有了.我们可以追着代码 ...
- Spring.NET依赖注入框架学习--简介
Spring.NET依赖注入框架学习--Spring.NET简介 概述 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入. ...
- MyBean 框架入门手册<感谢[青铜]整理的如此细致和系统>
MyBean 框架入门手册 2014/9/15 by lighttop 目 录 MyBean 框架学习笔记............................................... ...
- Gin 框架 - 使用 logrus 进行日志记录
目录 概述 日志格式 Logrus 使用 推荐阅读 概述 上篇文章分享了 Gin 框架的路由配置,这篇文章分享日志记录. 查了很多资料,Go 的日志记录用的最多的还是 github.com/sirup ...
- 01-Spring Security框架学习
目录 01-Spring Security框架学习 简介 Spring Security 是什么 Spring Security 解决那些问题 Spring Security 的优点 历史背景 Spr ...
随机推荐
- Windows 版本说明,Enterprise、Ultimate、Home、Professional知多少
关于Windows 的安装光盘版本很多种,很多人不知道选择哪些. Ultimate 旗舰版,VISTA开始有了这个级别,是最全最高级的,一般程序开发的电脑,玩游戏的电脑,建议用它,不过对配置稍有一些要 ...
- SAP LOGON 快捷登陆方式如何保存密码
默认情况下,快捷方式密码是不能输入的. 解决方法:修改注册表: 计算机\HKEY_CURRENT_USER\Software\SAP\SAPShortcut\Security EnablePasswo ...
- PXE 自动安装物理机 (DHCP服务由路由提供, 不能再配置)
目录 1. PXE 自动安装物理机 (DHCP服务由路由提供, 不能再配置) 1.1. 需要的软件 1.2. 启动 proxy dhcp 服务 1.3. 关键的几个配置文件 PXE 自动安装物理机 ( ...
- Vue+Webpack常见问题(持续更新)
常识 1.computed计算属性,使用的属性必需在data里面声明. computed: { canLogin: function(){ //注意这里的依赖的属性必需在data里面声明 return ...
- ABAP 7.40, SP08 中的 Open SQL 新特性
1,使用 data_source~*指定列 在7.40, SP08中,可以在SELECT语句中使用data_source~*来指定选取不同的数据库表.视图的全部列来作为结果集.它也可以和单独指定的列c ...
- 洛谷P1904
法一,数字太大,可能通过不了 #include <iostream>#include <algorithm>#include <cstdio>using nam ...
- ASP.NET 应用程序遭遇Server Application Unavailable问题的解决的方法
公司服务器有.NET2的应用在执行,而我使用了.NET4平台开发,本机測试没问题,扔服务器发现要么我的新平台不好使,要么.NET2的旧平台不好使,各种重新启动IIS服务和WWW服务都无济于事 当我意识 ...
- centos7下安装docker(26如何配置Health Check)
Docker只能从容器启动进程的返回代码判断其状态,而对于容器内部应用的运行状况基本没有了解 执行docker run命令时,通常根据dockerfile中的CMD或ENTRYPOINT启动一个进程, ...
- C++ 星号* 与 引用&
星号 * 1. 声明的时候有*, 表示指针变量 int *p=&a;// '&'的作用就是把a变量在内存中的地址给提取出来 2. * +地址, 表示地址操作符 3. 数字*数字, 表示 ...
- WPF ListView点击删除某一行并获取绑定数据
最近在开发WPF程序时遇到一个问题,在gridview中希望实现在每一行最后添加一个删除的按钮,但是发现点击每行的button时只会触发button的点击事件,并没有选中这一行,此时调用list.Se ...