Search code examples
goroutesmux

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")
    })
}

Solution

  • 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(),
        ),
    )