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

Routes Hierarchy #957

Closed
utiq opened this issue Jun 22, 2017 · 7 comments · Fixed by #2663
Closed

Routes Hierarchy #957

utiq opened this issue Jun 22, 2017 · 7 comments · Fixed by #2663
Labels

Comments

@utiq
Copy link

utiq commented Jun 22, 2017

I have this routes:

r.GET("/drivers/price", ctrl.GetDriversPrice)
r.GET("/drivers/:id", ctrl.GetDriver)

So when I call http://.../drivers/price, I'm routed to ctrl.GetDriver. How can I make to get routed to ctrl.GetDriversPrice?

@easonlin404
Copy link
Contributor

Gin will panic because wildcard route conflicts with existing route, maybe your code is not like the snippet . Could you post your example code?

2017-06-24 12 00 28

@utiq
Copy link
Author

utiq commented Jun 23, 2017

Yes, It panics. That's my question about. For example in Ruby On Rails, if those routes are defined

r.GET("/drivers/price", ctrl.GetDriversPrice)
r.GET("/drivers/:id", ctrl.GetDriver)

if you go to http://.../drivers/price the router will take it as a route and it wont take /price as a wildcard :id

@nazwa
Copy link

nazwa commented Jun 23, 2017

Gin requires routes to be unambiguous. It's one of the ups and downs of a non regexp based routing.

@EtienneR
Copy link

EtienneR commented Jul 3, 2017

Not the best solution but you can use the strings.EqualFold function from "strings" package with c.Request.RequestURI function for checking the URI.

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"strings"
)

func main() {
	r := gin.Default()

	r.GET("/drivers/:id", GetDriverHandler)

	r.Run(":3000")
}

func GetDriverHandler(c *gin.Context) {
	//fmt.Println(c.Request.RequestURI)
	if strings.EqualFold(c.Request.RequestURI, "/drivers/price") {
		fmt.Fprintf(c.Writer, "GetDriversPrice")
	} else {
		id := c.Param("id")
		fmt.Fprintf(c.Writer, "GetDriversId "+id)
	}
}

@javierprovecho
Copy link
Member

or something like

	// main.go

	r.GET("/drivers/:id", ctrl.GetDriver)

	...

	// ctrl/handlers.go

	func GetDriver(c *gin.Context) {
		if c.Request.RequestURI == "/drivers/price" {
			GetDriversPrice(c)
			return
		}

		...

	}

	...

@ferluk
Copy link

ferluk commented Jun 10, 2018

how to use middleware in your solution ?

example:

r.GET("/drivers/price", ctrl.GetDriversPrice)
r.GET("/drivers/:id", ctrl.AuthRequired, ctrl.GetDriver)

@leefernandes
Copy link

leefernandes commented Apr 4, 2019

@ferluk it feels like a bummer if you're accustomed to leveraging the order of declared paths as it's implemented in other routing libs, but with gin you can consider flipping the conflicting route.

r.GET("/price/drivers", ctrl.GetDriversPrice)
r.GET("/drivers/:id", ctrl.AuthRequired, ctrl.GetDriver)

Unfortunately it sometimes means that routes which feel like they should hang off the same Group sharing a set of common middleware can't, and our middleware usage can become repetitive like below where ctrl.AuthRequired is repeated twice (unless moveable to a higher group)...

priceRoutes := r.Group("/price").Use(ctrl.AuthRequired)
{
  priceRoutes.GET("/drivers", ctrl.GetDriversPrice)
}

driverRoutes := r.Group("/drivers").Use(ctrl.AuthRequired)
{
  // cannot hang export off of driveRoutes due to conflict with /:id
  // driverRoutes.GET("/price", ctrl.GetDriversPrice)
  driverRoutes.GET("/:id", ctrl.GetDriver)
}

However I still think this ^ is likely better than nesting routing logic for pricing in another handler, ideally route definitions can inform of all url routing rather than overloading handlers with route logic which has been suggested by others.

Another option is to increase the specificity of your drivers/:id endpoint...

r.GET("/drivers/price", ctrl.GetDriversPrice)
r.GET("/drivers/id/:id", ctrl.AuthRequired, ctrl.GetDriver)

That ^ may feel less congenial to the typical restful pattern of resources/:resourceID but at least grouping middleware is a bit cleaner...

v1 := r.Group("/v1").Use(ctrl.AuthRequired)

driverRoutes := v1.Group("/drivers").Use(ctrl.DriverMiddleware)
{
  driverRoutes.GET("/price", ctrl.GetDriversPrice)
  driverRoutes.GET("/id/:id", ctrl.GetDriver)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
7 participants