daryl
daryl

Reputation: 15237

http.ServeMux route mounting?

Let's take the following pattern:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    admin := http.NewServeMux()
    admin.HandleFunc("/", root)
    admin.HandleFunc("/foo", foo)
    http.Handle("/admin", admin)
    http.ListenAndServe(":4567", nil)
}

func root(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Admin: ROOT")
}

func foo(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Admin: FOO")
}

How is it that when I run /admin, it will fire the root handler, but when I run /admin/foo it won't? Just to be clear, I'm not looking for an alternative package, I actually have a custom router, I'm just generally curious to what's going on here as this pattern isn't making much sense to me.

Upvotes: 3

Views: 2535

Answers (2)

Simon Fox
Simon Fox

Reputation: 6425

Try the following:

func main() {
    admin := http.NewServeMux()

    // Prefix all paths with the mount point. A ServeMux matches
    // the full path, even when invoked from another ServeMux.
    mountPoint := "/admin"
    admin.HandleFunc(mountPoint, root)
    admin.HandleFunc(mountPoint + "/foo", foo)

    // Add a trailing "/" to the mount point to indicate a subtree match.
    http.Handle(mountPoint + "/", admin)

    http.ListenAndServe(":4567", nil)
}

Upvotes: 1

OneOfOne
OneOfOne

Reputation: 99421

Like @DewyBroto said, you have to use the full path in the child mux.

You could make a wrapper like this:

func NewChildMux(prefix string, vars ...interface{}) *http.ServeMux {
    sm := http.NewServeMux()
    for i := 0; i < len(vars); i += 2 {
        path := prefix + vars[i].(string)
        sm.HandleFunc(path, vars[i+1].(func(http.ResponseWriter, *http.Request)))
    }
    return sm
}

func main() {
    admin := NewChildMux("/admin",
        "/", root,
        "/foo/", foo,
    )
    http.Handle("/admin/", admin)
    http.ListenAndServe(":4567", nil)
}

Upvotes: 1

Related Questions