请判断下面的代码片段.我使用ReverseProxy重定向到给定的版本.您需要仔细验证给定的版本.否则,它将导致递归调用.
注意:我使用了/user
GET的两个版本(/v1/user
个和/v2/user
个).
示例代码
package main
import (
"net/http"
"net/http/httputil"
"regexp"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.Use(VersionMiddleware())
v1 := router.Group("/v1")
v1.GET("/user", func(c *gin.Context) {
c.String(http.StatusOK, "This is the v1 API")
})
v2 := router.Group("/v2")
v2.GET("/user", func(c *gin.Context) {
c.String(http.StatusOK, "This is the v2 API")
})
router.Run(":8082")
}
func VersionMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// You need to check c.Request.URL.Path whether
// already have a version or not, If it has a valid
// version, return.
regEx, _ := regexp.Compile("/v[0-9]+")
ver := regEx.MatchString(c.Request.URL.Path)
if ver {
return
}
version := c.Request.Header.Get("Accept-version")
// You need to validate given version by the user here.
// If version is not a valid version, return error
// mentioning that given version is invalid.
director := func(req *http.Request) {
r := c.Request
req.URL.Scheme = "http"
req.URL.Host = r.Host
req.URL.Path = "/"+ version + r.URL.Path
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(c.Writer, c.Request)
}
}
或
您可以使用下面的包装器实现GIN.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/udayangaac/stackoverflow/golang/75860989/ginwrapper"
)
func main() {
engine := gin.Default()
router := ginwrapper.NewRouter(engine)
defaultRouter := router.Default()
defaultRouter.Get("/profile",func(ctx *gin.Context) {
})
v1 := router.WithVersion("/v1")
v1.Get("/user",func(ctx *gin.Context) {
ctx.String(http.StatusOK, "This is the profile v1 API")
})
v2 := router.WithVersion("/v2")
v2.Get("/user",func(ctx *gin.Context) {
ctx.String(http.StatusOK, "This is the profile v2 API")
})
engine.Run(":8082")
}
package ginwrapper
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Router struct {
router *gin.Engine
versionGroups map[string]*gin.RouterGroup
}
type VersionedRouter struct {
version string
Router
}
func NewRouter(router *gin.Engine) *Router {
return &Router{
router: router,
versionGroups: make(map[string]*gin.RouterGroup),
}
}
func (a *Router) Default() VersionedRouter {
return VersionedRouter{Router: *a }
}
func (a *Router) WithVersion(version string) VersionedRouter {
if _,ok := a.versionGroups[version]; ok {
panic("cannot initialize same version multiple times")
}
a.versionGroups[version] = a.router.Group(version)
return VersionedRouter{Router: *a,version:version }
}
func (vr VersionedRouter) Get(relativePath string, handlers ...gin.HandlerFunc) {
vr.handle(http.MethodGet,relativePath,handlers...)
}
// Note: You need to follow the same for other HTTP Methods.
// As an example, we can write a method for Post HTTP Method as below,
//
// func (vr VersionedRouter) Post(relativePath string, handlers ...gin.HandlerFunc) {
// vr.handle(http.MethodPost,relativePath,handlers...)
// }
func (vr VersionedRouter)handle(method,relativePath string, handlers ...gin.HandlerFunc) {
if !vr.isRouteExist(method,relativePath) {
vr.router.Handle(method,relativePath,func(ctx *gin.Context) {
version := ctx.Request.Header.Get("Accept-version")
if len(version) == 0 {
ctx.String(http.StatusBadRequest,"Accept-version header is empty")
}
ctx.Request.URL.Path = fmt.Sprintf("/%s%s", version, ctx.Request.URL.Path)
vr.router.HandleContext(ctx)
})
}
versionedRelativePath := vr.version + relativePath
if !vr.isRouteExist(method,versionedRelativePath) {
vr.router.Handle(method,versionedRelativePath,handlers... )
}
}
func (a VersionedRouter) isRouteExist(method,relativePath string) bool {
for _,route := range a.router.Routes() {
if route.Method == method && relativePath == route.Path {
return true
}
}
return false
}
样例请求
curl --location 'localhost:8082/user' \
--header 'Accept-version: v1'
curl --location 'localhost:8082/user' \
--header 'Accept-version: v2'