Intro to (images in) Go – http

This post is part of a series. For a listing of all the posts, as well as instructions on running the code, see here.

So far, we’ve pretty much always been using Go to spit out static image files. However, it’d be nice if we could do something a little bit more interactive, like generate images based on user input and the like. So here’s the plan:

  • We’ll look at how to put together a HTTP server first
  • Then we’ll add in some websockets for realtime communication with our Go code
  • And finally we’ll use the HTML5 canvas to display our creations

Today we’ll just take a look at the HTTP server, in the process explaining how to build API handlers, serve static files and “decorate” functions. So sorry, no piccies today.

Basic HTTP server

Getting a simple HTTP working in Go is a piece of cake – it is even included as part of the Tour of Go . To make things more interesting and to give ourselves more flexibility in the future we’ll also reach for a third-party component to make routing requests cleaner and easier to write. Mux is that badger, letting us define route handlers using regular expressions and more. If you’ve used Sinatra or Flask, this should be familiar.

To fetch the Mux code, use go get github.com/gorilla/mux. Once that is done, here’s is how you’d use it:

import (
  "io"
  "net/http"

  "github.com/gorilla/mux"
)

func HelloHandler(writer http.ResponseWriter, request *http.Request) {
    io.WriteString(writer, "hello!")
}

func main() {
  router := mux.NewRouter()
  router.HandleFunc("/hello", HelloHandler)
  http.ListenAndServe("localhost:1234", router)
}

There we are, one http server. Try running it and navigating to localhost:1234/hello. A few things to notice:

  • When importing the mux package, we use the full package name.
  • In the main function we create a router object and register the HelloHandler function on it, with an associated route: "/hello"
  • The HelloHandler takes 2 arguments, a writer into which we send the response and the request itself, which we can examine, e.g. to see what headers were set.

Parsing the URL

The power of Mux is that it makes it easy to parse out pieces of the URL. So if we’d like to say hello, based on the user’s name we could do:

router.HandleFunc("/hello/{name}", HelloHandler)

And then in the handler access the variables using mux.Vars():

vars := mux.Vars(request)
io.WriteString(writer, "hello " + vars["name"] + "!")

There’s much more that Mux can do, check out their docs if curious.

Serving static files

For writing an API the above is great, but what if we want to serve static files? Luckily for us, the Go library come with a FileHandler ready to go. Adding it in is just one line:

staticHandler := http.FileServer(http.Dir("."))
router.PathPrefix("/").Handler(staticHandler)

(Well, two lines. But it could be one. Rest assured, all will be explained)

The staticHandler will take the part of the route requested after the "/", and try to serve that file if it exists in the current directory. For example http://localhost:1234/sweet_goat.jpeg will return the file sweet_goat.jpeg found in the directory the code is running from. This router is then registered with our Mux router object, which will send any requests that match to it – in this case everything will match as we’ve entered "/".

There is one subtlety here though: what if we wanted to serve our static content at http://localhost:1234/static? Easy, we’d change the PathPrefix argument above from "/" to "/static/", right? Well, not quite.

The trouble is that if we do this then while our router will correctly recognise that we have navigated to /static/file, our FileServer does not know that it should throw away the static part of the URL when handling the request and tries to find the file in the static directory on its filesystem, which does not exist. To remedy this, we need some sort of way to strip out the prefix before passing the request onto the FileServer.

Again, luckily for us, the standard library comes to the rescue:

staticHandler = http.StripPrefix("/static/", staticHandler)

See here for a working example

Decorating a Handler

It’s worth taking a look at the source of the StripPrefix function. What it is doing, is wrapping the staticHandler function in another function and then returning that. For those of you know Python, this is more or less the same as decorating a function, except without the syntactic sugar.

To really understand the concept, lets create a “decorator” of our own. Specifically, let’s modify a Handler so that we set the headers in such a way to instruct a web browser to not cache our data. I find this pretty useful during development, when I want to be sure I’m running the latest code.

So, here’s our decorator:

func NoCacheDecorator(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
    w.Header().Set("Pragma", "no-cache")
    w.Header().Set("Expires", "0")
    h.ServeHTTP(w, r)
  })
}

Which we’ll apply to an existing Handler like so:

staticHandler = NoCacheDecorator(staticHandler)

First off, notice what it is that we’re actually returning. We are defining a function, and then calling http.HandlerFunc on it. This is just a convenient way of converting our function into a Handler object. The function itself, takes the request that will be passed onto the Handler we are decorating (in this example staticHandler) and modifies some of the headers. It then passes the request onto the decorated Handler, by invoking its ServeHttp method.

You can verify this is working by using something like the Chrome develop tools to examine the headers on the response.

One nice feature about the decorator pattern is that we can stack multiple decorators on top of each other and things will work (as long as the decorators do not interfere functionally). In fact, that is exactly what we’ve done. We’ve created a bog standard FileHandler, decorated it once using StripPrefix and then again using NoCacheDecorator. The result is a file handler which knows to strip off the correct prefix, and set the correct no caching headers.

Check out the code on github for a working example. Run using go run simple_server.go

  • Abdurehman Yousaf

    i have built a REST API using Golang and MongoDB and make separate packages for controllers and models and use them in main package using main.go fiel, Now i want to do same thing for routes, want to define all of m routes in a separate package and access them in main, i’m performing routing using gorilla/mux package.
    Can anyone help me Plz!
    Thanks in Advance!