An Intro to Templates in Go (part 1 of 3)

Datetime:2016-08-23 03:01:04          Topic: HTML           Share

NOTE: This is a multi-part blog post (likely 3 parts), and I intend to publish the follow-up posts next week. Sign up for my mailing list to get notified when they are posted. I promise I don't spam.

If you have done any web programming, chances are you have heard of or used templates at one point or another. Put simply, templates are basically text files that can be used to create dynamic content. For example, you might have a template for your website's navigation bar, and part of the dynamic content might be whether to show a signin or logout button depending on whether the current user is logged in or not.

Go provides two pretty amazing templating libraries - text/template and html/template . How you use these templates is identical, but behind the scenes the html/template package will do some encoding to help prevent code injection. The coolest part about this encoding is that is contextual, meaning that the it can happen inside of HTML, CSS, JavaScript, or even in URLs and the template library will determine how to properly encode the text.

Code injection is a common exploit where users try to get your server or visitors of your page to execute code that they write and input into your application. This typically becomes an issue when you don't process or escape input in order to make it invalid. For example, if someone were to sign up for your web app with the username <script>alert("Hi!");</script> and you simply inserted their username on their profile page this could lead to code injection. In this case, anytime someone visited that user's profile page they would see a javascript alert with the text "hi" in it. This becomes dangerous because javascript is typically attached to a user's session, so if you were to instead run some code that visits the /delete-my-account page, it could delete the account of any user who visits the injected user's profile page. Go combats this by encoding text in html templates so that it can't execute. For example, the username <script>alert("Hi!");</script> would get encoded to be &lt;script&gt;alert(&quot;Hi!&quot;);&lt;/script&gt; which will render as the original username, but won't be executed by anyone visiting the page as actual code.

Since both template libraries utilize the same interface, everything covered in this article could be used for either package, but most of the examples will be using the html/template package to generate HTML snippets.

Creating a Template

First lets go ahead and create a main function that executes a really simple template so we can see it in action. Open up your editor and navigate to $GOPATH/src/???/ (the ??? can be whatever you want). Create the files hello.gohtml and main.go . Add the following code to hello.gohtml :

<h1>Hello, {{.Name}}!</h1>

NOTE: The extension gohtml is commonly used by editors to indicate that you want Go HTML template syntax highlighting. Both Atom and GoSublime recognize this by default.

And then add the following code to main.go :

package main

import (  
  "html/template"
  "os"
)

func main() {  
  t, err := template.ParseFiles("hello.gohtml")
  if err != nil {
    panic(err)
  }

  data := struct {
    Name string
  }{"John Smith"}

  err = t.Execute(os.Stdout, data)
  if err != nil {
    panic(err)
  }
}

Now go ahead and run your code with go run main.go . You should see the following output:

<h1>Hello, John Smith!</h1>

You have successfully created your first template! Now lets explore how Go's template libraries handles encoding.

Contextual Encoding

I mentioned before that Go's html/template package does encoding based on the context of the code, and in this section we are going to demonstrate what that encoding actually looks like in different contexts. Create another template named context.gohtml and add the following code to it.

{{.Title}}
{{.HTML}}
{{.SafeHTML}}
{{.}}


<a title="{{.Title}}">  
<a title="{{.HTML}}">

<a href="{{.HTML}}">  
<a href="?q={{.HTML}}">  
<a href="{{.Path}}">  
<a href="?q={{.Path}}">

<!-- Encoding even works on non-string values! -->  
<script>  
  var dog = {{.Dog}};
  var map = {{.Map}};
  doWork({{.Title}});
</script>

Then update main.go to have the following code.

package main

import (  
    "html/template"
    "os"
)

type Test struct {  
    HTML     string
    SafeHTML template.HTML
    Title    string
    Path     string
    Dog      Dog
    Map      map[string]string
}

type Dog struct {  
    Name string
    Age  int
}

func main() {  
    t, err := template.ParseFiles("context.gohtml")
    if err != nil {
        panic(err)
    }

    data := Test{
        HTML:     "<h1>A header!</h1>",
        SafeHTML: template.HTML("<h1>A Safe header</h1>"),
        Title:    "Backslash! An in depth look at the \"\\\" character.",
        Path:     "/dashboard/settings",
        Dog:      Dog{"Fido", 6},
        Map: map[string]string{
            "key":       "value",
            "other_key": "other_value",
        },
    }

    err = t.Execute(os.Stdout, data)
    if err != nil {
        panic(err)
    }
}

