microo8
microo8

Reputation: 3794

go http 1.22 subrouter

So I want to get rid of one more dependency (gorilla/mux) and use the std http.ServeMux with it's new routing patterns.

The problem is, that gorilla/mux had this concept of a subrouter, which could handle everything under a specified path.

I needed to serve paths like:

GET /api/user/id
POST /api/user/id
GET /api/product/id

So I had something like:

apirouter := mainRouter.PathPrefix("/api").Subrouter()
users.RegisterHandler(apirouter)
products.RegisterHandler(apirouter)

and then in eg. users package:

func RegisterHandler(router *mux.Router) {
    subrouter := router.PathPrefix("/user").Subrouter()
    subrouter.HandleFunc("/{id}", getUser).Methods(http.MethodGet)
    subrouter.HandleFunc("/{id}", postUser).Methods(http.MethodPost)
    subrouter.HandleFunc("/{id}", updateUser).Methods(http.MethodPut)
}

How could this be achieved with http.ServeMux? I want to be able to create a subrouter, pass it around and register new routes under it.

Something like this?

func RegisterHandler(router *http.ServeMux) {
    subrouter := http.NewServeMux()
    router.Handle("/user", someMagicMiddleware(subrouter))
    subrouter.HandleFunc("GET /{id}", getUser)
    subrouter.HandleFunc("POST /{id}", postUser)
    subrouter.HandleFunc("PUT /{id}", updateUser)
}

Upvotes: 2

Views: 2685

Answers (1)

microo8
microo8

Reputation: 3794

Ok so I think I have it:

package httphelp

func Subrouter(router *http.ServeMux, route string) *http.ServeMux {
    sr := http.NewServeMux()
    route = strings.TrimSuffix(route, "/")
    router.Handle(route, removePrefix(sr, route))
    router.Handle(route+"/", removePrefix(sr, route))
    return sr
}

func removePrefix(h http.Handler, prefix string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path
        r.URL.Path = "/" + strings.TrimPrefix(strings.TrimPrefix(path, prefix), "/")
        h.ServeHTTP(w, r)
        r.URL.Path = path
    })
}

It creates a new http.ServeMux which handles all subsequent requests under that prefix. And the sub-paths can be registered independently :)

apirouter := httphelp.Subrouter(mainRouter, "/api")
users.RegisterHandler(apirouter)
products.RegisterHandler(apirouter)

users package:

func RegisterHandler(router *http.ServeMux) {
    subrouter := httphelp.Subrouter(router, "/user")
    subrouter.HandleFunc("GET /{id}", getUser)
    subrouter.HandleFunc("POST /{id}", postUser)
    subrouter.HandleFunc("PUT /{id}", updateUser)
}

Any enhancements welcome :)

Upvotes: 2

Related Questions