Go web server with Bootstrap

On the server side: Go web server

Go is a new language by Google, and it is getting something of a following in terms of being used to develop web servers.

There are a bunch of Go webserver frameworks, akin to Rails for Ruby, Django for Python, Node.js, and so on. The main ones at this point are: Beego, Revel, Martini. Whether to use these frameworks or not is your choice.

There are many who propose that not using them is a better idea in the long run, and to rely on Go’s excellent net/http package, or simple middleware such as Gorilla’s suite of packages, known as Gorilla web toolkit, or Gocraft/web packages.

Square chose to use Gorilla.

I, too.

On the design side: Bootstrap

Bootstrap is a popular framework that makes building websites that look good, easy.

Whether or not you like the idea that bootstrap proposes, or not, this post is here to help those who are keen to write a Go web server that uses Bootstrap.

The problem

The main problem that you’ll face is that you’ll notice that your Go server doesn’t recognize the static path for your web pages (e.g. your html files) to access bootstrap.

A simple Go web server

We’ll build a simple Go web server here to use as a starting point. This doesn’t deal with Bootstrap. It’s just nothing more than a simple web server.

We’ll use the Gorilla mux package, but you really don’t have to.

package main

import (
    "flag"
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func RootHandler(response http.ResponseWriter, request *http.Request) {
    response.Header().Set("Content-type", "text/html")
    fmt.Fprintf(response, "Hey there!\n")
}

func main() {
var host = flag.String("host", "127.0.0.1", "IP of host to run webserver     on")
    var port = flag.Int("port", 8080, "Port to run webserver on")

    flag.Parse()

    router := mux.NewRouter()
    router.HandleFunc("/", RootHandler)

    addr := fmt.Sprintf("%s:%d", *host, *port)
    log.Printf("Listening on %s", addr)

    err := http.ListenAndServe(addr, router)
    if err != nil {
        log.Fatal("ListenAndServe error: ", err)
    }
}

Really couldn’t be simpler, except to not use the flags package.

Serving static files

To allow your pages to get access to bootstrap’s files, you will want your server to serve static files off a particular directory. We’ll call it static/. So, we multiplex the router to serve the static directory.

We can do this as follows:

package main

import (
    "flag"
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func RootHandler(response http.ResponseWriter, request *http.Request) {
    response.Header().Set("Content-type", "text/html")
    fmt.Fprintf(response, "Hey there!\n")
}

func main() {
    var host = flag.String("host", "127.0.0.1", "IP of host to run webserver on")
    var port = flag.Int("port", 8080, "Port to run webserver on")
    var staticPath = flag.String("staticPath", "static/", "Path to static files")

    flag.Parse()

    router := mux.NewRouter()
    router.HandleFunc("/", RootHandler)
    router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(*staticPath))))

    addr := fmt.Sprintf("%s:%d", *host, *port)
    log.Printf("Listening on %s", addr)

    err := http.ListenAndServe(addr, router)
    if err != nil {
        log.Fatal("ListenAndServe error: ", err)
    }
}

A simple page

We create a simple page that we serve up. The key point here is the way the page accesses the Bootstrap files via the static path.

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
    <script src="/static/js/bootstrap.min.js"></script>

    <style type="text/css">
      body {padding-bottom: 70px;}
      .content {margin:10px;}
    </style>
  </head>

  <body>
    <nav class="navbar navbar-default" role="navigation">
      <div class="navbar-header">
        <a class="navbar-brand" href="/">Application</a>
      </div>
      <div class="collapse navbar-collapse navbar-ex1-collapse">
        <ul class="nav navbar-nav">
          <li><a href="/">Main</a></li>
          <li><a href="/about">About</a></li>
        </ul>
      </div>
    </nav>
  </body>
</html>

Note that in this page, you can directly use Go’s templating engine html/template to dynamically generate the page. The simple html code above does not illustrate this. But as a case in point, you could simple have the page title be dynamically generated by substituting the line above:

<title>Page Title</title>

with something like:

<title>{{.Title}}</title>

How to use Go’s templates is not the aim of this post though. But’s it’s pretty simple.

And that’s it. You may serve the html file in any way you wish (e.g. literally read it and write it back to the http.ResponseWriter as a simple proof-of-concept), and it will be able to successfully parse the Bootstrap CSS and render you a nice (Bootstrap-y) page.

Putting it together

Say we call the previous html file header.html. I’m going to put a full working code here, that also uses a touch of Go’s templates, for the web server, as a reference point.

package main

import (
    "flag"
    "fmt"
    "github.com/gorilla/mux"
    "html/template"
    "log"
    "net/http"
)

type Page struct {
    Title string
}

var templates = template.Must(template.ParseFiles("header.html"))

func RootHandler(response http.ResponseWriter, request *http.Request) {
    response.Header().Set("Content-type", "text/html")
    err := request.ParseForm()
    if err != nil {
        http.Error(response, fmt.Sprintf("error parsing url %v", err), 500)
    }
    templates.ExecuteTemplate(response, "header.html", Page{Title: "Home"})
}

func main() {
    var host = flag.String("host", "127.0.0.1", "IP of host to run webserver on")
    var port = flag.Int("port", 8080, "Port to run webserver on")
    var staticPath = flag.String("staticPath", "static/", "Path to static files")

    flag.Parse()

    router := mux.NewRouter()
    router.HandleFunc("/", RootHandler)
    router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(*staticPath))))

    addr := fmt.Sprintf("%s:%d", *host, *port)
    log.Printf("Listening on %s", addr)

    err := http.ListenAndServe(addr, router)
    if err != nil {
        log.Fatal("ListenAndServe error: ", err)
    }
}

That should render a Bootstrap themed header when you access it at http://localhost:8080/.