Then go ahead and run your code with go run main.go . You should see output that looks like this:

Backslash! An in depth look at the "\" character.  
<h1>A header!</h1>
<h1>A Safe header</h1>  
{<h1>A header!</h1> <h1>A Safe header</h1> Backslash! An in depth look at the "\" character. /dashboard/settings {Fido 6} map[key:value other_key:other_value]}


<a title="Backslash! An in depth look at the "\" character.">  
<a title="<h1>A header!</h1>">

<a href="%3ch1%3eA%20header!%3c/h1%3e">  
<a href="?q=%3ch1%3eA%20header%21%3c%2fh1%3e">  
<a href="/dashboard/settings">  
<a href="?q=%2fdashboard%2fsettings">


<script>  
  var dog = {"Name":"Fido","Age":6};
  var map = {"key":"value","other_key":"other_value"};
  doWork("Backslash! An in depth look at the \"\\\" character.");
</script>

There is a lot going on here, so lets take a minute to look it over.

Backslash! An in depth look at the "\" character.  
<h1>A header!</h1>
<h1>A Safe header</h1>  
{<h1>A header!</h1> <h1>A Safe header</h1> Backslash! An in depth look at the "\" character. /dashboard/settings {Fido 6} map[key:value other_key:other_value]}

In the first few lines (show above) we are encoding values inside of the HTML context. As a result, html/template encodes any characters that need encoded to be rendered correctly. Specifically, the < and > characters get encoded.

On the third line we are outputting a value of the type template.HTML , which is how you tell the html/template package that the string is safe to skip encoding. This means that you should NOT use this type when dealing with user input, as it could lead to code injection.

It is also worth noting that in this context a struct will just be encoded into its default string value, but nested objects will be encoded.

<a title="Backslash! An in depth look at the "\" character.">  
<a title="<h1>A header!</h1>">

The next 2 lines are anchor tags that show how values are encoded when put inside of an attribute like title . This is here mostly to demonstrate that the html/template package is aware of the attribute, and you will see in the next few lines that when values are inside of the href attribute they are encoded differently.

<a href="%3ch1%3eA%20header!%3c/h1%3e">  
<a href="?q=%3ch1%3eA%20header%21%3c%2fh1%3e">  
<a href="/dashboard/settings">  
<a href="?q=%2fdashboard%2fsettings">

The next 4 lines demonstrate values being encoded as a query parameter or as a raw path in an href attribute. There are two examples here because I wanted to demonstrate that different values are encoded here than in the HTML context, but the two have differences. For example, query paramters have the forward slash ( / ) character encoded, but when the value is inserted as the path the slash is not encoded.

Next up we have the comment line (look in context.gohtml for it). html/template handles removing any HTML comments, so you won't see this in the output.

If you NEED your comments to be shipped with your final code (eg for ie compatibility) there are techniques to handle this. If you need a code snippet for this get in touch -jon@calhoun.io - and I will write a blog post and link to it here.

<script>  
  var dog = {"Name":"Fido","Age":6};
  var map = {"key":"value","other_key":"other_value"};
  doWork("Backslash! An in depth look at the \"\\\" character.");
</script>

Finally we have the JavaScript. This is, in my opinion, the coolest section. While most template libraries don't do anything too fancy here, Go's html/template does an amazing job of determining the proper context and what you likely intended. For example, in the first two lines the struct and map will be expanded into JSON objects, which is very likely what anyone would intend, and in the last example we are inserting a string, but since it isn't already wrapped in quotes the template library will also include those so that you don't instead end up with doWork(something without quotes) .

Up Next

This post should have given you a pretty good overview of what sort of contexts the html/template library can handle. In the next post we will spend more time focusing on how to build simple templates and build up to more complex templates using actions and functions like if , and , index and adding custom functions to your templates.

Finally, in the third post in this series we will go over how to use templates to create the view layer of a web application, including how to create and reuse templated layouts where the layout is the same for every view, but the content in the page changes, or uses a default value.





About List