Go Web Programming: MVC architecture based web app

In this article I want to talk about how to structure your MVC based app so its reusable and robust. Much of the style you are going to see in this article is based on Django (Python web framework).

Let’s start, are we going to use a framework ? No, we are going to use GoLang’s inbuilt net/http package at the core in conjunction with some handy tools from Gorilla (web tool kit). Throughout the article I’m also going to talk about the development environment in which we are going to build a tiny post publishing web app.

First things first, we are going to use a REPL (read, eval, print, loop) in the form of gin, a live build server tool which will recompile your Go app when there are any changes to the code base.

To install just do go get https://github.com/codegangsta/gin from your app directory .

We are also going to need a package dependency manager, for this I’m going to use GoDep. Godep is also smart enough to create a vendors directory and load third party packages into that folder, this is required if you want to containerize the app.

Just like earlier, to install, go get https://github.com/tools/godep .

Folder Tree Structure

Right, now that the environment is sorted, lets talk about the folder structure.

That’s a lot of folders, I’ll cover most of them as we go along but for now the most important folders to note are

templates — For holding our html

controllers — For holding our controllers

models — For holding our data models

medium.go is going to the starting point of our application

Finally, its code time, without wasting any time lets dive into medium.go

package main
import (
"log" // System
"medium/routers" // Local
"github.com/urfave/negroni" // Third Party
)
func main() {
router := routers.GetRouter()
n := negroni.Classic()
n.UseHandler(router)
log.Println("Listening:")
n.Run(":3001")
}

Looking at the imports, having an import order is really useful for others to understand. Here, we use negroni, a tiny middleware focused library which works directly with Go’s net/http . n.Run(":3001") will run our app on localhost:3001 , since we are using gin, from our app directory lets run gin --port:3000 , this will run a proxy server for our live reload server. Now our web app can be accessed either from ports 3000 or 3001. Of course the whole point of gin is to use it from 3000. Using gin I can skip running / building the project every time I make a change.

To install negroni, go get https://github.com/urfave/negroni

The terminal window when there is an error in our code followed by when there is none.

Now, what does the router variable hold? Good question, lets dive into router.go .

package routers
import (
"net/http"
"medium/controllers"
"medium/middlewares"
"medium/urls"
"github.com/gorilla/pat"
"github.com/urfave/negroni"
)
// registers all routes for the application.
func GetRouter() *pat.Router {
// url paths imported from urls package
url_patterns := urls.ReturnURLS()
medium := pat.New()
medium.Get(url_patterns.HOME_PATH, controllers.HomeController)
common := pat.New()
// static route
common.PathPrefix(url_patterns.STATIC_PATH).Handler(
http.StripPrefix(url_patterns.STATIC_PATH, http.FileServer(http.Dir("static"))))
// applying middlewares
common.PathPrefix(url_patterns.MEDIUM_PATH).Handler(
negroni.New(
negroni.HandlerFunc(
middlewares.LoggingMiddleware),
negroni.Wrap(medium),
),
)
common.Get(url_patterns.LOGIN_PATH, controllers.LoginController)
common.Post(url_patterns.LOGIN_PATH, controllers.LoginController)
return common
}

Ok this file has lots of things going on, a variable url_patterns is holding values returned from the urls package.

package urls
type (
urls struct {
STATIC_PATH string
LOGIN_PATH string
HOME_PATH string
MEDIUM_PATH string
}
)
func ReturnURLS() urls {
var url_patterns urls
url_patterns.STATIC_PATH = "/static/"
url_patterns.LOGIN_PATH = "/"
url_patterns.MEDIUM_PATH = "/medium/"
url_patterns.HOME_PATH = url_patterns.MEDIUM_PATH + "home/"
return url_patterns
}

This is the urls package, here we define all the urls we want our application to have access to, the urls defined here are going to be used in the routers , controllers, templates packages. This way we have one file where we can manage/edit urls throughout the app. Any url we want to add to the application needs to be done here.

Going back to the routers package, after we have retrieved all the urls, we start to construct routes that are associate with controllers.

common := pat.New()
common.Get(url_patterns.LOGIN_PATH, controllers.LoginController)
common.Post(url_patterns.LOGIN_PATH, controllers.LoginController)

Here common is a pat object, pat is a request router and dispatcher which uses regex under the hood to match urls requests.

To install pat, go get https://github.com/gorilla/pat

At this point, we have installed quite a few packages, lets use GoDep to save the packages to a file, we can do this by hitting godep save , this will do a bunch of things, first of, it will create a Godeps directory with a json file containing all the packages required in the application including the package versions. Under any circumstance do we lose the packages from our $goroot or $gopath we can easily restore using the json by hitting godep restore , handy !!

