Fiber: Problem with Route path

Created on 16 Jul 2020  路  15Comments  路  Source: gofiber/fiber

Fiber version
1.12.6

Issue description
Nested Route paths not invoking the correct handler.

Code snippet

func SetupRoutes(app *fiber.App) {
    todo := app.Group("/todo", middleware.Logger())

    todo.Get("/:id", handler.ReadTodo) 
    todo.Post("/", handler.CreateTodo)
    todo.Put("/:id", handler.UpdateTodo)
    todo.Delete("/:id", handler.DeleteTodo)
    todo.Get("/all/todos", handler.ReadAllTodo) 
    todo.Get("/all", handler.DummyAllHandler)  // this does not working, it always invoking todo.Get("/:id", handler.ReadTodo) 
}

Sample Code Repo

馃 Question

Most helpful comment

option := a.Group("/option")
option.Get("/:id", getById)
option.Get("/all", getAll)

But why it is not working in the above case.
I am new to golang, I choose fiber coz it gives express(nodejs) like experience.
The above case is still a bug, right?
Is there any plan for a fix, or at least it needs to mention somewhere in the documentation

The presented case above would have the same results in Expressjs, /:id also matches /all.
@kiyonlin explained this perfectly, just like in Express: order matters :+1:

All 15 comments

It is probably taking your path "all" as the id parameter. Being in the same method I assume that you must be taking it this way id = "all". Try putting using StrictRouting.

app := fiber.New(&fiber.Settings{ StrictRouting: true, })

Thanks for your reporting! But I don't it is counted as a bug.

According to your example, there are two approaches to solve the problem:

package main

import (
    "strconv"

    "github.com/gofiber/fiber"
)

func main() {
    app := fiber.New()

    // get /option1/all => "all"
    app.Get("/option1/all", func(c *fiber.Ctx) { c.SendString("all") })
    // get /option1/1   => "1"
    app.Get("/option1/:id", func(c *fiber.Ctx) { c.Send(c.Params("id")) })

    // get /option2/1   => "1"
    app.Get("/option2/:id", func(c *fiber.Ctx) {
        // assume id is integer
        if id, err := strconv.Atoi(c.Params("id")); err != nil {
            c.Next()
        } else {
            c.Send(id)
        }
    })
    // get /option2/all => "all"
    app.Get("/option2/all", func(c *fiber.Ctx) { c.SendString("all") })

    app.Listen(3000)
}

You can place routes in the appropriate order so that Fiber can do the right thing :smile:

@solrac97gr

It is probably taking your path "all" as the id parameter. Being in the same method I assume that you must be taking it this way id = "all". Try putting using StrictRouting.

app := fiber.New(&fiber.Settings{ StrictRouting: true, })

app := fiber.New(&fiber.Settings{
        StrictRouting: true,
})

setting StrictRouting: true does not fix the problem still invoking todo.Get("/:id", handler.ReadTodo)

@mohanasundaramn follow the @kiyonlin solution. That will fix that.

Thanks for your reporting! But I don't it is counted as a bug.

According to your example, there are two approaches to solve the problem:

package main

import (
  "strconv"

  "github.com/gofiber/fiber"
)

func main() {
  app := fiber.New()

  // get /option1/all => "all"
  app.Get("/option1/all", func(c *fiber.Ctx) { c.SendString("all") })
  // get /option1/1   => "1"
  app.Get("/option1/:id", func(c *fiber.Ctx) { c.Send(c.Params("id")) })

  // get /option2/1   => "1"
  app.Get("/option2/:id", func(c *fiber.Ctx) {
      // assume id is integer
      if id, err := strconv.Atoi(c.Params("id")); err != nil {
          c.Next()
      } else {
          c.Send(id)
      }
  })
  // get /option2/all => "all"
  app.Get("/option2/all", func(c *fiber.Ctx) { c.SendString("all") })

  app.Listen(3000)
}

You can place routes in the appropriate order so that Fiber can do the right thing 馃槃

// get /option1/all => "all"
app.Get("/option1/all", func(c *fiber.Ctx) { c.SendString("all") })

Seems this is not working

package main

import (
    "strconv"

    "github.com/gofiber/fiber"
)

func getAll(c *fiber.Ctx) { c.SendString("option all") }

func getById(c *fiber.Ctx) { c.Send(c.Params("id")) }

func setupRoutes(a *fiber.App) {
    option := a.Group("/option")
    option.Get("/:id", getById)
    option.Get("/all", getAll)

    option2 := a.Group("/options2")
    option2.Get("/all", func(c *fiber.Ctx) { c.SendString("option2 all") })
    option2.Get("/:id", func(c *fiber.Ctx) {
        // assume id is integer
        if id, err := strconv.Atoi(c.Params("id")); err != nil {
            c.Next()
        } else {
            c.Send(id)
        }
    })
}

func main() {
    app := fiber.New()

    setupRoutes(app)

    app.Listen(3000)
}

But if I change the order of option/all option/:id it works. why is that?

package main

import (
    "strconv"

    "github.com/gofiber/fiber"
)

func getAll(c *fiber.Ctx) { c.SendString("option all") }

func getById(c *fiber.Ctx) { c.Send(c.Params("id")) }

func setupRoutes(a *fiber.App) {
    option := a.Group("/option")
    option.Get("/all", getAll)
    option.Get("/:id", getById)

    option2 := a.Group("/options2")
    option2.Get("/all", func(c *fiber.Ctx) { c.SendString("option2 all") })
    option2.Get("/:id", func(c *fiber.Ctx) {
        // assume id is integer
        if id, err := strconv.Atoi(c.Params("id")); err != nil {
            c.Next()
        } else {
            c.Send(id)
        }
    })
}

func main() {
    app := fiber.New()

    setupRoutes(app)

    app.Listen(3000)
}

Hi @mohanasundaramn

Seems this is not working

option := a.Group("/option")
option.Get("/:id", getById)
option.Get("/all", getAll)

When you put /:id above /all, you should do the Next thing. But your first example just do the opposite.

Btw @mohanasundaramn

But if I change the order of option/all option/:id it works. why is that?

That is because Fiber always finds the first route to match the request's path and breaks the route chain if there is no more Next is called.

@solrac97gr @kiyonlin Can you please try both codes, I still believe this is a bug

@solrac97gr @kiyonlin Can you please try both codes, I still believe this is a bug

I adjusted your code:

package main

import (
    "strconv"

    "github.com/gofiber/fiber"
)

func getAll(c *fiber.Ctx) { c.SendString("option all") }

func getById(c *fiber.Ctx) { c.Send(c.Params("id")) }

func setupRoutes(a *fiber.App) {
    option := a.Group("/option")
    // 馃憢馃憢馃憢 get /options/all => option all
    option.Get("/all", getAll)
    // 馃憢馃憢馃憢 get /options/1 => 1
    option.Get("/:id", getById)

    option2 := a.Group("/option2")
    // 馃憢馃憢馃憢 get /option2/2 => 2
    option2.Get("/:id", func(c *fiber.Ctx) {
        // assume id is integer
        if id, err := strconv.Atoi(c.Params("id")); err != nil {
            c.Next()
        } else {
            c.Send(id)
        }
    })
    // 馃憢馃憢馃憢 get /option2/all => option2 all
    option2.Get("/all", func(c *fiber.Ctx) { c.SendString("option2 all") })
}

func main() {
    app := fiber.New()

    setupRoutes(app)

    app.Listen(3000)
}

@mohanasundaramn Problem sovled?

Feel free to close this issue if you don't have further questions 馃憤

option := a.Group("/option")
option.Get("/:id", getById)
option.Get("/all", getAll)

But why it is not working in the above case.
I am new to golang, I choose fiber coz it gives express(nodejs) like experience.
The above case is still a bug, right?
Is there any plan for a fix, or at least it needs to mention somewhere in the documentation

Can I use Fiber for the production application?

option := a.Group("/option")
option.Get("/:id", getById)
option.Get("/all", getAll)

But why it is not working in the above case.
I am new to golang, I choose fiber coz it gives express(nodejs) like experience.
The above case is still a bug, right?
Is there any plan for a fix, or at least it needs to mention somewhere in the documentation

Nope, as I said above.

That is because Fiber always finds the first route to match the request's path and breaks the route chain if there is no more Next is called.

Can I use Fiber for the production application?

Definately sure.

@kiyonlin Thank you for your time.

option := a.Group("/option")
option.Get("/:id", getById)
option.Get("/all", getAll)

But why it is not working in the above case.
I am new to golang, I choose fiber coz it gives express(nodejs) like experience.
The above case is still a bug, right?
Is there any plan for a fix, or at least it needs to mention somewhere in the documentation

The presented case above would have the same results in Expressjs, /:id also matches /all.
@kiyonlin explained this perfectly, just like in Express: order matters :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lucasmdomingues picture lucasmdomingues  路  3Comments

jeyraj picture jeyraj  路  4Comments

Aguezz picture Aguezz  路  4Comments

Badrouu17 picture Badrouu17  路  4Comments

mhf-ir picture mhf-ir  路  3Comments