天涯海角异孤星
要将请求体绑定到结构体中,使用模型绑定。 Gin目前支持JSON、XML、YAML和标准表单值的绑定(foo=bar&boo=baz)。
Gin使用 go-playground/validator.v8 进行验证。 查看标签用法的全部文档.
使用时,需要在要绑定的所有字段上,设置相应的tag。 例如,使用 JSON 绑定时,设置字段标签为 json:"fieldname"
。
Gin提供了两类绑定方法:
Bind
, BindJSON
, BindXML
, BindQuery
, BindYAML
MustBindWith
的具体调用。 如果发生绑定错误,则请求终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)
。响应状态码被设置为 400 并且 Content-Type
被设置为 text/plain; charset=utf-8
。 如果您在此之后尝试设置响应状态码,Gin会输出日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422
。 如果您希望更好地控制绑定,考虑使用 ShouldBind
等效方法。ShouldBind
, ShouldBindJSON
, ShouldBindXML
, ShouldBindQuery
, ShouldBindYAML
ShouldBindWith
的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用 MustBindWith
或 ShouldBindWith
。
使用 c.Query
或 c.DefaultQuery
获取 URL 地址中的参数,代码:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
// 使用现有的基础请求对象解析查询字符串参数。
// 示例 URL: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的一种快捷方式
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
映射查询字符串或表单参数,类似于 PHP 的字符串下标数组参数。代码:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
fmt.Printf("ids: %v, names %v", ids, names)
})
router.Run(":8080")
}
用 curl 发送请求:
$ curl 'http://localhost:8080/post?ids[a]=1234&ids[b]=hello' -X POST -d 'names[first]=thinkerou&names[second]=tianou'
服务端控制台输出:
ids: map[a:1234 b:hello], names map[first:thinkerou second:tianou]
一般通过调用 c.Request.Body
方法绑定数据,但不能多次调用这个方法。
要想多次绑定,可以使用 c.ShouldBindBodyWith
.
c.ShouldBindBodyWith
会在绑定之前将 body 存储到上下文中。 这会对性能造成轻微影响,如果调用一次就能完成绑定的话,那就不要用这个方法。JSON
, XML
, MsgPack
, ProtoBuf
。 对于其他格式, 如 Query
, Form
, FormPost
, FormMultipart
可以多次调用 c.ShouldBind()
而不会造成任任何性能损失 (详见 #1341)。将访问日志写入到文件:
package main
import (
"io"
"os"
"github.com/gin-gonic/gin"
)
func main() {
// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
gin.DisableConsoleColor()
// 加入到文件
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// 如果需要同时将日志写入文件和控制台,请使用以下代码。
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
注意:如果服务重启,日志文件会被覆盖。
当在中间件或 handler 中启动新的 Goroutine 时,不能使用原始的上下文,必须使用只读副本。
package main
import (
"log"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// 创建在 goroutine 中使用的副本
cCp := c.Copy()
go func() {
// 用 time.Sleep() 模拟一个长任务。
time.Sleep(5 * time.Second)
// 请注意您使用的是复制的上下文 "cCp",这一点很重要
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c *gin.Context) {
// 用 time.Sleep() 模拟一个长任务。
time.Sleep(5 * time.Second)
// 因为没有使用 goroutine,不需要拷贝上下文
log.Println("Done! in path " + c.Request.URL.Path)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
url 参数除了通过 c.Query()
或 c.DefaultQuery()
获取值,还可以绑定到结构体。
c.ShouldBindQuery()
函数只绑定 url 查询参数而忽略 post 数据。
示例代码:
package main
import (
"log"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
}
func main() {
route := gin.Default()
route.Any("/testing", startPage)
route.Run(":8085")
}
func startPage(c *gin.Context) {
var person Person
if c.ShouldBindQuery(&person) == nil {
log.Println("------ Only Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
}
c.String(200, "Success")
}
Gin 框架支持的 HTTP 方法有:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS
func main() {
// 禁用控制台颜色
// gin.DisableConsoleColor()
// 使用默认中间件(logger 和 recovery 中间件)创建 gin 路由
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
// Any 表示此 url 接受任意 HTTP 方法
router.Any("/any", any)
// 默认在 8080 端口启动服务,除非定义了一个 PORT 的环境变量。
router.Run()
// router.Run(":3000") hardcode 端口号
}
微信小程序的订阅消息接口 wx.requestSubscribeMessage
,只能是用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。
除去支付回调这种情况,你必须给页面某个元素绑定 tap 事件,把这个 API 放到 tap 事件里,当用户去点击这个元素时才能调起订阅消息界面,且不能通过 setTimeout 延迟执行。
有一种特殊情况,wx.showModal
模态窗口的 success 回调可以调起订阅消息界面,举例如下:
wx.showModal({
showCancel: false,
content: '操作成功',
success: res => {
wx.requestSubscribeMessage({
tmplIds: [''],
success: res => { }
})
}
})