Anyways, pat, has all your basic http methods like get, post, put, patch, delete. I have decided to use only get and post for this demo.

common.Get(url_patterns.LOGIN_PATH, controllers.LoginController)

As you can see, each route will look something like this, i.e,

<router object>.<http method>(<url pattern>, <controller the request has to be sent to>)

Any route that you want to add to the application needs to be done here. And to do so we need a url and a controller.

One more thing, I want to talk about in the router.go file before I head onto controllers, is the following two entries,

// static route
common.PathPrefix(url_patterns.STATIC_PATH).Handler(
http.StripPrefix(url_patterns.STATIC_PATH, http.FileServer(http.Dir("static"))))

To serve static files, like JS, CSS and images, we use this route.
This route basically says any request with url that starts with the “static” prefix means it is going to look for a file in the static directory to be served directly.

 // applying middlewares
common.PathPrefix(url_patterns.MEDIUM_PATH).Handler(
negroni.New(
negroni.HandlerFunc(
middlewares.LoggingMiddleware),
negroni.Wrap(medium),
),
)

What if we need every request that comes to our web server to undergo some processing? This is where negroni middlewares come in. Here we are saying any request with url that starts with medium needs to be sent to LoggingMiddleware first.

Let’s have a peek into middlewares.go ,

package middlewares
import (
"fmt"
"net/http"
"os"
"strings"
"time"
)
func LoggingMiddleware(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
// Logic to write request information, i.e. headers, user agent etc to a log file.
next(res, req)
}

I have removed the actual implementation as that is not what we want to discuss here. The link to the entire repo is at the bottom of the article.

The LoggingMiddleware takes a request and response objects as arguments and then uses the same objects to redirect to a controller after finishing some task.

Controllers

Controllers , here we go

Every entry in the router.go file is mapped to a controller in the controllers package.

package controllers
import (
"log"
"net/http"
"time"
"medium/helpers"
"medium/models"
"medium/store"
"medium/templates"
"medium/urls"
"medium/utils"
"github.com/gorilla/schema"
)
func LoginController(res http.ResponseWriter, req *http.Request) {
data := make(map[string]interface{})
controllerTemplate := templates.LOGIN
url_patterns := urls.ReturnURLS()
if req.Method == "GET" {
utils.CustomTemplateExecute(res, req, controllerTemplate, data)
}
if req.Method == "POST" {
err := req.ParseForm()
user := new(models.User)
decoder := schema.NewDecoder()
err = decoder.Decode(user, req.Form)
if err != nil {
utils.CustomTemplateExecute(res, req, controllerTemplate, data)
} else {
session, _ := utils.GetValidSession(req)
session.Values["nickname"] = helpers.StripWhiteSpaces(req.Form["Nickname"][0])
session.Save(req, res)
http.Redirect(res, req, url_patterns.HOME_PATH, http.StatusSeeOther)
}
}
}

A controller takes a response and a request object.

The data variable is a map of string keys holding any interface value. Will talk about this in a bit. The controllerTemplate is a string const from the templates package, holding the path to a html file. url_patterns holds the urls from the urls package.

Here is the templates.go file, holding the html paths,

package templates
const BASE string = "templates/base.html"
const HOME string = "templates/home.html"
const LOGIN string = "templates/login.html"
const POSTS string = "templates/posts.html"
const POST_ADD string = "templates/post_add.html"
const POST_VIEW string = "templates/post_view.html"

Now, every controller is divided into get and post, in the LoginController the get request handler calls utils.CustomTemplateExecute from the utils package. Any template that needs to be added to the application needs to be done here.

The utils package holds any common functionality throughout the app which helps in the flow of the app

Here is the utils.go file,

package utils
import (
"net/http"
"strings"
"text/template"
"medium/models"
"medium/templates"
"medium/urls"
"github.com/gorilla/sessions"
)
func CustomTemplateExecute(res http.ResponseWriter, req *http.Request, templateName string, data map[string]interface{}) {
// Append common templates and data structs and execute template
tempFunc := template.FuncMap{
"add_params": func(url string, params ...string) string {
for _, param := range params {
// not proud of this
if strings.Contains(url, "placeholder") {
url = strings.Replace(url, "placeholder", param, 1)
} else {
url = strings.Replace(url, "{"+param+"}", "placeholder", 1)
}
}
return url
},
}
data["url_patterns"] = urls.ReturnURLS()
session, _ := GetValidSession(req)
data["nickname"] = session.Values["nickname"].(string)
t, _ := template.New("").Funcs(tempFunc).ParseFiles(templates.BASE, templateName)
t.ExecuteTemplate(res, "base.html", data)
}

