To everyone who's new here:

If you use RESTful-Style API, router of gin will be a TROUBLEMAKER!

205

1681

136

2005

The root of the problem is httprouter

Comment From: wodim

I wrote my own "router". Example:

router.GET("/:first/:second/:third", ViewRouterThree)

Then in ViewRouterThree you access c.Param("first"), second, or third to figure out which view you want to call and call it directly. Example:

func ViewRouterTwo(c *gin.Context) {
    if c.Param("second") == "new" {
        viewNewThreadForm(c)
        return
    } else if c.Param("first") == "recent" {
        if c.Param("second") == "threads" {
            viewRecentThreads(c)
            return
        } else if c.Param("second") == "posts" {
            viewRecentPosts(c)
            return
...

Crappy but it works fine. Of course having a decent router would be nice.

Comment From: TaceyWong

In my point, because Gin is based on httprouter (in pursuit of speed, a special algorithm is used.), there is no elegant solution that can maintain both speed and decent routing.

Comment From: jgoc

this is idiomatic, imo

/:second /:second/:id /:second/:id/:first /:second/:id/:first/:thisshouldbethefirstinstead

reduces parsing

Comment From: LaysDragon

yes,I encounter problem of conflict between path and wildcard ,and need to rewrite the openapi spec...It is annoying while gin have some limit with restful api style url path. That really unexpected while I use this have-most-star web server framework in go world And there is no any warning about those limitation while user expected a full function router. :(

Comment From: appleboy

related issue: https://github.com/julienschmidt/httprouter/issues/73

Comment From: vishnukvmd

@appleboy Are there plans to support alternatives to httprouter that do not have this limitation? Currently it is difficult to build REST-ful services using Gin.

Comment From: lawrsp

yes, the conflicts issue is annoy

my solutions are:

/path/static: RouterA
/path/:id: RouterB
/path/:id/values: RouterC 

write like this:


"/path/:id":  { if get(id) == "static" { RouterA} else { RouterB } }
"/path/:id/some":  RouterC

 "/path/some/static": RouterA
 "path/:id/values": RouterB

change the route as:

 "/path-some/static": RouterA
 "/path/:id/values": RouterB

the first noe is not bad,

I did't find any good solution for the second one

I hope there would be a support of alternatives

Comment From: vietvudanh

Well, I have never thought a simple REST could cause problems like this. Of course I can rewrite my router, but what the hell?

Comment From: skipsizemore

I would never have chosen Gin if I had known about this beforehand. Having to write my own router makes Gin worse than worthless. This needs to be configurable.

Comment From: ItalyPaleAle

I ended up here because I have a similar issue, which gave me the same error.

I took a quick look at the code and it looks like Gin is creating a tree in memory with all routes. Because of that, there can't be overlap.

A different approach that would solve both this issue and mine would be instead to keep all routes in a slice, and then iterate through them on each request. This way, the router would stop on the first match, and it would allow to have routes that partially overlap too.

For example:

router.GET("/:second/:id/:first/:thisshouldbethefirstinstead", handler)
router.GET("/:second/:id/:first", handler)
router.GET("/:second/:id", handler)
router.GET("/:second", handler)

If you navigate to /foo/bar, the router would first try to match it against /:second/:id/:first/:thisshouldbethefirstinstead, which will fail. It would then try /:second/:id/:first, and then finally find a match with /:second/:id. Because there's a match, it will stop processing the other requests. Of course, in this case order matters.

Now, I know that the current implementation, based on a tree, has a bit more performance, as it does less lookups on each route.

In practice, however, this won't matter much: most apps have a small number of routes, and the difference in time should be negligible. Additionally, because requests are matched in order, developers could put the most common routes at the top, thus making very few lookups for most requests.

There's significant prior art for doing routing this way:

  • Express: https://github.com/expressjs/express/blob/master/lib/router/index.js#L136
  • Hapi: https://hapi.dev/api/?v=20.0.0#path-matching-order
  • Even my own front-end router svelte-spa-router: https://github.com/ItalyPaleAle/svelte-spa-router/blob/master/Router.svelte#L445

Comment From: skipsizemore

I wrote a simple router based on this principle that has the same routing interface as the Gin engine (GET, POST, etc.). My router creates a generic, catch-all handler for each method, while adding the "actual" routes as regular expressions to a slice. When a request is received, Gin calls the generic catch-all handler, which in turn cycles through the slice of regexps to find the "real" handler. The generic handler also handles adding route parameters to the Gin Context, where they can be read just as if the Gin router had gone directly to the handler.

It sucks to have to do this, but it does work.

Comment From: bluven

I would never use gin for this reason.

Comment From: tiagoacardoso

This is a known issue and really need to be addressed, some kind of abstraction would be great.

Comment From: LaysDragon

https://github.com/gin-gonic/gin/issues/1432 here is another problem if you trying to use google pattern you will encountered. Httprouter can't escaped colons which is valid url character.

Comment From: rustagram

Guys please fix this! The library that has most stars should not have this kind of problems. My employers are considering firing me because of this bug, I really would appreciate you guys fixing this!

Comment From: dennypenta

totally sucks, never would like to use it

Comment From: leogtzr

Just hit the same problem everybody is having ... time to look to a different framework.

Comment From: irisida

I ended up here because I have a similar issue, which gave me the same error.

I took a quick look at the code and it looks like Gin is creating a tree in memory with all routes. Because of that, there can't be overlap.

A different approach that would solve both this issue and mine would be instead to keep all routes in a slice, and then iterate through them on each request. This way, the router would stop on the first match, and it would allow to have routes that partially overlap too.

For example:

go router.GET("/:second/:id/:first/:thisshouldbethefirstinstead", handler) router.GET("/:second/:id/:first", handler) router.GET("/:second/:id", handler) router.GET("/:second", handler)

If you navigate to /foo/bar, the router would first try to match it against /:second/:id/:first/:thisshouldbethefirstinstead, which will fail. It would then try /:second/:id/:first, and then finally find a match with /:second/:id. Because there's a match, it will stop processing the other requests. Of course, in this case order matters.

Now, I know that the current implementation, based on a tree, has a bit more performance, as it does less lookups on each route.

In practice, however, this won't matter much: most apps have a small number of routes, and the difference in time should be negligible. Additionally, because requests are matched in order, developers could put the most common routes at the top, thus making very few lookups for most requests.

There's significant prior art for doing routing this way:

  • Express: https://github.com/expressjs/express/blob/master/lib/router/index.js#L136
  • Hapi: https://hapi.dev/api/?v=20.0.0#path-matching-order
  • Even my own front-end router svelte-spa-router: https://github.com/ItalyPaleAle/svelte-spa-router/blob/master/Router.svelte#L445

Is there any intention to make something like this an opt-in configuration possibility if the 'raw speed' aspect of the original source of the issue is not to be resolved? I think it makes a strong case that for some notion of fully RESTful adherence as a perf trade-off that can be switched on is a way to make an inroad to the issue the users could live with.

Comment From: matheus-meneses

Hi guys, any updates about this issue?

Comment From: ridwanakf

Is this issue still persist now? just wondering. Still amazed that the most starred framework for its kind has this kinda problem.

Comment From: tiagoacardoso

yes, the issue is still an issue.

On Tue, 15 Dec 2020 at 07:00, Ridwan Afwan Karim Fauzi notifications@github.com wrote:

Is this issue still persist now? just wondering. Still amazed that the most starred framework for its kind has this kinda problem.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/gin-gonic/gin/issues/2016#issuecomment-745096450, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIDTGVHYCTHJZC7NLZD7AVLSU4CPFANCNFSM4IL5IANA .

-- [image: SWORD health] https://swordhealth.com/

Tiago Cardoso

Software Engineer | SWORD Health https://swordhealth.com/

+351 911 977 902 <+351912345678> [image: Skype] [image: LinkedIn] https://pt.linkedin.com/in/tiagocardosoweb

Disclaimer: The sender of this message cannot ensure the security of its electronic transmission and consequently does not accept liability for any fact which may interfere with the integrity of its content.

Comment From: micfok

Would love to see this issue resolved, but if it's not possible, or not on the roadmap it'd be a good idea to disclose this routing limitation prominently in the README. This format of nesting is standard in RESTful APIs and I will need to consider ripping Gin out of the repo because of this.

Comment From: p3ym4n

Been tracking this issue for a while, and it seems like the problem is not just changing the router, is the performance impact that can happen after that. which seems irrelevant when you cannot comply with the standards. ripping out the lovely Gin is much harder than fixing this issue, but the pain of not having a standard router is convincing me to choose the harder approach.

Comment From: ItalyPaleAle

I would argue that the performance impact is irrelevant regardless. Micro-optimizations in the router is likely not what will make or break your app's performance overall.

In the Node.Js space, Eran Hammer wrote this, which is definitely controversial but an interesting perspective: https://web.archive.org/web/20171017173141/https://medium.com/@eranhammer/when-500-faster-is-garbage-553121a088c3

Comment From: liov

Once gave up gin because of this problem, and later chose gin again, now I am considering giving up again.

Comment From: vuonghungvinh

Hi Guys, please suggest for me other libraries, first time learning Golang, but I see have problem with the router, this thing is not good.

Comment From: vietvudanh

Hi Guys, please suggest for me other libraries, first time learning Golang, but I see have problem with the router, this thing is not good.

Try echo

Comment From: allesan

Or Fiber.

Comment From: agengdp

yes, this issue still persist

Comment From: rw-access

@rustagram you still employed? hope i can help save your job. it's the year of the stimulus package https://github.com/julienschmidt/httprouter/pull/329

Comment From: rw-access

Update: I haven't heard from @julienschmidt on that PR, but I ported it here as #2663 and it's 🟢 in CI. I'm not a maintainer here, and I hope the current ones welcome this functionality. 🤞

Comment From: mqzabin

I discovered this issue right now, after porting 2k+ lines to gin. Can't say how lucky i am to see @rw-access PR seconds before considering moving out from this framework. Hope PR get merged.

Comment From: mqzabin

People, where did you migrate from gin? To which framework?

I was in the middle of rewriting from .net core, when I found this issue. .net has an excellent routing, also like laravel, django, rails.

Any suggestion for the alternative with proper routing (also, static assets embedding) ? Echo, fiber, chi? I've slightly benchmarked all top, echo is very stable under the massive load (the same as gin), fiber crashed.

I would migrate to Echo. But, to be honest, if you follow REST API routing good practices, you will rarely fall on any issue listed here. My app have 250+ endpoints and only 2 ran into these wildcard issues, so I just refactored them.

Comment From: matheus-meneses

Finally. Is there a date for the next release?

Comment From: appleboy

@matheus-meneses We will release the new version v1.7.0 this week

Comment From: matheus-meneses

@appleboy thanks

Comment From: appleboy

bump to v1.7.0 version. See https://github.com/gin-gonic/gin/releases/tag/v1.7.0

Comment From: jellyfisharuka

Is this issue still persist now? just wondering. Still amazed that the most starred framework for its kind has this kinda problem.

yes:(((

Comment From: matheus-meneses

It's already solved

Comment From: jellyfisharuka

It's already solved i am still facing with this problem: "panic: ':tenderId' in new path '/api/bids/:tenderId/list' conflicts with existing wildcard ':bidId' in existing prefix '/api/bids/:bidId'"