gin基础知识

1. gin.Context

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
}

Params

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参数值
有两种方法回去参数值,

  1. ctx.Params.ByName(name string) 这个可以简写为ctx.Param(name)
  2. ctx.Params.Get(name string),注意这个不能简写为ctx.Get(name).ctx.Get(string)获取的是ctx.Keys里面的键值对的值。
    两个方法差不多,都是在Params的数组切片中找到第一个key与name匹配的键值对的value。如果没找到话就返回一个空字符串。差异是Get多返回一个bool参数,表示是否成功找到。

2. 使用GET, POST, PUT, PATCH, DELETE and OPTIONS

    // 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种。

3. URL带路由参数(Parameters in path)

  1. 形式1 冒号+参数名/:name
  2. 形式2 星号+参数名/*name
    获取参数值:ctx.Param(参数名的字符串) 是上面的ctx.Params.ByName(name string)的简写。
{
         
    // 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")
}

4. URL中的查询参数(Querystring parameters)

// 查询参数(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 for c.Request.URL.Query().Get(key) GET

// 以这个url为例:/?name=Manu&lastname=

("Manu", true) == c.GetQuery("name")
("", false) == c.GetQuery("id")
("", true) == c.GetQuery("lastname")

5. Multipart/Urlencoded Form

网络基础知识
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,
   })
})

gin doc

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可以重复出现,每次指定的值不一样,最终导致值可能有多个,所以有这个函数。上面的只返回一个值的函数其实返回的都是找到的第一个。

postman发送方法

gin基础知识_第1张图片
URL的参数和POST form表单可以结合起来,制造更加丰富的场景。

6. 文件上传Upload files

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")
}

7. URL分组

使用相同的路径路径前缀可以将他们归成一组,更加的合理。同时可以更加方便的让它们使用相同的中间件。

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")
}

这个比较简单,没有什么额外的可说。

8. Blank Gin without middleware by default

// Blank gin
r := gin.New() 
// Default With the Logger and Recovery middleware already attached
r := gin.Default()

就是默认的相比空白的多了日志和恢复的中间件。

9. 将日志写到文件中

不做额外设置,日志是输出到标准输出的。可以按照如下设置将日志输出到文件中。

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")
}

参考文献:

  1. https://chenyitian.gitbooks.io/gin-web-framework/content/docs/17.html
  2. https://godoc.org/github.com/gin-gonic/gin
  3. https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/POST

你可能感兴趣的:(golang,web框架,golang,gin框架)