CustomTemplateExecute is a function that takesa request and response object as arguments and additionally the data map variable which we just talked about, and the template path.

The values from data map variable is now available to be used in a html file via the string key.

The CustomTemplateExecute then calls for the template to be executed after parsing the html files.

Now, lets talk about the post part of the LoginController,

Post normally takes form data which is parse by calling req.ParseForm(),

Form data is now available for us to use,

err := req.ParseForm()
user := new(models.User)
decoder := schema.NewDecoder()
err = decoder.Decode(user, req.Form)

The above snippet from LoginController uses the schema package to map Form data to a User struct from models package.

Let us see how the form and model look like,

<form class="c-form" action="" method="post">
<div class="input-field col s12">
<input id="nickname" name="Nickname" type="text" class="validate" required>
<label class="active" for="nickname">Nickname</label>
</div>
<div class="input-field col s12">
<input class="btn blue" type="submit" value="Enter" />
</div>
</form>

models.go

package models
import (
"time"
)
type (
User struct {
Nickname string
}
)

As simple as that, we have a form with a text input of “Nickname” and a model with a “Nickname” field. Schema does a great job at mapping and now we can use the form value as user.Nickname. Any models that needs to added the application needs to be done here.

Note: Any changes to the html field from the user will yield in a schema error. No tampering !!

Now

err = decoder.Decode(user, req.Form)
if err != nil {
utils.CustomTemplateExecute(res, req, controllerTemplate, data)
} else {
session, _ := utils.GetValidSession(req)
session.Values["nickname"] = helpers.StripWhiteSpaces(req.Form["Nickname"][0])
session.Save(req, res)
http.Redirect(res, req, url_patterns.HOME_PATH, http.StatusSeeOther)
}

If err is not nil we re render the html, else we create a session with the value of “Nickname”. We are using a method here from the helpers package.

helper.go

package helpers
import "strings"
func StripWhiteSpaces(whiteSpacedString string) string {
return strings.Join(strings.Fields(whiteSpacedString), "")
}

This helper function helps in removing any white spaces from the Form’s “Nickname” value.

Any function that helps a controller with it’s flow is put in the helpers package. We need to use our best judgement to see if a function belongs to the helpers package or the utils package. I normally put domain/controller specific functions in helpers and application/system specific functions in utils.

If you have observed, we just came across a very important topic, Sessions !

A function in the utils package is responsible for creating a session to be accessed and used throughout the application

func GetValidSession(req *http.Request) (*sessions.Session, error) {
// Returns a valid authenticated user session
sessStore := sessions.NewCookieStore([]byte("medium"))
return sessions.Store.Get(sessStore, req, "medium")
}

sessions is again a Gorilla package.

Note: NewCookieStore takes an argument to encrypt, loading a hash string from the environment is a good idea.

Templates

That controller had a lot of things in it, lets move on to templates

Go has template inheritance and other handy functionalities built into the text/template package. Let’s talk about template inheritance,

Some templates I have defined,

base.html — The common template for all templates, includes CSS, JS, Nav Bar etc

home.html — Welcomes the user, and contains link to adding a post

posts.html — Shows all the posts

base.html

<!DOCTYPE html>
<head>
<title> { Medium } </title>
</head>
<body class="grey lighten-2">
<nav>
<div class="nav-wrapper green darken-1">
<a href="#" id="title-text" class="c-no-pointer center brand-logo">{ Medium }</a>
<ul id="nav-mobile" class="left hide-on-med-and-down">
</ul>
</div>
</nav>
<div class="">
<div class="row">
<div class="col s1">
</div>
<div class="col s10">
{{ template "content" .}} // content block
</div>
<div class="col s1">
</div>
</div>
</div>
<!-- JavaScript Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js">
$(document).ready(function () {
// Initialize collapse button
$(".button-collapse").sideNav();
});
</script>
</body>
</html>

base.html is rendered in every call of utils.CustomTemplateExecute

t, _ := template.New("").Funcs(tempFunc).ParseFiles(templates.BASE, templateName)

In the above line, templateName is passed from a controller and is basically inserted in the template "content" block of base.html

login.html

{{ define "content" }}
<div class="col s12 card c-padding-top-20 c-padding-bottom-10">
<div class="col s4">
<form class="c-form" action="" method="post">
<div class="input-field col s12">
<input id="nickname" name="Nickname" type="text" class="validate" required>
<label class="active" for="nickname">Nickname</label>
</div>
<div class="input-field col s12">
<input class="btn blue" type="submit" value="Enter" />
</div>
</form>
</div>
</div>
{{ end }}

This is how login.html would look, starts of by defining “content” block.

For every block that is declared in base.html needs to be defined in login.html or any other template passed from the controller.

login.html

home.html

{{ define "content" }}
<div class="col s12 card c-padding-top-20 c-padding-bottom-10">
Hello, {{.nickname}}
<a href="{{.url_patterns.POST_ADD_PATH}}" class="btn blue right">Add Post</a>
</div>
{{ end }}

Looking at home.html we can see we can use urls from the urls package.

As we are sensing, any {{ }} in html is replaced by values from data map variable from the utils.CustomTemplateExecute function.

session, _ := GetValidSession(req)
data["nickname"] = session.Values["nickname"].(string)

The above snippet from utils.CustomTemplateExecute gives us access to “Nickname” in any template.

Nifty !

So far so good, what about REST urls you might ask ? Let’s head on to

posts.html

{{ define "content" }}
<div class="row">
{{$urls := .url_patterns}}
{{ range .posts }}
<div class="col s12 card">
<h5><a href='{{add_params $urls.POST_VIEW_PATH "postid" .ID.Hex }}'>{{.Text }}</a></h5> <h6 class="right"> by {{.Nickname}} on {{.Time}} </h6>
</div>
{{ end }}
</div>
{{ end }}
posts.html

posts.html contains any posts we have submitted, clicking on a post will take us to the specific post page post_view.html

post_view.html

Not fancy, but if you observe the url, this was basically generated by add_params function.

add_params is template function that we declared in utils.CustomTemplateExecute, any functions defined there would be available for us to use in html.

The function add_params,

tempFunc := template.FuncMap{
"add_params": func(url string, params ...string) string {
for _, param := range params {
// not proud of this
if strings.Contains(url, "placeholder") {
url = strings.Replace(url, "placeholder", param, 1)
} else {
url = strings.Replace(url, "{"+param+"}", "placeholder", 1)
}
}
return url
},
}

posts.html

<a href='{{add_params $urls.POST_VIEW_PATH "postid" .ID.Hex }}'>

The first argument is the url from the urls package, in this case

url_patterns.POST_VIEW_PATH = url_patterns.MEDIUM_PATH + "post/{postid}/"

The next two arguments need to be the param name i.e postid followed by the value.

Right, now I want to talk about some snippets from the app which I feel are important to note,

Redirection

Redirection from a controller can be made using a simple statement,

http.Redirect(res, req, url_patterns.HOME_PATH, http.StatusSeeOther)

What if we need to pass some params ?

We use the AddParamsToUrl function from, you guessed it, the utils package.

func AddParamsToUrl(url string, args []models.Kwargs) string {
// Add params to url using a splice of models.kwargs struct
for _, arg := range args {
url = strings.Replace(url, "{"+arg.Key+"}", arg.Value, 1)
}
return url
}

We can use the above function in the following way,

var kwargs []models.Kwargs
kwargs = append(kwargs, models.Kwargs{Key: "postid", Value: <post id>)})
http.Redirect(res, req, utils.AddParamsToUrl(urls.POST_VIEW_PATH, kwargs), http.StatusSeeOther)

What is models.Kwargs ?

package models
type (

// Pass keyword args to urls
Kwargs struct {
Key string
Value string
}
)

Kwargs is a struct in models.go , which has two fields, Key and Value.

Key is the param name in the url_patterns, and Value is the value you the param name to be replaced with, in this case {postid}.

Data persistence:

This is out of the scope of this article but I am just going the pen down my thoughts here. There are ORM’s for SQL based databases like gorm, there are mature drivers for NoSQL databases as well like mgo for MongoDB.

Personally, I have used mgo and wrote my own persistence layer which can be found in the store package.

Final Thoughts

Following this architecture / blueprint helps in easily understanding the flow of the application and modularizes the app into components for robust development.

There are enough packages and tools out there to build powerful Go Web apps. There are also several frameworks out there which do a job but building your own blueprint doesn’t take too long, and gives you the ease of owning your entire code base.

I have tried to cover the most important parts of the app, but also managed to leave some parts out. I strongly encourage you to clone the repository and give it a test run. Go through the code and fiddle around with it.

Link to repo: https://github.com/priyankcommits/medium

Steps to run:

  1. Clone repo
  2. Install godep, go get https://github.com/tools/godep
  3. Install gin, go get https://github.com/codegangsta/gin
  4. Install dependencies by hitting godep save ( go get -v also works)
  5. Run using gin --port:3000
  6. Point browser to localhost:3000

That’s about it for now, do let me know what you think and I will be glad to answer any questions.

Lastly, Write in GO !!!