A Comparison of Go Web Frameworks
corner.squareup.com
corner.squareup.com
Instead of duplicating code to implement common/middleware functionality, I like to just pass around functions. I usually just have one makeStandardHandler type function that covers most cases. For the special cases, I can easily combine middleware in any order for the desired result.
// Execution timing middleware
func makeTimingHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
requestStartTime := time.Now()
fn(w, r)
totalRequestTime := time.Now().Sub(requestStartTime)
log.Printf("URL: %s TIME: %s", r.URL.Path, totalRequestTime.String())
}
}
// Serve up one file
func makeSingleFileHandler(file string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, file)
}
}
func main() {
...
http.HandleFunc("/robots.txt", makeTimingHandler(makeSingleFileHandler("./s/robots.txt")))
http.HandleFunc("/", makeTimingHandler(indexHandler))
...
}The problem there is that it doesn't allow you to either:
• Apply the middleware to routers or sub-routers;
• You end up with http.HandleFunc("/", loggingMiddleware(recoveryMiddleware(sessionMiddleware(myHandler)))) - which is no fun, repetitive and prone to errors
• There's no request context - your handlers have no way to communicate between themselves (i.e. passing a CSRF token from middleware to the handler rendering the template or writing to the response header)
It does work for basic applications, but it just doesn't scale beyond more than a (small) handful of routes. I wrote a little function[1] that helps with avoiding the gratuitous function nesting, but you'll need to BYO request context in many cases. This often ends up being map[string]interface{} (so you may want getters/setters to wrap types coming in and out), as the alternative is to rely on structs + methods (ala https://github.com/gocraft/web) which can be a little unwieldy. Horses for courses, as they say!
I've been playing around a fair bit with Goji and gocraft/web over the last week and enjoy both their approaches, and may end up porting over a gorilla/* + net/http project to get some sanity out of my middleware and request context.
I can't find the link right now but there was an article somewhere stating that the TL;DR is a redundant construct because if you want to summarize your article you should do it at the top, in the first paragraph.
That first paragraph is called an "Introduction".
Funny enough the author of the blog post wrote a good introduction to his article, but chose to tack a TL;DR to it.
(negroni is another coctail...).
> Martini is now succeeded by negroni.
From the link, > Martini is not going away, and I’m not going to stop
> supporting Martini.The reflective dependency injection is actually one of the biggest things that drew me to Martini initially. My usual Node framework of choice, Bogart, recently added this feature and, having just wrapped up a 2-month project with it, I didn't run into the problem that Jeremy (codegangsta) expressed in that post:
> [..] Martini allows you to inject all the things and that leads to a level of indirection that, while modular, requires a certain amount of cognitive overhead to fully understand what is actually going on.
To the contrary, I actually found that it made me juggle much less in my head. I know exactly where something is being injected (since they all get injected in one place), and I know generally what types of things we inject as dependencies, so the questions "Is this available as a dependency?" and "Should I inject this as a dependency?" are very easy to answer. This makes composing the application (and each route) a much smoother experience.
I compose APIs from Go and NodeJS servers and get really good productivity & performance benefits. And this is what I use; http://github.com/azer/atlas
web frameworks do two things and they're terrible on both. they invent a lot of stuff that you'll likely try to get rid of a later time and it's gonna be too late since your codebase (both frontend and backend) is locked.
but API servers are not like that. they lead you the right (and the more productive) way; composing small libraries into something ideal for you. I'm not sure if it makes sense for people who tend to use frameworks but this works for me great. may be it's my taste
For example, a Java-based website designed this way would have two servlets -- one serving the JSON API and one templating servlet that consumes the JSON API and generates the page.
Another approach would be to forgo the templating servlet (or Rails/Django templating layer) and do all the rendering in a JavaScript client-side app that consumes the JSON API, but this can have SEO implications that may mean you also need to generate static pages for bot consumption (pjax can be useful here https://github.com/defunkt/jquery-pjax).
That might be OK for some instances but many times, one needs to be able to serve a browser directly.
Your idea of a API is still a good one, you might just change the level at which you create this "backend" API.
It's resulted in a very straight forward go server implementation. The only package I use outside of the base go libraries is beego's session package.
So if you're a single guy writing everything by yourself, yeah you can also use your own programming language for both backend and frontend.
I write Go on the server to serve pages, and write rich front ends using simple javascript and jQuery. Being able to do nice quick page fetches means I don't get into massive complexity on the front end with a bloated single-page app, and not trying to deal with front-end complexity on the server means my server routes stay nice and simple.
I use Gocraft/web on the server because it's a useful library not an opinionated framework, and IMHO the most Go-idiomatic of the options (I tried them all and it was a toss-up between Gorilla and Gocraft/web, which won because of the really simple, useful context and middleware paradigm).
Creating API endpoints and page routes is simple and easy, and then writing the javascript to call endpoints when needed is simple (once I'd learned to stop jQuery from appending my data to the url and put it in the request body!).
My only gripe about the mix is that GoConvey doesn't integrate with Jasmine so I have to run two test suites.
Why? In fact, there's someone writing a Golang → Haxe transpiler, which is no less of a hack than CoffeeScript: http://tardisgo.github.io/
I think the majority of people attracted to go are fundamentally opposed to broad statements such as that one.
I have made similar decisions before, and it is more fun and certainly gives a sense of freedom and less to learn, but I think that is generally an incorrect decision and in this case it is particularly obvious. There are many existing Go web frameworks, and I am sure that some existing Go web frameworks are good enough for Square.
This sort of thing is why I prefer to work alone. Because those types of incorrect decisions are very common. There is always more than one way to do something. It is just surprising to me how often engineers carefully select the ways that are inferior (more work, and/or less sophisticated). Of course, as I said, sometimes I make wrong decisions, but in those cases I can always change my mind. An organization or another individual is harder to change though, and bad design decisions can become solidified.
What do they have against dependency injection? Why do they think its 'magic' ?
Developers who are used Rails, Django, Laravel and other large frameworks might not really care, and they might not need to. You develop for and with the framework you picked an understand and accept that stuff works a certain way, because that's what the framework does. If however you're a Go developer and want to do web development Gorilla will seem like a better approach than Martini og Revel, because it's just add the bits you're missing. Picking a larger framework and you get locked in doing stuff their way, and the dependency injection and "magic" makes it hard or impossible to escape the framework, should you want to.
For me, I like to be able to look a just my own code and reason about what it does, without ending in "here I pass a bunch of stuff of the framework and hope it does the right thing". Normally I know what I want and if the framework has these magical bits they more often than not get in the way... I'm looking at you stupid Django Rest Framework.