Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seeing "panic: wildcard route conflicts with existing children" unexpectedly #12

Closed
dropwhile opened this issue Jun 14, 2014 · 11 comments
Closed

Comments

@dropwhile
Copy link

When trying to add simple routes that should not overlap, I get wildcard route conflicts.

Simple crafted example:

package main

import (
    "fmt"
    "net/http"
    "github.com/julienschmidt/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Welcome!\n")
}

func main() {
    router := httprouter.New()
    router.Handler("HEAD", "/:some/:thing", http.HandlerFunc(Index))
    router.Handler("GET", "/:some/:thing", http.HandlerFunc(Index))
    router.Handler("GET", "/otherthing", http.HandlerFunc(Index))
    http.Handle("/", router)
}
$ go run test.go

goroutine 1 [running]:
runtime.panic(0x1afa20, 0xc21000a600)
    /go/1.2.2/libexec/src/pkg/runtime/panic.c:266 +0xb6
github.com/julienschmidt/httprouter.(*node).insertChild(0xc2100390c0, 0xc210000102, 0x254591, 0xc, 0xc21000a5f0)
    /build/src/github.com/julienschmidt/httprouter/tree.go:201 +0x148
github.com/julienschmidt/httprouter.(*node).addRoute(0xc2100390c0, 0x254591, 0xc, 0xc21000a5f0)
    /build/src/github.com/julienschmidt/httprouter/tree.go:172 +0x8d5
github.com/julienschmidt/httprouter.(*Router).Handle(0xc2100484a0, 0x247fe0, 0x3, 0x254590, 0xd, ...)
    /build/src/github.com/julienschmidt/httprouter/router.go:205 +0x15a
github.com/julienschmidt/httprouter.(*Router).Handler(0xc2100484a0, 0x247fe0, 0x3, 0x254590, 0xd, ...)
    /build/src/github.com/julienschmidt/httprouter/router.go:215 +0x9f
main.main()
    /some/user/path/test.go:18 +0x16d
exit status 2

In the sample code the routes that appear to conflict are /:some/:thing and /otherthing. These should not really be conflicting though, as one requires two path components, and the other is a fixed route with no wildcards. Kind of odd. Is this a bug or intended behavior?

@julienschmidt
Copy link
Owner

Intended.
The router matches prefixes, not the complete route at once. /:some and /otherthing conflict obviously. It doesn't matter that no handler is registered for /:some only.

The intention is, that there is only one or none possible route for any prefix when matching incoming requests. But if someone requests /otherthing both the variable-length parameter /:some and the static path /otherthing would match.

@codepushr
Copy link

I have the same issue. Take the following example:

a.mux.POST("users/:id/verify", user.Verify)
a.mux.POST("users/activated", user.GetActivatedUsers)

The first verifies one special user while the second returns a collection of activated users based on the request payload.

This is possible with many modern routers so the decision really surprised me.

@nucleartide
Copy link

I have the same problem as @CodingRogue. Surprisingly, we're both doing something user-related..

users.GET("/new")
users.GET("/:id/edit")

Something like /users/edit/:id instead of /users/:id/edit would work, but I'd really prefer /users/:id/edit.

@codepushr
Copy link

I'm afraid this will happen quite often in more complex rest apis. Is there any solution or workaround except switching routers?

@nucleartide
Copy link

You could probably embed httprouter in a new struct and refine httprouter's methods to support the routes above. But I ended up just replacing httprouter with gorilla/pat. gorilla/pat also has regex matching so you can match numbers.

@codepushr
Copy link

I will check out pat then, thanks.

@emilgpa
Copy link

emilgpa commented Aug 27, 2015

the routes /users/:id/verify and /users/activated collide. I guess that you have a route like /users/:id to view the profile of user but, what happen if a username is named "activated"? goes to route /users/activated or /users/:id (:id would be "activated")?

httprouter encourages the good route design.

For fix your routes, should look like:

a.mux.POST("user/:id", user.Profile)
a.mux.POST("user/:id/verify", user.Verify)
a.mux.POST("users", user.GetAll)
a.mux.POST("users/activated", user.GetActivatedUsers)

@codepushr
Copy link

I can see your point but this route design is not REST compatible. I need to have the same resource identifier reachable through all necessary http verbs (GET, POST, PUT, PATCH, DELETE...).

@emilgpa
Copy link

emilgpa commented Aug 28, 2015

But each http verb must do things differently from the rest...

@bradrydzewski
Copy link

I hit the same issue. I think this is a pretty common use case, and something I would love to see gin accommodate. You can see similar patterns in GitHub, Twitter, Facebook, etc:

github.com/
github.com/issues
github.com/:user
github.com/:user/:repo

twitter.com/mentions
twitter.com/i/notifications
twitter.com/:username

www.facebook.com/events/upcoming
www.facebook.com/messages/
www.facebook.com/:username

@julienschmidt
Copy link
Owner

#73

Repository owner locked and limited conversation to collaborators Sep 17, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants