Table of Contents
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 astruct
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
implementsio.Writer
. -
Execute
applies a parsed template to the specified data object, and writes the output toos.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
andnet/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 ashttp.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 theFileSystem
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.
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!!