Gin 路由和代理

admin
admin 4月16日
  • 在其它设备中阅读本文章

路由

Gin 路由遵循 Restful API 接口规范,是基于 httproute 实现的

基本路由

语法如下,httpMethod 为请求类型,relativePath 为路径,handlers 为该路由响应函数列表(多个中间件加一个执行函数)

router.<httpMethod>(relativePath string, handlers ...HandlerFunc)

支持 GET、POST、PUT、PATCH、DELETE、OPTIONS 等请求类型,Any 方法匹配所有类型,Match 方法匹配指定的多个类型

func main() {
    router := gin.Default()

    router.GET("/login", loginEndpoint)
    router.POST("/login", loginEndpoint)
    router.Any("/login", loginEndpoint)
    router.MatchMatch([]string{"POST","GET"}, "/login", loginEndpoint)

    router.Run(":8080")
}

路径规则如下

  1. "/path",指定路径,只能匹配/path
  2. "/:path",参数路由,可以匹配/a/b等,不能匹配/a/b
  3. "/*path",任意路由,可以匹配//a/a/b等所有路由

路由按指定路径优先进行匹配,参数路由和任意路由在同一级不可共存

func main() {
    router := gin.Default()

    // 匹配/user/john但不会匹配/user/或者/user
    router.GET("/user/:name", func(c *gin.Context) {
        name := c.Param("name")
        c.String(http.StatusOK, "Hello %s", name)
    })

    // 将匹配/user/john/和/user/john/send
    // 如果没有其他路由匹配 /user/john,它将重定向到 /user/john/
    router.GET("/user/:name/*action", func(c *gin.Context) {
        name := c.Param("name")
        action := c.Param("action")
        message := name + " is " + action
        c.String(http.StatusOK, message)
    })

    router.Run(":8080")
}

路由分组

func main() {
    router := gin.Default()

    // 简单的路由组: v1
    v1 := router.Group("/v1")
    {
        v1.POST("/login", loginEndpoint)
        v1.POST("/submit", submitEndpoint)
    }

    // 简单的路由组: v2
    v2 := router.Group("/v2")
    {
        v2.POST("/login", loginEndpoint)
        v2.POST("/submit", submitEndpoint)
    }

    router.Run(":8080")
}

路由中间件

Gin 框架是洋葱结构的,可以同时使用多个中间件,按注册顺序执行,中间件中使用c.Next()函数调用下一个中间件或路由响应函数,如果没有使用c.Next()则执行完此中间件将会返回

func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()

        // 请求前
        c.Next()
        // 请求后

        latency := time.Since(t)
        log.Print(latency)
    }
}

func main() {
    r := gin.New()
    r.Use(Logger())

    r.GET("/test", func(c *gin.Context) {

    })

    r.Run(":8080")
}

中间件可以给指定路由用,比如r.GET("/test", Logger, loginEndpoint)

代理

拼接真实服务器链接,使用 http 请求资源,再使用 DataFromReader 方法返回资源

const apiAddr = "https://example.com"

func Proxy(c *gin.Context) {
    url, err := url.Parse(apiAddr + c.Request.URL.Path)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "err": err.Error(),
        })
    }
    resp, err := http.DefaultClient.Do(&http.Request{
        URL:    url,
        Method: c.Request.Method,
        Body:   c.Request.Body,
        Header: c.Request.Header,
    })

    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{
            "err": err.Error(),
        })
    }
    defer resp.Body.Close()
    c.DataFromReader(resp.StatusCode, resp.ContentLength, resp.Header.Get("Content-Type"), resp.Body, nil)
}

使用方法,注意:使用 Any 匹配所有 HTTP 类型的请求,使用/*path匹配所有路径,可加入鉴权中间件限制代理的访问

router.Any("/:api/*path", Proxy)