Skip to content

Router

基本路由

  • 基本路由 gin 框架中采用的路由库是 httprouter。
    // 创建带有默认中间件的路由:
    // 日志与恢复中间件
    router := gin.Default()
    //创建不带中间件的路由:
    //r := gin.New()
    
    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)
    

路由参数

  • gin的路由来自httprouter库。因此httprouter具有的功能,gin也具有,不过gin不支持路由正则表达式。

API参数

  • api 参数通过Context的Param方法来获取。(除了:,gin还提供了*号处理参数,*号能匹配的规则就更多。)
    router.GET("/user/:name/*behavior", func(context *gin.Context) {
        name := context.Param("name")
        behavior := context.Param("behavior")
        context.String(http.StatusOK, name+"to"+behavior)
    })
    
    jartin@macbookpro1 machine % curl localhost/user/姚明/篮球
    姚明to/篮球%  
    

URL参数

  • web提供的服务通常是client和server的交互。其中客户端向服务器发送请求,除了路由参数,其他的参数无非两种,查询字符串query string和报文体body参数。所谓query string,即路由用,用?以后连接的key1=value2&key2=value2的形式的参数。当然这个key-value是经过urlencode编码。
  • URL 参数通过 DefaultQueryQuery 方法获取。
  • 对于参数的处理,经常会出现参数不存在的情况,对于是否提供默认值,gin也考虑了,并且给出了一个优雅的方案,使用c.DefaultQuery方法读取参数,其中当参数不存在的时候,提供一个默认值。使用Query方法读取正常参数,当参数不存在的时候,返回空字串。
    router.GET("/dataindex", func(context *gin.Context) {
        name := context.DefaultQuery("name", "jartin")
        //name := c.Query("name") // 是 c.Request.URL.Query().Get("name") 的简写
        context.String(http.StatusOK, "your name is "+name)
    })
    
    jartin@macbookpro1 machine % curl localhost/dataindex                        
    your name is jartin%   
    
    jartin@macbookpro1 machine % curl localhost/dataindex?name=张三
    your name is 张三%   
    

表单参数

  • http的报文体传输数据就比query string稍微复杂一点,常见的格式就有四种。例如application/jsonapplication/x-www-form-urlencoded,application/xmlmultipart/form-data。后面一个主要用于图片上传。json格式的很好理解,urlencode其实也不难,无非就是把query string的内容,放到了body体里,同样也需要urlencode。默认情况下,c.PostFROM解析的是x-www-form-urlencodedfrom-data的参数。
  • 表单参数通过 PostForm 方法获取:
    router.POST("/form", func(context *gin.Context) {
        //type1 := context.DefaultPostForm("type1", "male") // 默认值方法
        uname := context.PostForm("uname")
        upwd := context.PostForm("upwd")
        //hobbys := context.PostFormMap("hobby")
        //hobbys := context.QueryArray("hobby")
        sex := context.PostFormArray("sex")
        context.String(http.StatusOK, fmt.Sprintf("uname is %s, upwd is %s, sex is %v", uname, upwd, sex))
    })
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <form action="http://localhost/form" method="post" enctype="application/x-www-form-urlencoded">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input type="text" name="uname"></td>
                </tr>
                <tr>
                    <td>密码</td>
                    <td><input type="password" name="upwd"></td>
                </tr>
                <tr>
                    <td>性别</td>
                    <td>
                        <input type="checkbox" value="男人" name="sex">男人
                        <input type="checkbox" value="女人" name="sex">女人
                    </td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="提交" /></td>
                </tr>
            </table>
        </form>
    </body>
    </html>
    
    uname is 张三, upwd is 112233, sex is [男人 女人]
    

文件上传

上传单个文件

  • 前面介绍了基本的发送数据,其中multipart/form-data转用于文件上传。gin文件上传也很方便,和原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中了。
    // 文件上传
    router.POST("/upload", func(context *gin.Context) {
        // 单文件上传
        file, _ := context.FormFile("file")
        log.Println(file.Filename)
        // 将文件上载
        context.SaveUploadedFile(file, file.Filename)
        /*
           也可以直接使用io操作,拷贝文件数据。
           out, err := os.Create(filename)
           defer out.Close()
           _, err = io.Copy(out, file)
        */
        context.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
    })
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <form action="http://localhost/upload" method="post" enctype="multipart/form-data">
            <table>
                <tr>
                    <td>文件</td>
                    <td><input type="file" name="file"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="提交" /></td>
                </tr>
            </table>
        </form>
    </body>
    </html>
    

上传多个文件

  • 所谓多个文件,无非就是多一次遍历文件,然后一次copy数据存储即可。
    router.POST("/uploads", func(context *gin.Context) {
        // 多文件上传
        form, err := context.MultipartForm()
        if err != nil {
            context.String(http.StatusBadRequest, fmt.Sprintf("upload err is %:", err.Error()))
            return
        }
    
        files := form.File["files"]
    
        for _, file := range files {
            if err := context.SaveUploadedFile(file, file.Filename); err != nil {
                context.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
                return
            }
        }
    
        context.String(http.StatusOK, fmt.Sprintf("upload succ %d files", len(files)))
    })
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
        <form action="http://localhost/uploads" method="post" enctype="multipart/form-data">
            <table>
                <tr>
                    <td>多文件</td>
                    <td><input type="file" name="files" multiple></td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="提交" /></td>
                </tr>
            </table>
        </form>
    </body>
    </html>
    
    • 与单个文件上传类似,只不过使用了c.Request.MultipartForm得到文件句柄,再获取文件数据,然后遍历读写。

Grouping routes

  • router group是为了方便一部分相同的URL的管理。
    package main
    
    import (
        "fmt"
        "github.com/gin-gonic/gin"
        "net/http"
    )
    
    func main() {
        router := gin.Default()
    
        // router group v1
        v1 := router.Group("/v1")
        {
            v1.GET("/login", loginEndpoint)
            v1.GET("/submit", submitEndpoint)
            v1.POST("/read", readEndpoint)
        }
    
        // router group v2
        v2 := router.Group("/v2")
        {
            v2.POST("/login", loginEndpoint)
            v2.POST("/submit", submitEndpoint)
            v2.POST("/read", readEndpoint)
        }
    
        router.Run(":80")
    }
    
    func loginEndpoint(context *gin.Context) {
        name := context.DefaultQuery("name", "Guest") // 默认值
        context.String(http.StatusOK, fmt.Sprintf("登录 %s", name))
    }
    
    func submitEndpoint(context *gin.Context) {
        name := context.DefaultQuery("name", "Guest") // 默认值
        context.String(http.StatusOK, fmt.Sprintf("提交 %s", name))
    }
    
    func readEndpoint(context *gin.Context) {
        name := context.DefaultQuery("name", "Guest") // 默认值
        context.String(http.StatusOK, fmt.Sprintf("读取 %s", name))
    }