学习 Gin 问题总结 2020.12.29
数据绑定与解析
BindXXX
,ShouldBindXXX
和ShouldBindWith
区别
-
BindXXX
会自动返回信息,输入无效时,在
header
写入400。 -
ShouldBindXXX
返回消息,输入无效时,不会在
header
写入400状态码,这时候可以自定义返回信息,在使用上相对来说更加灵活。 -
ShouldBindWith
在
gin
1.4 之前,重复使用ShouldBind
绑定会报错EOF
。gin
1.4 之后官方提供了一个ShouldBindBodyWith
的方法,可以支持重复绑定,原理就是将body
的数据缓存了下来,但是二次取数据的时候还是得用ShouldBindBodyWith
才行,直接用ShouldBind
还是会报错的。根据方法内参数
b
的类型去绑定json
,query
,去绑定obj
,不返回状态码。
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
return b.Bind(c.Request, obj)
}
HTML渲染
func main() {
// 1.创建路由
r := gin.Default()
//静态文件,声明模板文件所在的目录,系统自动解析
r.LoadHTMLGlob("static/html/*")
...
}
LoadHTMLGlob()
,该方法参数内的目录下,不能有二级目录,存在二级目录将会panic
。
而且该方法只能在一个实例中对一个目录使用一次(使用过的目录下的子目录同样是为已使用)。
使用两次
//静态文件,声明模板文件所在的目录,系统自动解析
r.LoadHTMLGlob("static/html/*")
r.LoadHTMLGlob("static/html/html2/*")
将会:
panic: read statichtmlhtml2: The handle is invalid.
如果需要解析两个目录下的HTML
文件,可在同一目录下创建两个文件夹:
//静态文件,声明模板文件所在的目录,系统自动解析
r.LoadHTMLGlob("static/html/*")
r.LoadHTMLGlob("static/html2/*")
[GIN-debug] Loaded HTML Templates (4):
- form.html
- login.html
- upload.html
-[GIN-debug] Loaded HTML Templates (2):
- uploadMore.html
-
此时将会正常解析两个目录下的HTML
模板。
还可以使用:
//静态文件,声明模板文件所在的目录,系统自动解析
r.LoadHTMLGlob("static/**/*")
-[GIN-debug] Loaded HTML Templates (5):
- form.html - login.html - upload.html - uploadMore.html
static
目录下的子目录内的所有模板文件都将被解析。
注意:static
目录下的模板文件将不会被解析。
重定向
c.Redirect(http.StatusPermanentRedirect,"http://localhost:8080/updateBook")
状态码可选:
http.StatusPermanentRedirect //永久性重定向
http.StatusTemporaryRedirect //临时性重定向
第二个参数为重定向的地址。
异步处理
// Async 异步处理,保存上传文件,开启相应数量的 goroutine
func Async(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest,fmt.Sprintf("get err %s",err.Error()))
}
//根据key:‘files’,获取表单中的文件切片
files := form.File["files"]
fileSum := len(files)
fileName := make([]string,fileSum)
//控制上传文件同步问题处理
waitG := sync.WaitGroup{}
waitG.Add(fileSum)
for index, file := range files {
fileName[index] = file.Filename
//异步处理时,上下文应该复制一个只读副本,不直接使用原始上下文
cc := c.Copy()
go func(c *gin.Context,fileName string,fileUpload *multipart.FileHeader) {
defer waitG.Done()
fmt.Println("正在保存",fileName)
cc.SaveUploadedFile(fileUpload,fileName)
}(cc,fileName[index],file)
}
waitG.Wait()
c.String(http.StatusOK,"%s",strings.Join(fileName,",
"))
}
初始写法(错误):
go func() {
defer waitG.Done()
fmt.Println("正在保存",fileName[index])
cc.SaveUploadedFile(file,fileName[index])
}()
}
错误原因总结:
未进行传参,导致只有最后一个文件被上传,通过debug
发现,虽然已经做同步处理(使用WaitGroup
),但是goroutine
内因为延迟求值的原因,导致每次都只是保存最后一个文件。传入参数fileName
、fileUpload
后,会立即复制参数,不会出现延迟求值的情况,使得保存文件按顺序进行保存。