3 ms·
Author here. Thanks for your input, but I'd like to clarify some points. The blog post's intent was not to show how to implement your own middleware mechanism
by sjustinas 13y ago
Author here. Thanks for your input, but I'd like to clarify some points.
The blog post's intent was not to show how to implement your own middleware mechanism in a web framework you're writing, but how to create HTTP middleware using the standard Go net/http interface.
Your proposed middleware type might feel a bit nicer, but it disregards the standard net/http interface and makes your middleware incompatible with it. Well, you could in theory make it compatible, but you would need some more "glue" and hacks. net/http doesn't really work the way you're proposing (returning a Response object, etc.). Please see my other post on the standard Go HTTP tools: http://justinas.org/embrace-gos-http-tools/ http://justinas.org/embrace-gos-http-tools/
If you don't want full compatibility with existing net/http libraries, then sure – there are nicer ways. Again, my intention was to show how to do it with net/http and I agree that there are certain trade-offs.
"(note, I'd make the middlewares nesteable, passing a next middleware to each middleware, but that wasn't covered in the article)."
It was covered to some extent. In a model where every middleware is http.Handler itself, you nest your middlewares like:
finalHandler := Middleware1(Middleware2(MyApp))
So every middleware does actually get a reference to the next handler that should run.
- latch 13y agoI guess we'll disagree about whether eliminating a global variable [in a multi-threaded language] is nothing more than "might feel a bit nicer". Same with using a class which specifically exists for testing. net/http works fine with a response interface: context := newContext(req) response := chain.yield(context) // the exact same code you had: for k, v := range response.Header() { w.Header()[k] = v } ... This has the added benefit of letting you use different response implementations. A streaming implementation, an in-memory implementation, or, probably the most important if you're doing high traffic, one that relies on a pre-allocated byte-pool. A little nitpick on the side, new(sync.RWMutex) isn't needed. You can just do var cmMutex sync.RWMutex (http://golang.org/src/pkg/net/http/server.go?s=45646:46651#L877 http://golang.org/src/pkg/net/http/server.go?s=45646:46651#L...) EDIT: Sorry, not trying to be negative (so sick of this on here), but I do think global variables are really, really bad and we probably shouldn't be promoting patterns that rely on them.
- sjustinas 13y agoSure, I didn't say it was impossible to convert your proposed Response type to a proper net/http response when you need it. But, like I said and you just demonstrated, glue code is needed to do that. That makes people write differing middleware application mechanisms that are incompatible with each other. Sure, ResponseRecorder is placed in net/http/httptest and that makes sense. But then again, nothing in the actual implementation is integrated with the testing package or tied to testing in general. IMO there's nothing that makes it unsuitable for other usage. It's a simple buffer to accumulate HTTP, that's it. Even if one were to implement a buffering writer like that on their own, it wouldn't differ too much.