Zachscs
Zachscs

Reputation: 3663

Using intermediate handler function to route requests

I was thinking of ways to do routing with the standard library. Is it valid to have a handler function that will call other handler functions depending on the request type? e.g.

func main() {
    m := http.NewServeMux()
    m.HandleFunc("/books", books)
    // ...
}

func books(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
        case "GET":
            getBooks(w, r)
        case "POST":
            createBook(w, r)
    }
}

Is this good practice? I didn't want to declare a custom handler since I find functions to be a bit cleaner.

Upvotes: 5

Views: 635

Answers (3)

Zak
Zak

Reputation: 5898

This is valid, as mentioned in the other answers handlers are just functions.

The thing that changes is the request method; it might worth considering an implementation with a map. This could be more extensible as the number of handlers you need change.

type BookController struct {
    routes map[string]http.HandlerFunc
}

func (b BookController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // beware this very basic example will panic if there's not a registered handler for r.Method
    // but I assume you'd have to do handling of missing method logic in your default switch anyway
    b.routes[r.Method].ServeHTTP(w, r)
}

And to register them:

m := http.NewServeMux()
b := BookController{}
b.routes = map[string]http.HandlerFunc{
    "GET": getBooks,
    "POST": createBooks,
}
m.Handle("/books", b)

Slightly more boiler plate, but might allow for easier adding / removing of handlers later.

Upvotes: 2

Martin Tournoij
Martin Tournoij

Reputation: 27822

Yes, this is perfectly valid; handlers are just functions so there is no reason it shouldn't be. In fact, this is how middleware is usually implemented.

There is nothing "magic" about handler functions at all. As long as you're writing to the correct file descriptor (w http.ResponseWriter) anything goes.

That doesn't mean that using this pattern is necessarily a good idea for all applications – routing libraries exist for a reason – but for smaller programs it will work just fine.

Upvotes: 5

Mihailo
Mihailo

Reputation: 4917

It's completely valid to have a HandlerFunc call another HandlerFunc.

In fact that's how you would implement middleware using the standard lib.

Here's a Post by Mat Ryer (amazing guy btw) that explains in some further detail what he calls
"http.Handler wrapper technique"

The way you want to use HandlerFuncs reminds me of Laravel's Resource Controllers, which I think are pretty neat. But using them carelessly could become an unnecessary overhead.

I would suggest you perhaps give julienschmidt/httprouter library a try.
It's pretty fast and easy to use.

Upvotes: 3

Related Questions