How do I use the template package and handle forms?
How do I use the template package and handle forms?
Satish Talim
Buy on Leanpub

Preface

Who is the book for?

Are you new to Go and taking part in the Go Challenge? Some of the challenges might require you to use the template package and handle forms in Go. Don’t know how? If yes, then this eBook is for you and will show you how it’s done.

What will you learn?

In the end, you will understand and know how to use the template package and handle forms.

Using Code Examples

All of the code in this book can be used pretty much anywhere and anyhow you please.

How to Contact Me

I can be reached via e-mail at golangchallenge@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the eBook. Constructive criticism is definitely appreciated; I want this eBook to get better through your feedback.

Thanks

Thanks for downloading and checking out this eBook. As part of the lean publishing philosophy, you’ll be able to interact with me as the eBook is completed. I’ll be able to change things, reorganize parts, and generally make a better eBook. I hope you enjoy.

Package template

text/template

Usage: import "text/template"

Most server-side languages have a mechanism for taking predominantly static pages and inserting a dynamically generated component, such as a list of items. Typical examples are scripts in Java Server Pages, PHP scripting and many others. Go has adopted a relatively simple scripting language in the template package.

The package is designed to take text as input and output different text, based on transforming the original text using the values of an object.

To generate HTML output, we shall soon use package html/template, which has the same interface as this package but automatically secures HTML output against certain attacks.

The original source is called a template and will consist of text that is transmitted unchanged, and embedded commands which can act on and change text. The commands are delimited by {{ ... }}, similar to the JSP commands <%= ... =%> and PHPs <?php ... ?>.

A template is applied to a Go object. Fields from that Go object can be inserted into the template, and you can “dig” into the object to find sub-fields, etc. The current object is represented as ., so that to insert the value of the current object as a string, you use {{.}}. The package uses the fmt package by default to work out the string used as inserted values.

To insert the value of a field of the current object, you use the field name prefixed by .. For example, if the object is of type:

1 type Student struct {
2         Name string
3 }

then you insert the values of Name by The name is {{.Name}}.

Thus, templates are a way to merge generic text with more specific text i.e. retain the content that is common in the template and then substitute the specific content as required.

The syntax of such definitions is to surround each template declaration with a “define” and “end” action.

The define action names the template being created by providing a string constant. Here is a simple example:

1 {{define "T1"}}ONE{{end}}
2 {{define "T2"}}TWO{{end}}
3 {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
4 {{template "T3"}}

This defines two templates, T1 and T2, and a third T3 that invokes the other two when it is executed. Finally it invokes T3. If executed this template will produce the text

1 ONE TWO

In Go, we use the template package and methods like Parse, ParseFile, Execute to load a template from a string or file and then perform the merge. The content to merge is within a defined type and that has exported fields, i.e. fields within the struct that are used within the template have to start with a capital letter.

Let us look at a simple example.

Make a new folder and cd to it as follows:

1 $ mkdir $GOPATH/src/github.com/SatishTalim/texttmpl
2 $ cd $GOPATH/src/github.com/SatishTalim/texttmpl

In this folder write the program stud_struct.go as follows:

Program stud_struct.go


 1 package main
 2 
 3 import (
 4         "log"
 5         "os"
 6         "text/template"
 7 )
 8 
 9 type Student struct {
10         //exported field since it begins
11         //with a capital letter
12         Name string
13 }
14 
15 func main() {
16         //define an instance
17         s := Student{"Satish"}
18 
19         //create a new template with some name
20         tmpl := template.New("test")
21 
22         //parse some content and generate a template
23         tmpl, err := tmpl.Parse("Hello {{.Name}}!")
24         if err != nil {
25                 log.Fatal("Parse: ", err)
26                 return
27         }
28 
29         //merge template 'tmpl' with content of 's'
30         err1 := tmpl.Execute(os.Stdout, s)
31         if err1 != nil {
32                 log.Fatal("Execute: ", err1)
33                 return
34         }
35 }

You can now run the program by typing:

1 $ go run stud_struct.go

The output is:

1 Hello Satish!

Note:

  • New allocates a new template with the given name.
  • Parse parses a string into a template.
  • To include the content of a field within a template, enclose it within curly braces and add a dot at the beginning. E.g. if Name is a field within a struct and its value needs to be substituted while merging, then include the text {{.Name}} in the template. Do remember that the field name has to be present and it should also be exported (i.e. it should begin with a capital letter in the type definition), or there could be errors. All text outside {{.Name}} is copied to the output unchanged.
  • We have used the predefined variable os.Stdout which refers to the standard output to print out the merged data - os.Stdout implements io.Writer.
  • Execute applies a parsed template to the specified data object, and writes the output to os.Stdout.

Pipelines

A pipeline may be “chained” by separating a sequence of commands with pipeline characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline.

The output of a command will be either one value or two values, the second of which has type error. If that second value is present and evaluates to non-nil, execution terminates and the error is returned to the caller of Execute.

Let us look at an example.

Make a new folder and cd to it as follows:

1 $ mkdir $GOPATH/src/github.com/SatishTalim/person
2 $ cd $GOPATH/src/github.com/SatishTalim/person

In this folder write the program person.go as follows:

Program person.go


 1 package main
 2 
 3 import (
 4         "log"
 5         "os"
 6         "text/template"
 7 )
 8 
 9 type Person struct {
10         Name   string
11         Emails []string
12 }
13 
14 const tmpl = `The name is {{.Name}}.
15 {{range .Emails}}
16     His email id is {{.}}
17 {{end}}
18 `
19 
20 func main() {
21         person := Person{
22                 Name:   "Satish",
23                 Emails: []string{"satish@rubylearning.org", "satishtalim@gmail.com"},
24         }
25 
26         t := template.New("Person template")
27 
28         t, err := t.Parse(tmpl)
29         if err != nil {
30                 log.Fatal("Parse: ", err)
31                 return
32         }
33 
34         err = t.Execute(os.Stdout, person)
35         if err != nil {
36                 log.Fatal("Execute: ", err)
37                 return
38         }
39 }

You can now run the program by typing:

1 $ go run person.go

The output is:

1 The name is Satish.
2 
3     His email id is satish@rubylearning.org
4 
5     His email id is satishtalim@gmail.com

In the above program we have {{range .Emails}}. With range the current object . is set to the successive elements of the array or slice Emails.

Variables

The template package allows you to define and use variables. In the above example, how would we print each person’s email address prefixed by their name? Let’s modify the above program.

In the code snippet:

1 {{range .Emails}}
2     {{.}}
3 {{end}}

We cannot access the Name field as . is now traversing the array elements and the Name is outside of this scope. The solution is to save the value of the Name field in a variable that can be accessed anywhere in its scope. Variables in templates are prefixed by $. So we write:

1 {{$name := .Name}}
2 {{range .Emails}}
3     Name is {{$name}}, email is {{.}}
4 {{end}}

The modified program, named new_person.go is:

Program new_person.go


 1 package main
 2 
 3 import (
 4         "log"
 5         "os"
 6         "text/template"
 7 )
 8 
 9 type Person struct {
10         Name   string
11         Emails []string
12 }
13 
14 const tmpl = `{{$name := .Name}}
15 {{range .Emails}}
16     Name is {{$name}}, email is {{.}}
17 {{end}}
18 `
19 
20 func main() {
21         person := Person{
22                 Name:   "Satish",
23                 Emails: []string{"satish@rubylearning.org", "satishtalim@gmail.com"},
24         }
25 
26         t := template.New("Person template")
27 
28         t, err := t.Parse(tmpl)
29         if err != nil {
30                 log.Fatal("Parse: ", err)
31                 return
32         }
33 
34         err = t.Execute(os.Stdout, person)
35         if err != nil {
36                 log.Fatal("Execute: ", err)
37                 return
38         }
39 }

You can now run the program by typing:

1 $ go run new_person.go

The output is:

1     Name is Satish, email is satish@rubylearning.org
2 
3     Name is Satish, email is satishtalim@gmail.com

The Go template package is useful for certain kinds of text transformations involving inserting values of objects. It does not have the power of, say, regular expressions, but is faster and in many cases will be easier to use than regular expressions.

A Static Site with Go

Let’s build a web app in Go to display a static site, which we call “Dosa Diner”.

Dosa is a fermented crepe or pancake made from rice batter and black lentils. This staple dish is widely popular in all southern Indian states Karnataka, Andhra Pradesh, Tamil Nadu and Kerala, as well as being popular in other countries like Sri Lanka, Malaysia and Singapore.

Start by creating the folders to hold the project:

DosaSite folder structure


 1 c:\go_projects
 2 \---go
 3     \---src
 4         \---github.com
 5             \---SatishTalim
 6                 \---dosasite
 7                     |   dosasite.go
 8                     |   
 9                     \---public
10                         |   index.html
11                         |   
12                         +---images
13                         |       dosa.jpg
14                         |       
15                         \---stylesheets
16                                 dosasite.css

We will write our Go code in the file dosasite.go and some sample HTML and CSS files in a public folder.

File index.html


 1 <!DOCTYPE html>
 2 <html>
 3 
 4   <head>
 5     <title>Dosa Diner</title>
 6     <meta charset="utf-8">
 7     <link rel="stylesheet" href="stylesheets/dosasite.css">
 8   </head>
 9 
10   <body>
11   <h1><img src="images/dosa.jpg" alt="Dosa Diner" />Dosa Diner</h1>
12 
13   <h2>The Restaurant</h2>
14   <p>The Dosa Diner offers casual lunch and dinner fare in a hip atmosphere.
15      The menu changes regularly to highlight the freshest ingredients.</p>
16 
17   <h2>Catering Services</h2>
18   <p>You have fun... we'll do the cooking. Dosa Diner Catering can handle events
19      from snacks for bridge club to elegant corporate fundraisers.</p>
20 
21   <h2>Location and Hours</h2>
22   <p>Deccan Corner in Pune, India;
23   Monday through Thursday 11am to 9pm, Friday and Saturday, 11am to midnight.</p>
24   </body>
25 
26 </html>

File dosasite.css


 1 body {
 2   background-color: #C2A7F2;
 3   font-family: sans-serif;
 4 }
 5 h1 {
 6   color: #2A1959;
 7   border-bottom: 2px solid #2A1959;
 8 }
 9 h2 {
10   color: #474B94;
11   font-size: 1.2em;
12 }
13 h2, p {
14   margin-left: 120px;
15 }

Once the above files are created, the code we need to get up and running is quite compact:

Program dosasite.go


 1 package main
 2 
 3 import (
 4         "fmt"
 5         "log"
 6         "net/http"
 7         "os"
 8 )
 9 
10 func main() {
11         fs := http.FileServer(http.Dir("public"))
12         http.Handle("/", fs)
13 
14         fmt.Println("Listening...")
15         err := http.ListenAndServe(GetPort(), nil)
16         if err != nil {
17                 log.Fatal("ListenAndServe: ", err)
18                 return
19         }
20 }
21 
22 // Get the Port from the environment so we can run on Heroku (more of this later)
23 func GetPort() string {
24         var port = os.Getenv("PORT")
25 	// Set a default port if there is nothing in the environment
26 	if port == "" {
27 		port = "4747"
28 		fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
29 	}
30 	return ":" + port
31 }

  • like all Go programs that need to be executed, our program has a package main.
  • we import the fmt and net/http packages from the Go standard library.
  • to work with some printing functions, we import the package fmt.
  • for web related http functionality, we import the package http. Any functions within that we refer as http.function_name.
  • we use the FileServer function to create a handler that responds to HTTP requests with the contents of a given FileSystem.
  • we use the operating system’s file system implementation by http.Dir
  • we’ve used the public folder relative to our application, but you could use any other folder on your system (or indeed anything that implements the FileSystem interface).
  • we use the Handle function to register it as the handler for all requests, and launch the server listening on port 4747.

Now you can run program with the go tool:

1 $ cd $GOPATH/src/github.com/SatishTalim/dosasite
2 $ go run dosasite.go

Now open http://localhost:4747/index.html in your browser. You should see the HTML page we have made.

DosaSite

DosaSite

html/template

Usage: import "html/template"

Package template html/template implements data-driven templates for generating HTML output safe against code injection. It provides the same interface as package text/template and should be used instead of text/template whenever the output is HTML.

Modify dosasite.go to use templates

Previously we had written the program dosasite.go where in the program, all requests are being handled by our file server. Let’s make a slight adjustment so that it only handles request paths that begin with the pattern /public/ instead.

1 ...
2 fs := http.FileServer(http.Dir("public"))
3 http.Handle("/public/", http.StripPrefix("/public/", fs))
4 ...

The function StripPrefix returns a handler that serves HTTP requests by removing the given prefix from the request URL’s Path and invoking the handler fs. StripPrefix handles a request for a path that doesn’t begin with prefix by replying with an HTTP 404 not found error.

Now you can run program with the go tool:

1 $ cd $GOPATH/src/github.com/SatishTalim/dosasite
2 $ go run dosasite.go

Now open http://localhost:4747/public/index.html in your browser. You should see the HTML page we have made.

Next, create a templates folder as shown below, containing a layout.html file with shared markup, and an indexnew.html file with some page-specific content.

DosaSite modified folder structure


 1 c:\go_projects
 2 \---go
 3     \---src
 4         \---github.com
 5             \---SatishTalim
 6                 \---dosasite
 7                     |   dosasite.go
 8                     |   
 9                     \---public
10                     |   |   
11                     |   +---images
12                     |   |       dosa.jpg
13                     |   |       
14                     |   \---stylesheets
15                     |           dosasite.css
16                     \---templates
17                         indexnew.html
18                         layout.html

Program layout.html


 1 {{define "layout"}}
 2 <!doctype html>
 3 <html>
 4 <head>
 5   <meta charset="utf-8">
 6   <title>{{template "title"}}</title>
 7   <link rel="stylesheet" href="/public/stylesheets/dosasite.css">
 8 </head>
 9 
10 <body>
11   {{template "body"}}
12 </body>
13 </html>
14 {{end}}

Program indexnew.html


 1 {{define "title"}}Dosa Diner{{end}}
 2 
 3 {{define "body"}}
 4   <h1><img src="public/images/dosa.jpg" alt="Dosa Diner" />Dosa Diner</h1>
 5 
 6   <h2>The Restaurant</h2>
 7   <p>The Dosa Diner offers casual lunch and dinner fare in a hip atmosphere.
 8      The menu changes regularly to highlight the freshest ingredients.</p>
 9 
10   <h2>Catering Services</h2>
11   <p>You have fun... we'll do the cooking. Dosa Diner Catering can handle events
12      from snacks for bridge club to elegant corporate fundraisers.</p>
13 
14   <h2>Location and Hours</h2>
15   <p>Deccan Corner in Pune, India;
16   Monday through Thursday 11am to 9pm, Friday and Saturday, 11am to midnight.</p>
17 {{end}}

Go templates, as discussed before, are essentially just named text blocks surrounded by {{define}} and {{end}} tags. Templates can be embedded into each other, as we do above where the layout template embeds both the title and body templates.

The modified dosasite.go program is:

Program dosasite.go


 1 package main
 2 
 3 import (
 4         "fmt"
 5         "html/template"
 6         "log"
 7         "net/http"
 8         "os"
 9         "path"
10 )
11 
12 func main() {
13         fs := http.FileServer(http.Dir("public"))
14         http.Handle("/public/", http.StripPrefix("/public/", fs))
15 
16         http.HandleFunc("/", ServeTemplate)
17         
18         fmt.Println("Listening...")
19         err := http.ListenAndServe(GetPort(), nil)
20         if err != nil {
21                 log.Fatal("ListenAndServe: ", err)
22                 return
23         }
24 }
25 
26 // Get the Port from the environment so we can run on Heroku
27 func GetPort() string {
28         var port = os.Getenv("PORT")
29 	// Set a default port if there is nothing in the environment
30 	if port == "" {
31 		port = "4747"
32 		fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
33 	}
34 	return ":" + port
35 }
36 
37 func ServeTemplate(w http.ResponseWriter, r *http.Request) {
38         lp := path.Join("templates", "layout.html")
39         fp := path.Join("templates", r.URL.Path)
40 
41         // Return a 404 if the template doesn't exist
42         info, err := os.Stat(fp)
43         if err != nil {
44                 if os.IsNotExist(err) {
45                         http.NotFound(w, r)
46                         return
47                 }
48         }
49 
50         // Return a 404 if the request is for a directory
51         if info.IsDir() {
52                 http.NotFound(w, r)
53                 return
54         }
55 
56         templates, _ := template.ParseFiles(lp, fp)
57         if err != nil {
58                 fmt.Println(err)
59                 http.Error(w, "500 Internal Server Error", 500)
60                 return
61         }
62   
63         templates.ExecuteTemplate(w, "layout", nil)
64 }

In the above program, we’ve added the html/template and path packages to the import statement.

We’ve then specified that all the requests not picked up by the static file server should be handled with a new ServeTemplate function.

In the ServeTemplate function, we build paths to the layout file and the template file corresponding with the request. Rather than manual concatenation we use Join, which has the advantage of cleaning the path to help prevent directory traversal attacks.

We then use the ParseFiles function to bundle the requested template and layout into a template set. Finally, we use the ExecuteTemplate function to render a named template in the set, in our case the layout template.

Our code also has some error handling:

  • Send a 404 response if the requested template doesn’t exist.
  • Send a 404 response if the requested template path is a directory.
  • Send and print a 500 response if the template.ParseFiles function throws an error.

Special thanks to Alex Edwards whose article has been adapted for this topic.

Handling Forms

If we want users to be able to post their own messages on a webpage, we need a way to process this information submitted by the user with a web form. The Go http package makes processing form data easy.

Let us see how this is done.

Web app stringupper

The directory structure for this app is as follows:

App folder structure


 1 C:\go_projects>
 2 \---go
 3     \---src
 4         \---github.com
 5             \---SatishTalim
 6                 +---stringupper
 7                 |   |   stringupper.go
 8                 |   |
 9                 |   \---css
10                 |           upper.css

Here are the contents of the file upper.css:

upper.css


 1 body {
 2   background-color: #C2A7F2;
 3   font-family: sans-serif;
 4 }
 5 h1 {
 6   color: #2A1959;
 7   border-bottom: 2px solid #2A1959;
 8 }
 9 h2 {
10   color: #474B94;
11   font-size: 1.2em;
12 }
13 h2 {
14   margin-left: 120px;
15 }

Here is the code for the app stringupper.go:

stringupper.go


 1 package main
 2 
 3 import (
 4 	"fmt"
 5 	"html/template"
 6 	"log"
 7 	"net/http"
 8 	"os"
 9 	"strings"
10 )
11 
12 func main() {
13 	// Add a handler to handle serving static files from a specified directory
14 	// The reason for using StripPrefix is that you can change the served 
15 	// directory as you please, but keep the reference in HTML the same.
16 	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
17 	
18 	http.HandleFunc("/", root)
19 	http.HandleFunc("/upper", upper)
20         fmt.Println("listening...")
21         err := http.ListenAndServe(GetPort(), nil)
22         if err != nil {
23                 log.Fatal("ListenAndServe: ", err)
24                 return
25         }
26 }
27 
28 func root(w http.ResponseWriter, r *http.Request) {
29 	fmt.Fprint(w, rootForm)
30 }
31 
32 const rootForm = `
33   <!DOCTYPE html>
34     <html>
35       <head>
36         <meta charset="utf-8">
37         <link rel="stylesheet" href="css/upper.css">
38         <title>String Upper</title>
39       </head>
40       <body>
41         <h1>String Upper</h1>
42         <p>The String Upper Service will accept a string from you and 
43            return you the Uppercase version of the original string. Have fun!</p>
44         <form action="/upper" method="post" accept-charset="utf-8">
45 	  <input type="text" name="str" value="Type a string..." id="str">
46 	  <input type="submit" value=".. and change to uppercase!">
47         </form>
48       </body>
49     </html>
50 `
51 var upperTemplate = template.Must(template.New("upper").Parse(upperTemplateHTML))
52 
53 func upper(w http.ResponseWriter, r *http.Request) {
54         strEntered := r.FormValue("str")
55         strUpper := strings.ToUpper(strEntered)
56         err := upperTemplate.Execute(w, strUpper)
57         if err != nil {
58 	        http.Error(w, err.Error(), http.StatusInternalServerError)
59         }
60 }
61 
62 const upperTemplateHTML = ` 
63 <!DOCTYPE html>
64   <html>
65     <head>
66       <meta charset="utf-8">
67       <link rel="stylesheet" href="css/upper.css">
68       <title>String Upper Results</title>
69     </head>
70     <body>
71       <h1>String Upper Results</h1>
72       <p>The Uppercase of the string that you had entered is:</p>
73       <pre>{{html .}}</pre>
74     </body>
75   </html>
76 `
77 
78 // Get the Port from the environment so we can run on Heroku
79 func GetPort() string {
80         var port = os.Getenv("PORT")
81 	// Set a default port if there is nothing in the environment
82 	if port == "" {
83 		port = "4747"
84 		fmt.Println("INFO: No PORT environment variable detected, defaulting to " + port)
85 	}
86 	return ":" + port
87 }

This app has two handlers: the path / is mapped to root, which displays a web form for the user to enter some text as a string. The path /upper is mapped to upper, which displays to the user the text entered by him/her in uppercase.

The upper function gets the form data by calling r.FormValue and passes it to upperTemplate.Execute that writes the rendered template to the http.ResponseWriter. In the template code, the content is automatically filtered to escape HTML special characters. The automatic escaping is a property of the html/template package, as distinct from text/template.

The line {{html .}} in const upperTemplateHTML, html is a predefined global function that returns the escaped HTML equivalent of the textual representation of its arguments, . in this case.

You can refer to Handling Web Forms for more information.

You can now run the program by typing:

1 $ go run stringupper.go

Have fun!!