An Intro to Templates in Go - Contextual Encoding

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 it is contextual, meaning that it can happen inside of HTML, CSS, JavaScript, or even in URLs and the template library will determine how to properly encode the text.

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 wherever you plan to write your Go code and store your templates. The path should look something like this: $GOPATH/src/???/ (the ??? can be whatever you want).

We are going to name our files with the .gohtml extension because it is commonly used by editors to indicate that you want Go HTML template syntax highlighting. Both Atom and Sublime Text have Go plugins that recognize this extension by default. That said, you can use .html or any other extension you want.

Create the files hello.gohtml and main.go, then add the following code to hello.gohtml:

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

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Backslash! An in depth look at the &#34;\&#34; character.
&lt;h1&gt;A header!&lt;/h1&gt;
<h1>A Safe header</h1>
{&lt;h1&gt;A header!&lt;/h1&gt; &lt;h1&gt;A Safe header&lt;/h1&gt; Backslash! An in depth look at the &#34;\&#34; character. /dashboard/settings {Fido 6} map[key:value other_key:other_value]}

<a title="Backslash! An in depth look at the &#34;\&#34; character.">
<a title="&lt;h1&gt;A header!&lt;/h1&gt;">

<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. We’ll start with the first four lines.

1
2
3
4
Backslash! An in depth look at the &#34;\&#34; character.
&lt;h1&gt;A header!&lt;/h1&gt;
<h1>A Safe header</h1>
{&lt;h1&gt;A header!&lt;/h1&gt; &lt;h1&gt;A Safe header&lt;/h1&gt; Backslash! An in depth look at the &#34;\&#34; character. /dashboard/settings {Fido 6} map[key:value other_key:other_value]}

In these first few lines 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.

The next two lines (6-7) 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.

6
7
<a title="Backslash! An in depth look at the &#34;\&#34; character.">
<a title="&lt;h1&gt;A header!&lt;/h1&gt;">

Lines 9 through 12 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.

9
10
11
12
<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">

Next up we would expect the comment line (look in context.gohtml for it), but as you can see the html/template package handles removing any HTML comments for us.

Comments being removed is useful most of the time, but sometimes you want comments to persist. If you would like to see how that is done, I suggest you check out the HTML safe strings and HTML comments section of the third article, Using Functions Inside Go Templates. In that section we discuss ways to preserve comments in your HTML templates.

Finally we have the JavaScript code, lines 14 throuhg 18. 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 a developer would intend to do. In the last example we are inserting a string, and as you can see this was also encoded into it’s JSON equivalent which is just a string with quotes around it. As a result, we don’t have to wrap the variable in quotes like doWork("{{.Title}}").

14
15
16
17
18
<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>

Up Next…

This post should have given you a pretty good overview of the different contexts the html/template package can handle, as well as how to use variables inside your templates. In future posts we are going to expand on this as we learn to use more features of the template library in Go.

In the next post, Template Actions and Nested Templates in Go, we will start with simple templates and build our way up to more complex ones using actions. The third post, Using Functions Inside Go Templates, looks at how to use both built-in and custom functions inside of your go templates. The fourth and final post, Creating the V in MVC, will put much of what we learned together with new concepts in order to build reusable views that can simplify the way the rest of your application renders HTML pages. Even if you don’t use MVC, this is likely to be an insightful read.

Want to see how templates work in the bigger picture?

In my course - Web Development with Go - we use the html/template package to build out an entire view layer for a realistic application. If you have ever wondered how all of these pieces fit together in the scope of a complete web application, I suggest you check out the course.

If you sign up for my mailing list (down there ↓) I'll send you a FREE sample so you can see if it is for you. The sample includes over 2.5 hours of screencasts and the first few chapters from the book.

You will also receive notifications when I release new articles, updates on upcoming courses (including FREE ones), and I'll let you know when my paid courses are on sale.

Avatar of Jon Calhoun
Written by
Jon Calhoun

Jon Calhoun is a full stack web developer who teaches about Go, web development, algorithms, and anything programming. If you haven't already, you should totally check out his Go courses.

Previously, Jon worked at several statups including co-founding EasyPost, a shipping API used by several fortune 500 companies. Prior to that Jon worked at Google, competed at world finals in programming competitions, and has been programming since he was a child.

More in this series

This post is part of the series, An Introduction to Templates in Go.

Spread the word

Did you find this page helpful? Let others know about it!

Sharing helps me continue to create both free and premium Go resources.

Want to discuss the article?

See something that is wrong, think this article could be improved, or just want to say thanks? I'd love to hear what you have to say!

You can reach me via email or via twitter.

Recent Articles All Articles Mini-Series Progress Updates Tags About Me Go Courses

©2024 Jonathan Calhoun. All rights reserved.