When using the gin framework, if there are two such routes.

  • GET /users/:id
  • GET /users/info

gin will report an error: panic: 'info' in new path '/users/info' conflicts with existing wildcard ':id' in existing prefix '/users/:id'

The reason is that the two routes have the same HTTP method (meaning GET/POST/PUT/DELETE, etc.) and request path prefix, and in the same route location, the first route is a wildcard (meaning :id of this form) parameter, and the second route is a normal string info, then a route conflict occurs. What can I do to resolve the conflict?

1
2
3
4
5
6
7
8
router.GET("/users/:id", func(ctx *gin.Context) {
        switch c.Param("id") {
        case "info":
            handlers.getUserInfo(ctx)
        default:
            handlers.GetUser(ctx)
        }
    })

The idea is that if the :id wildcard result is info, then intercept it and go to the handler logic corresponding to /users/info; if the :id wildcard result is not info, then go to the handler logic corresponding to /users/:id. It’s not pretty, but it solves the problem.

A slightly more complex routing conflict might look like this.

  • GET /:channelID/:programID
  • GET /redirect/:channelID/:programID

:channelID conflicts with redirect, and the two routes have different levels, the first one has two levels, the second one has three levels, how to solve it?

First, transform the above route into the following.

  • GET /:path1/:path2
  • GET /:path1/:path2/:path3

The logic is clearer after the conversion.

  • If path1 = redirect, and path2 and path3 are not empty, then the second route is matched, then path2 and path3 are channelID and programID respectively
  • If path1 is not empty, and path1 ! = redirect, and path2 is not empty, and path3 is empty, then the first route is matched, and path1 and path2 are channelID and programID, respectively
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
router.GET("/:path1/:path2", getHandler)       
router.GET("/:path1/:path2/:path3", getHandler)

func getHandler(ctx *gin.Context) {
    if ctx.Param("path1") == "redirect" {
        if ctx.Param("path2") != "" && ctx.Param("path3") != "" {
            ctx.Params = append(ctx.Params, gin.Param{Key: "channelID", Value: ctx.Param("path2")},
                gin.Param{Key: "programID", Value: ctx.Param("path3")})

            handlers.GetRedirectResult(ctx)
        }
    } else if ctx.Param("path1") != "" && ctx.Param("path2") != "" && ctx.Param("path3") == "" {
        ctx.Params = append(ctx.Params, gin.Param{Key: "channelID", Value: ctx.Param("path1")},
            gin.Param{Key: "programID", Value: ctx.Param("path2")})

        handlers.GetResult(ctx)
    }
}

The above method is not elegant, when encountering route conflicts, you can consider whether the routing design is reasonable (such as whether to follow the RESTful API specification) to avoid route conflict problems as much as possible. Since this is a refactoring project, it is handled this way in order to keep the interface routes unchanged.