type Context struct {
Request *http.Request
Writer ResponseWriter
Params Params
// Keys is a key/value pair exclusively for the context of each request. 专门用于每个请求上下文的键值对
Keys map[string]interface{
}
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
// Errors是附加到使用此上下文的所有处理程序/中间件的错误列表。
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
// Accepted定义用于内容协商的手动接受格式列表。
Accepted []string
// contains filtered or unexported fields
}
type Params []Param
type Param struct {
Key string
Value string
}
Params is a Param-slice, as returned by the router. The slice is
ordered, the first URL parameter is also the first slice value. It is
therefore safe to read values by the index.
Params是键值对(Param)的数组切片,与Keys的键值对不同的是,它是由路由返回(决定)的。
它是有序的,URL中第一个参数存放在参数中的第一个位置。也就是说可以按照参数位置索引。
获取URL参数值
有两种方法回去参数值,
// Creates a gin router with default middleware:
// logger and recovery (crash-free) middleware
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)
// By default it serves on :8080 unless a
// PORT environment variable was defined.
router.Run()
// router.Run(":3000") for a hard coded port
这个就是给URL绑定各种HTTP方法的请求,总共有9种。
/:name
/*name
{
// This handler will match /user/john but will not match neither /user/ or /user
// :name会自动将这一段URL视为name参数,而且末尾有斜杆和没斜杆都可以识别
// 没有name字段,即/user/或者/user不会被匹配
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// However, this one will match /user/john/ and also /user/john/send
// If no other routers match /user/john, it will redirect to /user/john/
// 根据实际测试发现,使用星号而非冒号,获取参数的时候,会获取到前面的斜杆,而且,参数可以只有一个斜杆
// 即 /user/bob/ 匹配,action为 "/" 但是实际运行发现,如果将上面一段注释掉,则/user/bob 不匹配,404;
// 如果不注释掉,user/bob匹配的是上面的,不匹配下面的。
// 像 /user/bob/send 匹配, action 为"/send"
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action") // /user/john/send action解析出来是"/send"
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.Run(":8080")
}
// 查询参数(Querying parameters)
// Query string parameters are parsed using the existing underlying request object.
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest") // 如果没有使用第二个参数指定的默认值
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
DefaultQuery returns the keyed url query value if it exists, otherwise
it returns the specified defaultValue string. See: Query() and
GetQuery() for further information.
如果存在查询参数键,获取查询参数值;如果不存在,返回默认值
// GET /?name=Manu&lastname=
c.DefaultQuery("name", "unknown") == "Manu" //正常的有查询参数键值对
c.DefaultQuery("id", "none") == "none" // 不存在这个查询参数键
c.DefaultQuery("lastname", "none") == "" // 有这个查询参数键,虽然值为空,但是依旧是存在的。
Query returns the keyed url query value if it exists, otherwise it
returns an empty string("")
. It is shortcut for
c.Request.URL.Query().Get(key)
GET /path?id=1234&name=Manu&value=
c.Query("id") == "1234"
c.Query("name") == "Manu"
c.Query("value") == ""
c.Query("wtf") == ""
注意:有这个查询键,但是值是空字符串(value=)和没有这个查询键(“wtf”),返回值都是空字符串。
如果需要区分,应当使用c.GetQuery(string).它会多返回一个flag,表示是否存在key.
func (*Context) GetQuery
func (c *Context) GetQuery(key string) (string, bool) GetQuery is like
Query(), it returns the keyed url query value if it exists(value, true)
(even when the value is an empty string), otherwise it returns
("", false)
. It is shortcut forc.Request.URL.Query().Get(key)
GET
// 以这个url为例:/?name=Manu&lastname=
("Manu", true) == c.GetQuery("name")
("", false) == c.GetQuery("id")
("", true) == c.GetQuery("lastname")
网络基础知识
multipart/form-data是POST请求的Content-Type的MIME类型。POST的默认是application/x-www-form-urlencoded
使用默认的 application/x-www-form-urlencoded 作为 content type 的简单表单示例:
POST / HTTP/1.1
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
say=Hi&to=Mom
使用 multipart/form-data 作为 content type 的表单:
POST /test HTTP/1.1
Host: foo.example
Content-Type: multipart/form-data;boundary="boundary"
--boundary
Content-Disposition: form-data; name="field1"
value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"
value2
--boundary--
func main() {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
example
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
func (*Context) PostForm
func (c *Context) PostForm(key string) string PostForm returns the
specified key from a POST urlencoded form or multipart form when it
exists, otherwise it returns an empty string("")
.
无论是multipart还是url编码的表单,PostForm都会根据key返回值,如果不存在,返回零值。
func (*Context) DefaultPostForm
func (c *Context) DefaultPostForm(key, defaultValue string) string
DefaultPostForm returns the specified key from a POST urlencoded form
or multipart form when it exists, otherwise it returns the specified
defaultValue string. See: PostForm() and GetPostForm() for further
information.
func (*Context) PostFormArray
func (c *Context) PostFormArray(key string) []string PostFormArray
returns a slice of strings for a given form key. The length of the
slice depends on the number of params with the given key.
这个是根据key,返回一个字符串切片,因为这个key可以重复出现,每次指定的值不一样,最终导致值可能有多个,所以有这个函数。上面的只返回一个值的函数其实返回的都是找到的第一个。
URL的参数和POST form表单可以结合起来,制造更加丰富的场景。
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// single file
file, _ := c.FormFile("file") // file是form表单的key
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/multiupload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm() // 多个上传的文件。
files := form.File["upload[]"] // upload[]同样是表单的key
for _, file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}
使用相同的路径路径前缀可以将他们归成一组,更加的合理。同时可以更加方便的让它们使用相同的中间件。
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
v2..Use(AuthRequired()) // v2的group都使用中间件。
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
这个比较简单,没有什么额外的可说。
// Blank gin
r := gin.New()
// Default With the Logger and Recovery middleware already attached
r := gin.Default()
就是默认的相比空白的多了日志和恢复的中间件。
不做额外设置,日志是输出到标准输出的。可以按照如下设置将日志输出到文件中。
func main() {
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// Use the following code if you need to write the logs to file and console at the same time.
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default() // 注意,是先设置DefaultWriter再gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run(":8080")
}
参考文献: