How to preserve path params after handling a request with router.Any and wildcard

Issue

In my case need to catch request and check is internal request or not. If not, redirect these request to other handler function.

URL example:

1. URL: http://localhost/internal/dosomething1
2. URL: http://localhost/internal/dosomething2/:id
3. URL: http://localhost/overview
4. URL: http://localhost/xxx

Only the URIs that start with internal should be handled in my own handle function (cases 1 and 2).

Others with any request method will proxy to another function (cases 3 and 4).

I am trying to use router.Any("/*uri", handlerExternal) as such:

func handlerExternal(c *gin.Context) {
    path := c.Param("uri")
    if strings.HasPrefix(path, "/internal/") {

        uri := strings.Split(path, "/")
        switch uri[2] {
        case "dosomething1":
            doInternal(c)
        }
    } else {
        doExternal(c)
    }
}

But with this solution, the doInternal(c) cannot catch path parameters like :id as in http://localhost/internal/dosomething2/:id

Is there any better solution for this case?

Solution

Use a sub-engine.

You can instantiate an engine with gin.New for internal routes only and not run it. Instead you pass the context from your Any route to Engine.HandleContext.

This will relay the context from the main engine, and it will match path params based on the placeholders in the sub-routes.

You can declare routes on the sub-engine as usual:

func main() {
    internalEngine := gin.New()
    internalEngine.GET("/internal/dosomething1", func(c *gin.Context) { c.JSON(200, "ok") })
    internalEngine.GET("/internal/dosomething2/:id", func(c *gin.Context) { c.JSON(200, c.Param("id")) })

    mainEngine := gin.New()
    mainEngine.Any("/*uri", func(c *gin.Context) {
        path := c.Param("uri")
        if strings.HasPrefix(path, "/internal/") {
            uri := strings.Split(path, "/")
            switch uri[2] {
            case "dosomething1", "dosomething2":
                internalEngine.HandleContext(c)
            }
        } else {
            doExternal(c)
        }
    })
    mainEngine.Run(":8800")
}

You can also modify c.Request.URL.Path before relaying the context to the sub-engine, e.g. by removing the internal prefix, if so you wish.

Now if you try a GET request to:

http://localhost:8855/internal/dosomething2/1234

it will echo the output:

"1234"

Answered By – blackgreen

Answer Checked By – David Marino (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.