Liondancer
Liondancer

Reputation: 16469

gorilla/mux nested routes not matching

I'm not sure why my route isn't being matched. localhost:8000 matches and I get the expected output but NOT localhost:8000/admin/test. I am getting 404 when I try to match /admin/test on the browser.

I wanted to organize my code this way because I wanted to contain the routes and other subroutes within each package.

project structure

/admin
  admin.go
main.go

main.go

package main

import (
    "example.com/liondancer/playground/admin"
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there!")
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", handler)
    r.PathPrefix("/admin").Handler(admin.NewHandler())
    log.Fatal(http.ListenAndServe(":8000", r))
}

admin.go

package admin

import (
    "fmt"
    "net/http"
    "github.com/gorilla/mux"
)

type Handler struct {
    router *mux.Router
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.router.ServeHTTP(w, r)
}

func NewHandler() *Handler {
    h := &Handler{}
    r := mux.NewRouter()
    h.router = r
    h.addRoutes()
    return h
}

func (h *Handler) addRoutes() {
    fmt.Print("hi here"). // <--- printed here to prove func gets ran.
    h.router.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "test")
    })
}

Upvotes: 0

Views: 1115

Answers (2)

colm.anseo
colm.anseo

Reputation: 22037

Your nested handler never matches because you are not removing the URL prefix of the earlier handler. A quick fix to show what I mean:

// h.router.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {

// quick fix
h.router.HandleFunc("/admin/test", func(w http.ResponseWriter, r *http.Request) {

Obviously you want your nested handler to be unaware how it is nested, so when registering the sub-handler, strip the URL prefix, so your sub-handler will match:

// r.PathPrefix("/admin").Handler(admin.NewHandler())

// better
r.PathPrefix("/admin/").Handler(
    http.StripPrefix(
        "/admin",
        admin.NewHandler(),
    ),
)

Upvotes: 1

Illia
Illia

Reputation: 374

I was able to run this code using the Subrouter(). Here is link with a sample of Subrouter() usage Nesting subrouters in Gorilla Mux

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", handler)
    r.PathPrefix("/admin").Subrouter().HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "test")
    })
    log.Fatal(http.ListenAndServe(":8000", r))
}

If you want to separate it into another file you can also do it like this:

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", handler)
    subRoute := r.PathPrefix("/admin").Subrouter()
    NewHandler(subRoute)
    log.Fatal(http.ListenAndServe(":8000", r))
}

func NewHandler(r* mux.Router) *Handler {
    h := &Handler{}
    h.router = r
    h.AddRoutes()
    return h
}

func (h *Handler) AddRoutes() {
    fmt.Print("hi here") // <--- printed here to prove func gets ran.
        h.router.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "test")
    })
}

Upvotes: 1

Related Questions