Webapp With Golang Anti Textbook
Webapp With Golang Anti Textbook
of Contents
Introduction
1.1
1.2
Installation
1.2.1
Tools
1.2.2
Go basic knowledge
1.3
Go foundation
1.3.1
1.3.2
struct
1.3.3
Object-oriented
1.3.4
interface
1.3.5
Concurrency
1.3.6
Summary
1.3.7
General
1.4
Go Programming Basics
1.4.1
1.4.2
Implementation
1.5
1.5.1
1.5.2
Database Handling
1.5.3
Webapp Example
1.5.4
Form handling
1.6
1.6.1
Uploading Files
1.6.2
Templates
Basics of Templates
1.7
1.7.1
User Authentication
1.8
1.9
Routing
1.10
Middleware
1.11
Building an API
1.12
Contributors
1.13
Introduction
Code
The book comes with corresponding code, please use it to understand the book completely,
the book is just plain old theory if you do not check the code out.
Contributing
I don't profess to be a God of either Go or webdev or anything in general, and I don't claim
that this is the best book for learning how to build web appplications with Go, but I do believe
that good things happen when people collaborate, so pull requests are not only appreciated,
but they are welcome.
I got feedback from a reddit user that maybe it is too early for me to start writing this book,
decades ago, a young student from the University of Helsinki had an endless debate with
Andrew Tannenbaum over comp.minix, it was about monolithic kernels, had the student
listened to Andrew Tannenbaum, the world probably would not have had Linux. This is the
whole point of open source projects, a little initiative from everyone goes a long way.
Philosophy
Through this book we want to teach how to develop web applications in Go. We expect
the reader to know the basics of Go but we assume the reader knows nothing about
how to write web applications
The book shall comprise of chapters, if the topic is huge and doesn't fit into one chapter,
then we split into multiple chapters, if possible.
4
Introduction
Each chapter should be split into logical parts or sections with a meaningful title which'll
teach the reader something.
Every concept should be accompanied by the Go code (if there is any), for sneak peek
type sections write the Go pseudo code, writing just the necessary parts of the code and
keeping everything else.
The code shouldn't be more than 80 characters wide because in the PDF versions of
the book the code is invisible.
Brevity is the soul of wit, so keep the description as small as possible. But this doesn't
mean that we should just assume that the reader knows the concept and skip it in such
cases do explain the concept.
In the todo list manager which we are creating, we'll strive to implement as much
functionality as possible to give a taste of practical Go programming to the reader, but
we should mention as a note the other way, suppose you re-implement a function like
ParseGlob by listing all html files and using ParseFiles to parse them, we should
License:
Book License: CC BY-SA 3.0 License
Note:
1. The Go Programming Basics section has been adapted from build-web-application-withgolang by astaxie Links were updated to refer the correct aspects of the current book,
titles were updated to fit into this book.
2. The gopher in the cover page is taken from
https://golang.org/doc/gopher/appenginegophercolor.jpg without modifications.
Links
Next section: Installation and Tools
Introduction
Installation
If you know about installation or have installed Go, you can skip to Tools.
This chapter is taken from install page verbatim, except for the changes to be made to adapt
to this book's styling format.
System requirements
Go binary distributions are available for these supported operating systems and
architectures. Please ensure your system meets these requirements before proceeding. If
your OS or architecture is not on the list, you may be able to install from source or use gccgo
instead
Operating system
Architectures
Notes
FreeBSD 8-STABLE
or later
amd64
amd64, 386,
arm
Mac OS X 10.7 or
later
amd64
Windows XP or later
amd64, 386
Choose the archive file appropriate for your installation. For instance, if you are installing Go
version 1.2.1 for 64-bit x86 on Linux, the archive you want is called go1.2.1.linuxamd64.tar.gz.
If you chose a directory other than c:\Go , you must set the GOROOT environment variable
to your chosen path.
Add the bin subdirectory of your Go root (for example, c:\Go\bin ) to your PATH
environment variable. Setting environment variables under Windows
Under Windows, you may set environment variables through the "Environment Variables"
button on the "Advanced" tab of the "System" control panel. Some versions of Windows
provide this control panel through the "Advanced System Settings" option inside the
"System" control panel. Test your installation
Check that Go is installed correctly by setting up a workspace and building a simple
program, as follows.
Create a directory to contain your workspace, $HOME/work for example, and set the
GOPATH environment variable to point to that location.
$ export GOPATH=$HOME/work
You should put the above command in your shell startup script ( $HOME/.profile for
example) or, if you use Windows, follow the instructions above to set the GOPATH
environment variable on your system.
Next, make the directories src/github.com/user/hello inside your workspace (if you use
GitHub, substitute your user name for user), and inside the hello directory create a file
named hello.go with the following contents:
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
The above command will put an executable command named hello (or hello.exe) inside the
bin directory of your workspace. Execute the command to see the greeting:
$ $GOPATH/bin/hello
hello, world
If you see the "hello, world" message then your Go installation is working.
Before rushing off to write Go code please read the How to Write Go Code document, which
describes some essential concepts about using the Go tools. Uninstalling Go
To remove an existing Go installation from your system delete the go directory. This is
usually /usr/local/go under Linux, Mac OS X, and FreeBSD or c:\Go under Windows.
You should also remove the Go bin directory from your PATH environment variable. Under
Linux and FreeBSD you should edit /etc/profile or $HOME/.profile . If you installed Go
with the Mac OS X package then you should remove the /etc/paths.d/go file. Windows
users should read the section about setting environment variables under Windows. Getting
help
For real-time help, ask the helpful gophers in #go-nuts on the Freenode IRC server.
The official mailing list for discussion of the Go language is Go Nuts.
Report bugs using the Go issue tracker.
10
Tools
Tools
For html: Brackets, a text editor for the web by Adobe.
For Go: Any IDE of your choice which has a Go language plugin.
gofmt
Usage:
gofmt <file name> : prints the formatted source code on the console
gofmt -w <file name/folder name>: writes the formatted code inside the file(s).
gofmt formats the source code of a Go source file/files in a folder. The basic point behind the
Go language is that they have standardized formatting. The language authors wanted to
create a language that gets things done quickly and didn't want the users of the language to
wage in the endless debate on trivial issues like the code formatting. Most IDEs can be
configured to run gofmt on save. It is recommended to run gofmt before committing to
version control.
godoc
Documentation in Go, is done via comments, each exported function/variable is supposed to
have respective comments on what it does. The command godoc is the standard
documentation generation tool. It extracts documentation comments on all the Go projects
present in $GOPATH/src . It's a good practice to give proper documentation while
programming, to make the code easier to understand for newcomers, for closed source and
open source projects alike.
Note:
godoc, by default runs on the entire $GOPATH, so depending on the projects you have in
your $GOPATH, it might take from few seconds to few minutes for godoc to start the server,
godoc doesn't notify you when the server has started, they do have a verbose flag which
prints when the server has started. It is -v
There are two modes for godoc,
Web interface: Usage: godoc -http=:6060 -v
For seeing documentation of net/http the link is localhost:6060/pkg/net/http
11
Tools
go test
This is the testing toolchain for Go. For each file.go , the corresponding test cases should
be present in a file named as file_test.go. If main.go is our Go source file, we should make
main_test.go file in the same folder as main.go. The Go compiler ignores all the _test.go
files.
go build
We use this command to do to build our application. It parses all the .go files except the
_test.go files in the entire folder and all sub folders along with imported libraries if any, and
creates a statically linked binary. The binary name is the same as the project folder name, if
we want a custom name we should use the -o flag.
Example: go build -o tasks
Note Cross compilation
With Go, you can cross compile your application. Below is the code to compile the
application for Windows and Mac from Linux.
env GOOS=darwin GOARCH=386 go build -o tasks.app
env GOOS=windows GOARCH=386 go build -o tasks.exe
If there are no quirks on the libraries, should give you a binary for the respective platforms.
go install
This command creates a statically linked binary and places it in $GOPATH/bin folder. It also
creates a binary version of all the dependent libraries and puts it in the $GOPATH/pkg folder
in the respective directories.
Note
While building your application, first use go install which'll cache your dependent libraries.
Then for subsequent changes use go build , this will save a lot of build time. This is
because go build doesn't cache any results, it builds everything.
go run
12
Tools
While running your app through the command line you typicall have to do the following: go
build -o app ./app
go run combines them into one command, it generates and runs a binary of your project.
go get
This is used to install packages in Go. It internally clones the version control repository
parameter passed to it, can be any local/remote git repository. It then runs go install on
the library, making the library available in $GOPATH/pkg.
13
Go basic knowledge
14
Go basic knowledge
of line, this is the reason you can't write functions like the second way, we mentioned in
the above section
3. All your Go code is present in a single folder, $GOPATH, it is called. Say goodbye to
your code thrown all around in your machine
4. gofmt will format your code, so there is one standard way to write Go code.
5. Built in http/testing support
6. Compiled language, thus very fast.
7. Can write webapps without any frameworks.
2.1 Hello, Go
Let's start with a simple example, the customary Hello World.
Program
package main
import "fmt"
func main() {
fmt.Printf("Hello, world or or or \n")
}
Explanation
Go programs use packages, which are same as libraries in other languages. Main is a
special package in Go, when the compiler starts compiling the source code, it starts with the
main package.
package <pkgName> (In this case is package main ) tells us this source file belongs to main
package, and the keyword main tells us this package will be compiled to a program instead
of package files whose extensions are .a .
15
Go basic knowledge
Per executable program, there can be only one main package, and one main function with
no arguments passed or returned. The Printf function, is imported from the format package
called fmt , we import a package using import "fmt" . When calling a function name or
referring to a variable inside another package, we use pkgName.FunctionName like
fmt.Println() .
This will function correctly, because we are in the Tasks directory while executing our binary,
all the templates and other files are present in this folder.
Without the main folder
[Tasks/main] $ go build main.go
[Tasks/main] $ ./main
Here, we are in the Tasks/main directory, the binary will expect all the other files in the
Tasks/main directory when they are in the Tasks directory,
16
Go foundation
2.2 Go foundation
Define variables
We use the keyword var to define a variable. Note that in Go the variable type comes after
the variable name.
// define a variable with name variableName and type "type"
var variableName type
// define three variables which types are "type"
var vname1, vname2, vname3 type
// define a variable with name variableName, type "type" and value "value"
var variableName type = value
/*
Define three variables with type "type", and initialize their values.
vname1 is v1, vname2 is v2, vname3 is v3
*/
var vname1, vname2, vname3 type = v1, v2, v3
:= can only be used inside functions, for defining global variables we have to stick to using
var.
_ variable is called the blank variable and it is used to ignore a value. This is a useless
Unused variables cause compilation errors. Compile the following code and see what
happens.
17
Go foundation
package main
func main() {
var i int
}
Constants
Constants are the values that are determined during compile time and you cannot change
them during runtime. In Go, you can use number, boolean or string as types of constants.
Define constants as follows.
const constantName = value
// you can assign type of constants if it's necessary
const Pi float32 = 3.1415926
More examples.
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
Elementary types
Boolean
We use bool to define a variable as boolean type, the value can only be true or false ,
and false will be the default value. ( You cannot convert variables' type between
number and boolean! )
// sample code
var isActive bool // global variable
var enabled, disabled = true, false // omit type of variables
func test() {
var available bool // local variable
valid := false // brief statement of variable
available = true // assign value to variable
}
18
Go foundation
Numerical types
Integer types include both signed and unsigned integer types. Go has int and uint at the
same time, they have same length, but specific length depends on your operating system.
They use 32-bit in 32-bit operating systems, and 64-bit in 64-bit operating systems. Go also
has types that have specific length including rune , int8 , int16 , int32 , int64 , byte ,
uint8 , uint16 , uint32 , uint64 . Note that rune is alias of int32 and byte is alias of
uint8 .
One important thing you should know that you cannot assign values between these types,
this operation will cause compile errors.
var a int8
var b int32
c := a + b
Although int32 has a longer length than int8, and has the same type as int, you cannot
assign values between them. ( c will be asserted as type int here )
Float types have the float32 and float64 types and no type called float . The latter one
is the default type if using brief statement.
Go supports complex numbers as well. complex128 (with a 64-bit real and 64-bit imaginary
part) is the default type, if you need a smaller type, there is one called complex64 (with a 32bit real and 32-bit imaginary part). Its form is RE+IMi , where RE is real part and IM is
imaginary part, the last i is the imaginary number. There is a example of complex number.
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
String
We just talked about how Go uses the UTF-8 character set. Strings are represented by
double quotes "" or backticks `` .
19
Go foundation
// sample code
var frenchHello string // basic form to define string
var emptyString string = "" // define a string with empty string
func test() {
no, yes, maybe := "no", "yes", "maybe" // brief statement
japaneseHello := "Ohaiou"
frenchHello = "Bonjour" // basic form of assign values
}
It's impossible to change string values by index. You will get errors when you compile the
following code.
var s string = "hello"
s[0] = 'c'
What if I really want to change just one character in a string? Try the following code.
s := "hello"
c := []byte(s) // convert string to []byte type
c[0] = 'c'
s2 := string(c) // convert back to string type
fmt.Printf("%s\n", s2)
and also.
s := "hello"
s = "c" + s[1:] // you cannot change string values by index, but you can get values in
stead.
fmt.Printf("%s\n", s)
20
Go foundation
Error types
Go has one error type for purpose of dealing with error messages. There is also a
package called errors to handle errors.
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
Some skills
Define by group
If you want to define multiple constants, variables or import packages, you can use the
group form.
Basic form.
import "fmt"
import "os"
const i = 100
const pi = 3.1415
const prefix = "Go_"
var i int
var pi float32
var prefix string
Group form.
21
Go foundation
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
Unless you assign the value of constant is iota , the first value of constant in the group
const() will be 0 . If following constants don't assign values explicitly, their values will be
the same as the last one. If the value of last constant is iota , the values of following
constants which are not assigned are iota also.
iota enumerate
Go has one keyword called iota , this keyword is to make enum , it begins with 0 ,
increased by 1 .
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // If there is no expression after the constants name, it uses the last express
ion,
//so it's saying w = iota implicitly. Therefore w == 3, and y and z both can omit
"= iota" as well.
)
const v = iota // once iota meets keyword `const`, it resets to `0`, so v = 0.
const (
e, f, g = iota, iota, iota // e=0,f=0,g=0 values of iota are same in one line.
)
Some rules
The reason that Go is concise because it has some default behaviors.
22
Go foundation
Any variable that begins with a capital letter means it will be exported, private otherwise.
The same rule applies for functions and constants, no public or private keyword
exists in Go.
in [n]type , n is the length of the array, type is the type of its elements. Like other
languages, we use [] to get or set element values within arrays.
var arr [10]int // an array of type [10]int
arr[0] = 42 // array is 0-based
arr[1] = 13 // assign value to element
fmt.Printf("The first element is %d\n", arr[0])
// get element value, it returns 42
fmt.Printf("The last element is %d\n", arr[9])
//it returns default value of 10th element in this array, which is 0 in this case.
Because length is a part of the array type, [3]int and [4]int are different types, so we
cannot change the length of arrays. When you use arrays as arguments, functions get their
copies instead of references! If you want to use references, you may want to use slice .
We'll talk about later.
It's possible to use := when you define arrays.
a := [3]int{1, 2, 3} // define an int array with 3 elements
b := [10]int{1, 2, 3}
// define a int array with 10 elements, of which the first three are assigned.
//The rest of them use the default value 0.
c := [...]int{4, 5, 6} // use `` to replace the length parameter and Go will calculat
e it for you.
You may want to use arrays as arrays' elements. Let's see how to do this.
23
Go foundation
// define a two-dimensional array with 2 elements, and each element has 4 elements.
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// The declaration can be written more concisely as follows.
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
slice
In many situations, the array type is not a good choice -for instance when we don't know
how long the array will be when we define it. Thus, we need a "dynamic array". This is called
slice in Go.
slice is not really a dynamic array . It's a reference type. slice points to an underlying
array whose declaration is similar to array , but doesn't need length.
// just like defining an array, but this time, we exclude the length.
var fslice []int
slice can redefine existing slices or arrays. slice uses array[i:j] to slice, where i is
the start index and j is end index, but notice that array[j] will not be sliced since the
length of the slice is j-i .
// define an array with 10 elements whose types are bytes
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// define two slices with type []byte
var a, b []byte
// 'a' points to elements from 3rd to 5th in array ar.
a = ar[2:5]
// now 'a' has elements ar[2],ar[3] and ar[4]
// 'b' is another slice of array ar
b = ar[3:5]
// now 'b' has elements ar[3] and ar[4]
24
Go foundation
Notice the differences between slice and array when you define them. We use [] to
let Go calculate length but use [] to define slice only.
Their underlying data structure.
The second index will be the length of slice if omitted, ar[n:] equals to
ar[n:len(ar)] .
You can use ar[:] to slice whole array, reasons are explained in first two statements.
More examples pertaining to slice
// define an array
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// define two slices
var aSlice, bSlice []byte
// some convenient operations
aSlice = array[:3] // equals to aSlice = array[0:3] aSlice has elements a,b,c
aSlice = array[5:] // equals to aSlice = array[5:10] aSlice has elements f,g,h,i,j
aSlice = array[:] // equals to aSlice = array[0:10] aSlice has all elements
// slice from slice
aSlice = array[3:7] // aSlice has elements d,e,f,glen=4cap=7
bSlice = aSlice[1:3] // bSlice contains aSlice[1], aSlice[2], so it has elements e,f
bSlice = aSlice[:3] // bSlice contains aSlice[0], aSlice[1], aSlice[2], so it has d,e
,f
bSlice = aSlice[0:5] // slice could be expanded in range of cap, now bSlice contains d
,e,f,g,h
bSlice = aSlice[:] // bSlice has same elements as aSlice does, which are d,e,f,g
slice is a reference type, so any changes will affect other variables pointing to the same
slice or array. For instance, in the case of aSlice and bSlice above, if you change the
value of an element in aSlice , bSlice will be changed as well.
slice is like a struct by definition and it contains 3 parts.
25
Go foundation
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
Slice_a := Array_a[2:5]
map
map behaves like a dictionary in Python. Use the form map[keyType]valueType to define it.
Let's see some code. The 'set' and 'get' values in map are similar to slice , however the
index in slice can only be of type 'int' while map can use much more than that: for
example int , string , or whatever you want. Also, they are all able to use == and != to
compare values.
// use string as the key type, int as the value type, and `make` initialize it.
var numbers map[string] int
// another way to define map
numbers := make(map[string]int)
numbers["one"] = 1 // assign value by key
numbers["ten"] = 10
numbers["three"] = 3
fmt.Println("The third number is: ", numbers["three"]) // get values
// It prints: The third number is: 3
26
Go foundation
It's quite easy to change the value through map . Simply use numbers["one"]=11 to
change the value of key one to 11 .
You can use form key:val to initialize map's values, and map has built-in methods to
check if the key exists.
Use delete to delete an element in map .
// Initialize a map
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map has two return values. For the second return value, if the key doesn't
//exist'ok' returns false. It returns true otherwise.
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // delete element with key "c"
As I said above, map is a reference type. If two map s point to same underlying data, any
change will affect both of them.
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // now the value of m["hello"] is Salut
make, new
make does memory allocation for built-in models, such as map , slice , and channel ,
value of type *T . By Go's definition, it returns a pointer which points to type T 's zerovalue.
new returns pointers.
The built-in function make(T, args) has different purposes than new(T) . make can be
used for slice , map , and channel , and returns a type T with an initial value. The reason
for doing this is because the underlying data of these three types must be initialized before
27
Go foundation
they point to them. For example, a slice contains a pointer that points to the underlying
array , length and capacity. Before these data are initialized, slice is nil , so for slice ,
map and channel , make initializes their underlying data and assigns some suitable values.
make returns non-zero values.
The following picture shows how new and make are different.
28
if x > 10 {
//when x is greater than 10
//program enters this block
fmt.Println("x is greater than 10")
} else {
//when x is smaller than 10
//program enters this block
fmt.Println("x is less than or equal to 10")
}
goto
29
Go has a goto keyword, but be careful when you use it. goto reroutes the control flow to
a previously defined label within the body of same code block.
func myFunc() {
i := 0
Here: // label ends with ":"
fmt.Println(i)
i++
goto Here // jump to label "Here"
}
for
Go does not have while, do while. Just a for , which is the most powerful control logic. It
can read data in loops and iterative operations, just like while . Like if , for doesn't
need parenthesis.
for expression1; expression2; expression3 {
//...
}
package main
import "fmt"
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("sum is equal to ", sum)
}
// Printsum is equal to 45
30
break : jumps out of the loop. If you have nested loops, use break along with labels.
2.
continue skips the current loop and starts the next one
Because Go supports multi-value returns and gives compile errors when you don't use
values that were defined, you may want to use _ to discard certain return values.
for _, v := range map{
fmt.Println("map's val:", v)
}
switch
Sometimes you may find that you are using too many if-else statements to implement
some logic, which may make it difficult to read and maintain in the future. The switch
statement solves this problem.
31
switch sExpr {
case expr1:
some instructions
case expr2:
some other instructions
case expr3:
some other instructions
default:
other code
}
The type of sExpr , expr1 , expr2 , and expr3 must be the same. switch is very flexible.
Conditions don't have to be constants and it executes from top to bottom until it matches
conditions. If there is no statement after the keyword switch , then it matches true .
i := 10
switch i {
case 1:
fmt.Println("i is equal to 1")
case 2, 3, 4:
fmt.Println("i is equal to 2, 3 or 4")
case 10:
fmt.Println("i is equal to 10")
default:
fmt.Println("All I know is that i is an integer")
}
In the fifth line, we put many values in one case , and we don't need to add the break
keyword at the end of case 's body. It will jump out of the switch body once it matched any
case. If you want to continue to matching more cases, you need to use the fallthrough
statement.
32
integer := 6
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("default case")
}
Functions
Use the func keyword to define a function.
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
// function body
// multi-value return
return value1, value2
}
33
In the above example, there are two arguments in the function max , their types are both
int so the first type can be omitted. For instance, a, b int instead of a int, b int . The
same rules apply for additional arguments. Notice here that max only has one return value,
so we only need to write the type of its return value -this is the short form of writing it.
Multi-value return
34
package main
import "fmt"
// return results of A + B and A * B
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
func main() {
x := 3
y := 4
xPLUSy, xTIMESy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
The above example returns two values without names -you have the option of naming them
also. If we named the return values, we would just need to use return to return the values
since they are initialized in the function automatically. Notice that if your functions are going
to be used outside of the package, which means your function names start with a capital
letter, you'd better write complete statements for return ; it makes your code more
readable.
func SumAndProduct(A, B int) (add int, multiplied int) {
add = A+B
multiplied = A*B
return
}
Variadic functions
Go supports functions with a variable number of arguments. These functions are called
"variadic", which means the function allows an uncertain numbers of arguments.
func myfunc(arg ...int) {}
arg int tells Go that this is a function that has variable arguments. Notice that these
arguments are type int . In the body of function, the arg becomes a slice of int .
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
35
Even though we called add1 with x , the origin value of x doesn't change.
The reason is very simple: when we called add1 , we gave a copy of x to it, not the x
itself.
Now you may ask how I can pass the real x to the function.
We need use pointers here. We know variables are stored in memory and they have some
memory addresses. So, if we want to change the value of a variable, we must change its
memory address. Therefore the function add1 has to know the memory address of x in
order to change its value. Here we pass &x to the function, and change the argument's
type to the pointer type *int . Be aware that we pass a copy of the pointer, not copy of
value.
36
package main
import "fmt"
// simple function to add 1 to a
func add1(a *int) int {
*a = *a+1 // we changed value of a
return *a // return new value of a
}
func main() {
x := 3
fmt.Println("x = ", x) // should print "x = 3"
x1 := add1(&x) // call add1(&x) pass memory address of x
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
fmt.Println("x = ", x) // should print "x = 4"
}
Now we can change the value of x in the functions. Why do we use pointers? What are the
advantages?
Allows us to use more functions to operate on one variable.
Low cost by passing memory addresses (8 bytes), copy is not an efficient way, both in
terms of time and space, to pass variables.
string , slice and map are reference types, so they use pointers when passing to
functions by default. (Attention: If you need to change the length of slice , you have to
pass pointers explicitly)
defer
Go has a well designed keyword called defer . You can have many defer statements in
one function; they will execute in reverse order when the program executes to the end of
functions. In the case where the program opens some resource files, these files would have
to be closed before the function can return with errors. Let's see some examples.
37
We saw some code being repeated several times. defer solves this problem very well. It
doesn't only help you to write clean code but also makes your code more readable.
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
If there are more than one defer s, they will execute by reverse order. The following
example will print 4 3 2 1 0 .
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
38
What's the advantage of this feature? The answer is that it allows us to pass functions as
values.
package main
import "fmt"
type testInt func(int) bool // define a function type of variable
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// pass the function `f` as an argument to another function
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // use function as values
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even)
}
It's very useful when we use interfaces. As you can see testInt is a variable that has a
function as type and the returned values and arguments of filter are the same as those
of testInt . Therefore, we can have complex logic in our programs, while maintaining
39
When a function F calls panic , F will not continue executing but its defer functions will
continue to execute. Then F goes back to the break point which caused the panic status.
The program will not terminate until all of these functions return with panic to the first level of
that goroutine . panic can be produced by calling panic in the program, and some errors
also cause panic like array access out of bounds errors.
Recover is a built-in function to recover goroutine s from panic status. Calling recover in
defer functions is useful because normal functions will not be executed when the program
is in the panic status. It catches panic values if the program is in the panic status, and it
gets nil if the program is not in panic status.
The following example shows how to use panic .
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
}
40
Go has two retentions which are called main and init , where init can be used in all
packages and main can only be used in the main package. These two functions are not
able to have arguments or return values. Even though we can write many init functions in
one package, I strongly recommend writing only one init function for each package.
Go programs will call init() and main() automatically, so you don't need to call them by
yourself. For every package, the init function is optional, but package main has one and
only one main function.
Programs initialize and begin execution from the main package. If the main package
imports other packages, they will be imported in the compile time. If one package is imported
many times, it will be only compiled once. After importing packages, programs will initialize
the constants and variables within the imported packages, then execute the init function if
it exists, and so on. After all the other packages are initialized, programs will initialize
constants and variables in the main package, then execute the init function inside the
package if it exists. The following figure shows the process.
import
We use import very often in Go programs as follows.
import(
"fmt"
)
41
1. Dot operator. Sometime we see people use following way to import packages.
import(
. "fmt"
)
The dot operator means you can omit the package name when you call functions inside
of that package. Now fmt.Printf("Hello world") becomes to Printf("Hello world") .
2. Alias operation. It changes the name of the package that we imported when we call
functions that belong to that package.
import(
f "fmt"
)
explaining it to you.
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
The _ operator actually means we just want to import that package and execute its
init function, and we are not sure if want to use the functions belonging to that
package.
42
struct
2.4 struct
struct
We can define new types of containers of other properties or fields in Go just like in other
programming languages. For example, we can create a type called person to represent a
person, with fields name and age. We call this kind of type a struct .
type person struct {
name string
age int
}
43
struct
44
struct
45
struct
46
struct
In the above example, we can see that all types can be embedded fields and we can use
functions to operate on them.
There is one more problem however. If Human has a field called phone and Student has a
field with same name, what should we do?
Go use a very simple way to solve it. The outer fields get upper access levels, which means
when you access student.phone , we will get the field called phone in student, not the one in
the Human struct. This feature can be simply seen as field overload ing.
package main
import "fmt"
type Human struct {
name string
age int
phone string // Human has phone field
}
type Employee struct {
Human // embedded field Human
specialty string
phone string // phone in employee
}
func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"},
"Designer", "333-222"}
fmt.Println("Bob's work phone is:", Bob.phone)
// access phone field in Human
fmt.Println("Bob's personal phone is:", Bob.Human.phone)
}
47
Object-oriented
Object-oriented
Go doesn't allow us to have functions as a part of structs, but it does allow us to bind
functions to structs, these functions are called methods. These methods can only be called
by an instance of the struct.
method
Suppose you define a "rectangle" struct and you want to calculate its area. We'd typically
use the following code to achieve this goal.
package main
import "fmt"
type Rectangle struct {
width, height float64
}
func area(r Rectangle) float64 {
return r.width*r.height
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
fmt.Println("Area of r1 is: ", area(r1))
fmt.Println("Area of r2 is: ", area(r2))
}
The above example can calculate a rectangle's area. We use the function called area , but
it's not a method of the rectangle struct (like class methods in classic object-oriented
languages). The function and struct are two independent things as you may notice.
It's not a problem so far. However, if you also have to calculate the area of a circle, square,
pentagon, or any other kind of shape, you are going to need to add additional functions with
very similar names.
48
Object-oriented
For those reasons, we have the method concept. method is affiliated with type. It has the
same syntax as functions do except for an additional parameter after the func keyword
called the receiver , which is the main body of that method.
Using the same example, Rectangle.area() belongs directly to rectangle, instead of as a
peripheral function. More specifically, length , width and area() all belong to rectangle.
As Rob Pike said.
"A method is a function with an implicit first argument, called a receiver."
Syntax of method.
func (r ReceiverType) funcName(parameters) (results)
49
Object-oriented
package main
import (
"fmt"
"math"
)
type Rectangle struct {
width, height float64
}
type Circle struct {
radius float64
}
func (r Rectangle) area() float64 {
return r.width*r.height
}
func (c Circle) area() float64 {
return c.radius * c.radius * math.Pi
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("Area of r1 is: ", r1.area())
fmt.Println("Area of r2 is: ", r2.area())
fmt.Println("Area of c1 is: ", c1.area())
fmt.Println("Area of c2 is: ", c2.area())
}
50
Object-oriented
One thing that's worth noting is that the method with a dotted line means the receiver is
passed by value, not by reference. The difference between them is that a method can
change its receiver's values when the receiver is passed by reference, and it gets a copy of
the receiver when the receiver is passed by value.
Can the receiver only be a struct? Of course not. Any type can be the receiver of a method.
You may be confused about customized types. Struct is a special kind of customized type there are more customized types.
Use the following format to define a customized type.
type typeName typeLiteral
I hope that you know how to use customized types now. Similar to typedef in C, we use
ages to substitute int in the above example.
51
Object-oriented
52
Object-oriented
in this method. Thus, if we don't use a pointer, it will only change the value inside a copy of
Box.
If we see that a receiver is the first argument of a method, it's not hard to understand how it
works.
You might be asking why we aren't using (*b).Color=c instead of b.Color=c in the
SetColor() method. Either one is OK here because Go knows how to interpret the
assignment. Do you think Go is more fascinating now?
You may also be asking whether we should use (&bl[i]).SetColor(BLACK) in PaintItBlack
because we pass a pointer to SetColor . Again, either one is OK because Go knows how to
interpret it!
Inheritance of method
53
Object-oriented
We learned about inheritance of fields in the last section. Similarly, we also have method
inheritance in Go. If an anonymous field has methods, then the struct that contains the field
will have all the methods from it as well.
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human // anonymous field
school string
}
type Employee struct {
Human
company string
}
// define a method in Human
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
Method overload
If we want Employee to have its own method SayHi , we can define a method that has the
same name in Employee, and it will hide SayHi in Human when we call it.
54
Object-oriented
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
}
type Employee struct {
Human
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
You are able to write an Object-oriented program now, and methods use rule of capital letter
to decide whether public or private as well.
55
interface
2.6 Interface
Interface
One of the subtlest design features in Go are interfaces. After reading this section, you will
likely be impressed by their implementation.
What is an interface
In short, an interface is a set of methods that we use to define a set of actions.
Like the examples in previous sections, both Student and Employee can SayHi() , but they
don't do the same thing.
Let's do some more work. We'll add one more method Sing() to them, along with the
BorrowMoney() method to Student and the SpendSalary() method to Employee.
Now, Student has three methods called SayHi() , Sing() and BorrowMoney() , and
Employee has SayHi() , Sing() and SpendSalary() .
This combination of methods is called an interface and is implemented by both Student and
Employee. So, Student and Employee implement the interface: SayHi() and Sing() . At
the same time, Employee doesn't implement the interface: SayHi() , Sing() ,
BorrowMoney() , and Student doesn't implement the interface: SayHi() , Sing() ,
SpendSalary() . This is because Employee doesn't have the method BorrowMoney() and
Type of Interface
An interface defines a set of methods, so if a type implements all the methods we say that it
implements the interface.
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
56
interface
}
type Employee struct {
Human
company string
money float32
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Human) Sing(lyrics string) {
fmt.Println("La la, la la la, la la la la la...", lyrics)
}
func (h *Human) Guzzle(beerStein string) {
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}
// Employee overloads Sayhi
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func (s *Student) BorrowMoney(amount float32) {
s.loan += amount // (again and again and...)
}
func (e *Employee) SpendSalary(amount float32) {
e.money -= amount // More vodka please!!! Get me through the day!
}
// define interface
type Men interface {
SayHi()
Sing(lyrics string)
Guzzle(beerStein string)
}
type YoungChap interface {
SayHi()
Sing(song string)
BorrowMoney(amount float32)
}
type ElderlyGent interface {
SayHi()
Sing(song string)
SpendSalary(amount float32)
}
57
interface
We know that an interface can be implemented by any type, and one type can implement
many interfaces simultaneously.
Note that any type implements the empty interface interface{} because it doesn't have
any methods and all types have zero methods by default.
Value of interface
So what kind of values can be put in the interface? If we define a variable as a type
interface, any type that implements the interface can assigned to this variable.
Like the above example, if we define a variable "m" as interface Men, then any one of
Student, Human or Employee can be assigned to "m". So we could have a slice of Men, and
any type that implements interface Men can assign to this slice. Be aware however that the
slice of interface doesn't have the same behavior as a slice of other types.
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
}
type Employee struct {
Human
company string
money float32
}
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
58
interface
}
// Interface Men implemented by Human, Student and Employee
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
// define interface i
var i Men
//i can store Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i can store Employee
i = tom
fmt.Println("This is Tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
// slice of Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
// these three elements are different types but they all implemented interface Men
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x {
value.SayHi()
}
}
Empty interface
An empty interface is an interface that doesn't contain any methods, so all types implement
an empty interface. This fact is very useful when we want to store all types at some point,
and is similar to void* in C.
59
interface
If a function uses an empty interface as its argument type, it can accept any type; if a
function uses empty interface as its return value type, it can return any type.
This means any type that implements interface Stringer can be passed to fmt.Println as an
argument. Let's prove it.
60
interface
package main
import (
"fmt"
"strconv"
)
type Human struct {
name string
age int
phone string
}
// Human implemented fmt.Stringer
func (h Human) String() string {
return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.
phone
}
func main() {
Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob)
}
Looking back to the example of Box, you will find that Color implements interface Stringer as
well, so we are able to customize the print format. If we don't implement this interface,
fmt.Println prints the type with its default format.
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())
Attention: If the type implemented the interface error , fmt will call error() , so you don't
have to implement Stringer at this point.
61
interface
If the element is the type that we expect, ok will be true, false otherwise.
Let's use an example to see more clearly.
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(name: " + p.name + " - age: " + strconv.Itoa(p.age) + " years)"
}
func main() {
list := make(List, 3)
list[0] = 1 // an int
list[1] = "Hello" // a string
list[2] = Person{"Dennis", 70}
for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
} else {
fmt.Printf("list[%d] is of a different type\n", index)
}
}
}
It's quite easy to use this pattern, but if we have many types to test, we'd better use switch .
switch test
Let's use switch to rewrite the above example.
62
interface
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(name: " + p.name + " - age: " + strconv.Itoa(p.age) + " years)"
}
func main() {
list := make(List, 3)
list[0] = 1 //an int
list[1] = "Hello" //a string
list[2] = Person{"Dennis", 70}
for index, element := range list {
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}
One thing you should remember is that element.(type) cannot be used outside of the
switch body, which means in that case you have to use the comma-ok pattern .
Embedded interfaces
The most beautiful thing is that Go has a lot of built-in logic syntax, such as anonymous
fields in struct. Not suprisingly, we can use interfaces as anonymous fields as well, but we
call them Embedded interfaces . Here, we follow the same rules as anonymous fields. More
63
interface
specifically, if an interface has another interface embedded within it, it will behave as if it has
all the methods that the embedded interface has.
We can see that the source file in container/heap has the following definition:
type Interface interface {
sort.Interface // embedded sort.Interface
Push(x interface{}) //a Push method to push elements into the heap
Pop() interface{} //a Pop method that pops elements from the heap
}
We see that sort.Interface is an embedded interface, so the above Interface has the three
methods contained within the sort.Interface implicitly.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less returns whether the element with index i should sort
// before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
Reflection
Reflection in Go is used for determining information at runtime. We use the reflect
package, and this official article explains how reflect works in Go.
There are three steps involved when using reflect. First, we need to convert an interface to
reflect types (reflect.Type or reflect.Value, this depends on the situation).
t := reflect.TypeOf(i) // get meta-data in type i, and use t to get all elements
v := reflect.ValueOf(i) // get actual value in type i, and use v to change its value
After that, we can convert the reflected types to get the values that we need.
64
interface
Finally, if we want to change the values of the reflected types, we need to make it modifiable.
As discussed earlier, there is a difference between pass by value and pass by reference.
The following code will not compile.
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
Instead, we must use the following code to change the values from reflect types.
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
We have just discussed the basics of reflection, however you must practice more in order to
understand more.
65
Concurrency
Concurrency
It is said that Go is the C language of the 21st century. I think there are two reasons: first, Go
is a simple language; second, concurrency is a hot topic in today's world, and Go supports
this feature at the language level.
goroutine
goroutines and concurrency are built into the core design of Go. They're similar to threads
but work differently. More than a dozen goroutines maybe only have 5 or 6 underlying
threads. Go also gives you full support to sharing memory in your goroutines. One goroutine
usually uses 4~5 KB of stack memory. Therefore, it's not hard to run thousands of
goroutines on a single computer. A goroutine is more lightweight, more efficient and more
convenient than system threads.
goroutines run on the thread manager at runtime in Go. We use the go keyword to create a
new goroutine, which is a function at the underlying level ( main() is a goroutine ).
go hello(a, b, c)
Output
66
Concurrency
hello
world
hello
world
hello
world
hello
world
hello
We see that it's very easy to use concurrency in Go by using the keyword go . In the above
example, these two goroutines share some memory, but we would better off following the
design recipe: Don't use shared data to communicate, use communication to share data.
runtime.Gosched() means let the CPU execute other goroutines, and come back at some
point.
The scheduler only uses one thread to run all goroutines, which means it only implements
concurrency. If you want to use more CPU cores in order to take advantage of parallel
processing, you have to call runtime.GOMAXPROCS(n) to set the number of cores you want
to use. If n<1 , it changes nothing. This function may be removed in the future, see more
details about parallel processing and concurrency in this article.
channels
goroutines run in the same memory address space, so you have to maintain synchronization
when you want to access shared memory. How do you communicate between different
goroutines? Go uses a very good communication mechanism called channel . channel is
like a two-way pipeline in Unix shells: use channel to send or receive data. The only data
type that can be used in channels is the type channel and the keyword chan . Be aware
that you have to use make to create a new channel .
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
67
Concurrency
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x + y)
}
Sending and receiving data in channels blocks by default, so it's much easier to use
synchronous goroutines. What I mean by block is that a goroutine will not continue when
receiving data from an empty channel, i.e ( value := <-ch ), until other goroutines send data
to this channel. On the other hand, the goroutine will not continue until the data it sends to a
channel, i.e ( ch<-5 ), is received.
Buffered channels
I introduced non-buffered channels above. Go also has buffered channels that can store
more than a single element. For example, ch := make(chan bool, 4) , here we create a
channel that can store 4 boolean elements. So in this channel, we are able to send 4
elements into it without blocking, but the goroutine will be blocked when you try to send a
fifth element and no goroutine receives it.
ch := make(chan type, n)
n == 0 ! non-bufferblock
n > 0 ! buffernon-block until n elements in the channel
You can try the following code on your computer and change some values.
68
Concurrency
package main
import "fmt"
func main() {
c := make(chan int, 2) // change 2 to 1 will have runtime error, but 3 is fine
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
for i := range c will not stop reading data from channel until the channel is closed. We
use the keyword close to close the channel in above example. It's impossible to send or
receive data on a closed channel; you can use v, ok := <-ch to test if a channel is closed.
If ok returns false, it means the there is no data in that channel and it was closed.
Remember to always close channels in producers and not in consumers, or it's very easy to
get into panic status.
69
Concurrency
Another thing you need to remember is that channels are not like files. You don't have to
close them frequently unless you are sure the channel is completely useless, or you want to
exit range loops.
Select
In the above examples, we only use one channel, but how can we deal with more than one
channel? Go has a keyword called select to listen to many channels.
select is blocking by default and it continues to execute only when one of channels has
data to send or receive. If several channels are ready to use at the same time, select
chooses which to execute randomly.
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x, y = y, x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
select has a default case as well, just like switch . When all the channels are not ready
for use, it executes the default case (it doesn't wait for the channel anymore).
70
Concurrency
select {
case i := <-c:
// use i
default:
// executes here when c is blocked
}
Timeout
Sometimes a goroutine becomes blocked. How can we avoid this to prevent the whole
program from blocking? It's simple, we can set a timeout in the select.
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
Runtime goroutine
The package runtime has some functions for dealing with goroutines.
runtime.Goexit()
Exits the current goroutine, but defered functions will be executed as usual.
runtime.Gosched()
Lets the scheduler execute other goroutines and comes back at some point.
runtime.NumCPU() int
71
Concurrency
runtime.NumGoroutine() int
72
Summary
2.8 Summary
In this chapter, we mainly introduced the 25 Go keywords. Let's review what they are and
what they do.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
If you understand how to use these 25 keywords, you've learned a lot of Go already.
73
General
Go Programming Basics
Workspace
$GOPATH and $GOROOT
$GOROOT: This is an environment variable which stores the path where your Go installation
is present if you have customized it. $GOPATH: The Go universe for your machine. The idea
is that all your Go code should reside in a directory tree so the code isn't lying around in
random places.
In most unix systems it is done with export GOPATH=/usr/home/suraj/Go .
My $GOPATH is /usr/home/suraj/Go , it has the following structure
pkg: The libraries which'll be imported in our code, there is a .a file created at the
respective path.
bin: The binary file of our project will be installed here on calling Go install in the
project folder
src: Will hold the actual code, if you have a github account, then you can create a folder
tree inside like
Go
src
github.com
thewhitetulip
wshare
picsort
golang.net
sourcegraph.com
bin (binaries)
wshare
picsort
pkg
Go takes this unique approach so that all the Go code is well organized and not thrown in a
haphazard manner. This makes it easy to locate code for humans and software alike.
Packages
74
General
Packages, in Go, are just one or more file(s) which contains Go source code. It can be
named anything, but it needs to be a in a folder and each file in the package should have the
line package <name> at the top. The folder name has to be exactly same as the <name> we
specifiy in each package source file.
There can be any number of files in a package directory, but only one file with the main
package. While building the code, the compiler starts with the main package.
MVC Patterns
When I started learning building web apps in Django, I first read about the MVC pattern, the
models, the views and what not. But that knowledge was to be assumed by me and I had no
idea why that decision was made, along with that Django works like magic, one has to adapt
to it's quirks. Hence while programming in Go, it'll be immensely benefitial to grow up to the
MVC pattern by starting out small. The first app should entirely reside in the main.go file,
then slowly, as the app grows, it needs to be structured well, so the all the handlers go into a
views package, templates go in a templates folder, database related entities go in another
package.
Learning by doing and experimenting is the best way. Challenge the status quo.
Package naming
The actual package name is just "views" or "views", the package is found out by the
compiler from it's absolute path from the $GOPATH/src directory. For the Tasks app, it
resides in $GOPATH/src/github/thewhitetulip/Tasks , thus, my packages will lie within the
Tasks folder. This makes it easy for distributing the libraries. Theoretically, one can create a
package directly in the $GOPATH/src folder to skip out the long naming conventions, but
doing so breaks the code distribution via version control.
When packages are resolved, by the Go compiler, it first looks in the standard library and
then starts looking for the full name in the $GOPATH/src folder.
Internal deployment
We'll follow the standard practice and put our code in
$GOPATH/src/github.com/thewhitetulip/Tasks folder. While testing our app, we need a
deployment version. I useed Tasks whiel building Tasks, I have made a folder ~/Tasks
and here I keep the deployment version of Tasks, which contains the binary version and the
static files. Every new build in the src gets pushed in this folder.
75
General
Running a server
We deploy webapps on IP addresses on specific ports. Typically we choose a large port
number like 8000 so it doesn't affect some other applications. Certain ports are restricted
and need sudo access, like 80.
The IP address we bind the server to can be a public or private. Public means using ":8080"
or "0.0.0.0:8080". Private means "127.0.0.1:8080"
Public means any computer in the same network can access the web app. Private means
only your computer will be able to access it.
//Public
log.Fatal(http.ListenAndServe(":8080", nil))
//Private
log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
Note: 127.0.0.1
127.0.0.1 , called localhost, is a special IP address which is given to every machine to refer
to itself. it doesn't matter if you are connected to the network or not, your machine will
always have this IP address, so if you are want privacy use 127.0.0.1 .
If you do want your web application to be accessible then use 0.0.0.0.0:8080 or just
:8080 , when you give just the port number, the Go language assumes the IP address the
76
HTTP Methods
HTTP has various methods like GET, POST, DELETE, PUT.
GET : used to retrieve the URL, GET / will get the home page. POST: used to create data
stored on the URL. PUT: used to update data on the URL. DELETE: used to delete data in
the URL.
// Create a new category.
// POST /categories
// Update an existing category.
// PUT /categories/12
// View the details of a category.
// GET /categories/12
// Delete an existing category.
// DELETE /categories/12
Think of categories as a document, POST to create it, GET to fetch it, PUT to update it, and
DELETE to delete it. For deleting a task, rather than sending GET /delete/1234, we should
send a DELETE /tasks/1234. Thus, rather than over using the GET method for everything,
77
GET vs POST
Apart from their functional differences, GET and POST differ in security perspectives.
GET transfers data via the URL. POST sends data in the request's body or payload, but that
isn't hidden or encrypted by default, but it isn't visible on the URL, it is easily accessible to
anyone who knows how to read a HTTP request.
Security is something you build your application around. In short there isn't much difference
between GET and POST when we consider security, both transfer data in plain text GET is
just relatively a little less secure since URLs are logged by a proxy server/firewall/browser
history and that GET requests can be done by the browser on behalf of the user without
confirmation. Bots are common over the internet, bots can visit randomly to every link
present in your application, but they don't send random data to any Form you have, or if so,
very few bots can do that.
For protecting data of the webapp, one has to stick to using HTTPS and sanitize any data
that comes from the user.
Example
A blog consists of a collection of posts, a post has tags, is written by some author, at some
time and has some primary key to uniquely identify it in our database and it has a slug which
means the URL.
This is the era of semantic web, thus the new beautiful URLs like,
surajblog.com/posts/welcome-the-new-year , the slug is the welcome-the-new-year .
When the server gets a HTTP GET request of /posts/welcome-the-new-year , it'll search for
URL handlers starting with the list of URL handlers we have given, then it'll find the closest
match, in our case it'll be /post/ , then it'll call the handler of this URL.
Our / root URL should be at the very bottom of our list. Because while executing, checks
are done from top to bottom.
//sample handler definition
http.HandleFunc("/post/", ShowPostBySlug)
http.HandleFunc("/", ShowAllPosts)
Handler talk to the database, fetch the data and render templates which show up as HTML
pages in our browser.
78
What is a template?
Templates are a way to present data to the user. The server populated the templates and
sends the HTML page back to the browser. For a blog, it doesn't make sense to generate
html page for each post. This is why there is a post template and the server will get all the
details like content, title, date published and populate the post template and return it back to
the browser.
A web application is basically a way of representing data stored in the database to the end
user using HTTP.
Writing a web application:
1. Fix the database structure.
2. Understand how data flows and decide the URLs
3. Write templates to corresponding to (almost) each URL
4. Write functions in Go to handle each URL pattern, called handlers.
5. Handlers fetch data from the database and populate data in the templates.
Not abusing templates
The logic behind creating templates was to not to repeat our html page. Template support
variables which our handlers are going to populate. The standard way is to handle the
business login inside the handler and use templates just for rendering the data. Templates
are to be used only for the presentation logic not the business logic. It becomes difficult to
maintain applications which have business logic inside the template. It should never be
done.
Example:
We are going to build a todo list manager in this book. It support multiple users.
Wrong way: Fetch all tasks in the template and only show those of the current user. i.e. filter
the tasks in the template Correct way: Fetch only the tasks belonging to the current user. i.e.
filter the tasks in the handler.
Functionality of our EditTask URL which is /edit/<id> .
file views/addViews.go
79
file db/tasks.go
func GetTaskByID(id int) types.Context {
//Code to fetch tasks of the current user
context := types.Context{Tasks: tasks}
return context
}
The EditTaskFunc talks to the database with the GetTaskByID function and fetches the tasks
for the current user and populates the editTemplate.
Thus we can split an application into views, database, templates and controller(main
package).
Static Files
Static files are required for templates, they are all the CSS/JS/Images which we load into our
html templates.
The URL will be /static/ .
Execution:
1. We get a request like /static/<filepath>
2. We go to the public directory of our application and look for
3. If we get a file of that path then we serve the file, othewise send a 404 error.
The public folder contains all your static files. We will have a templates folder on the same
folder where the public is present.
The reason templates is a separate folder is that it is a separate entity and shouldn't be
publicly available using the /static/ URL.
80
public
| |-- static
| | |-- css
| | | `-- styles.css
..and more
| | `-- js
| | |-- bootstrap.min.js
| | .... and more
templates
| |-- completed.html
| | ...and more
Note Output
The above output is of the tree program.
81
Implementation
You will notice that the program doesn't print anything because we told it only to listen on the
port. If we want to user to know that we are running a server on that port, we should print a
message saying so, as shown in the below example.
code example file: 3.1basicServer.go
82
Implementation
package main
import (
"log"
"net/http"
)
func main() {
PORT := ":8080"
log.Print("Running server on "+ PORT)
log.Fatal(http.ListenAndServe(PORT, nil))
}
Open localhost:8080 in a browser and you'll get the message "404 page not found"
This is because as of now we have just started a server to listen on the port 8080, but we
haven't handled the URL. The HTTP 404 error will be displayed each time the server won't
be able to figure out how to serve the request.
Handling URLs
http.HandleFunc("/complete/", ShowCompleteTasksFunc)
//ShowCompleteTasksFunc is used to populate the "/completed/" URL
func ShowCompleteTasksFunc(w http.ResponseWriter, r *http.Request) {
}
We HandleFunc in the net/http package to handle URLs. The first argument is the URL to
be handled and the second parameter is the function. We can define functions in the second
parameter, but you are advised against it.
The handler function requires two arguments, a ResponseWriter object and a Request
object. We are going to write to the ResponseWriter depending on what we get in the
Request object.
Handler Example
We want to write the URL that the user is visiting on our webapp. The URL can be found in
the request object, r.URL.Path .
83
Implementation
package main
import (
"log"
"net/http"
)
func main() {
PORT := ":8080"
log.Print("Running server on "+ PORT)
http.HandleFunc("/",CompleteTaskFunc)
log.Fatal(http.ListenAndServe(PORT, nil))
}
func CompleteTaskFunc(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(r.URL.Path))
}
Parameterized routing
When we get the URL /tasks/124, we want to get the task number 124, we do the following
//GetTaskFunc is used to delete a task,
func GetTaskFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
id := r.URL.Path[len("/tasks/"):]
w.Write([]byte("Get the task "+id))
}
We take a sub string of the URL and remove the /delete/ part and we have the ID of the
task.
This example makes use of the slicing concept. It is simple, if Path is our string variable
then Path[1:] is the substring which includes everything from the first character, index being
zero.
Note: Parameterized routing
Ideally we should checking the URL path inside our view, we are supposed to use a router.
For real projects, you should use a router. Here we are learning, so we won't be using a
router.
84
Implementation
For serving static files, we use the FileServer method of the http package. It takes a folder
as an argument. Make sure you give only the public folder path in the argument.
Homework
Read the documentation of net/http & log and get to know of the methods/functions
in the packages [1]
Find out how many alternatives are there to ListenAndServe.
Write a handler to serve static files, if file is available on that path then it should serve
the file, otherwise it must return an HTTP404 error.
Write a command line application to share files/folder over HTTP, the syntax should be
like this ./wshare -f file.pdf file on link = 192.168.2.1:8080.
Footnotes
[1]: Learn how to read documentation, it saves a lot of time.
85
The Design
Translating our design to API, we get the following.
/add/ POST = add new task
/ GET = show pending tasks
/complete/ GET = show completed tasks
/deleted/ GET = show deleted tasks
/edit/<id> POST = edit post
/edit/<id> GET = show the edit page
/trash/<id> POST = trash post to recycle bin
/delete/<id> POST = permanently delete post
/complete/<id> POST = mark post as complete
/login/ POST = do the login
/login/ GET = show login page
/logout/ POST = log the user out
/restore/<id> POST = restore that task
/update/<id> POST = update task
/change/ GET = will allow changing password
/register/ GET = show the register page
/register/ POST = will add entries into database
file ~/main/main.go
86
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/complete/", CompleteTaskFunc)
http.HandleFunc("/delete/", DeleteTaskFunc)
http.HandleFunc("/deleted/", ShowTrashTaskFunc)
http.HandleFunc("/trash/", TrashTaskFunc)
http.HandleFunc("/edit/", EditTaskFunc)
http.HandleFunc("/completed/", ShowCompleteTasksFunc)
http.HandleFunc("/restore/", RestoreTaskFunc)
http.HandleFunc("/add/", AddTaskFunc)
http.HandleFunc("/update/", UpdateTaskFunc)
http.HandleFunc("/search/", SearchTaskFunc)
http.HandleFunc("/login", GetLogin)
http.HandleFunc("/register", PostRegister)
http.HandleFunc("/admin", HandleAdmin)
http.HandleFunc("/add_user", PostAddUser)
http.HandleFunc("/change", PostChange)
http.HandleFunc("/logout", HandleLogout)
http.HandleFunc("/", ShowAllTasksFunc)
http.Handle("/static/", http.FileServer(http.Dir("public")))
log.Print("running on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Create all functions we mentioned above and make the necessary changes as per one
definition that we show below in this file.
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
message := "all pending tasks GET"
} else {
message := "all pending tasks POST"
}
w.Write([]byte(message))
}
87
In your browser type localhost:8080 and type all these URLs and see what message you
get.
Homework
Check the documentation for http.ResponseWriter and http.Request objects and get to
know all the variables/functions/constants for http package and these two which we
mentioned
88
Database Handling
Using databases in Go
Go doesn't provide out of the box support for any database, but it provides an interface,
which can be used by database library creators to keep all the database libraries compatible
with each other.
We will use sqlite for this book.
Use the following insert statements to enter data in our table, so we'll begin reading data in
our ShowAllTasks function which we wrote in the previous chapter
89
Database Handling
go get "github.com/mattn/go-sqlite3"
Accessing database in go
We import the library as import _ "github.com/mattn/go-sqlite3"
The package is imported anonymously, when we import net/http we have to use http. to
access the package. To be able to swap underlying databases, we won't do that. Thus the
anonymous package import.
Under the hood, the driver registers itself to the database/sql package.
90
Database Handling
Also note that we can give a package alias instead of the underscore character should we
want an alias for our package.
Every database has a connection mechanism, file for sqlite and IP address for
MySQL/Postgres.
We encapsulate our db object inside a struct. We also encapsulate the database actions as
shown below
var database Database
//Database encapsulates database
type Database struct {
db *sql.DB
}
func (db Database) begin() (tx *sql.Tx) {
tx, err := db.db.Begin()
if err != nil {
log.Println(err)
return nil
}
return tx
}
func (db Database) prepare(q string) (stmt *sql.Stmt) {
stmt, err := db.db.Prepare(q)
if err != nil {
log.Println(err)
return nil
}
return stmt
}
func (db Database) query(q string,
args ...interface{}) (rows *sql.Rows) {
rows, err := db.db.Query(q, args...)
if err != nil {
log.Println(err)
return nil
}
return rows
}
func init() {
database.db, err =
91
Database Handling
sql.Open("sqlite3", "./newtask.db")
if err != nil {
log.Fatal(err)
}
}
//Close database connection
func Close() {
database.db.Close()
}
//taskQuery encapsulates Exec()
func taskQuery(sql string, args ...interface{}) error {
SQL := database.prepare(sql)
tx := database.begin()
_, err = tx.Stmt(SQL).Exec(args...)
if err != nil {
log.Println("taskQuery: ", err)
tx.Rollback()
} else {
tx.Commit()
}
return err
}
Note init()
The init function is the first function to run when the package is imported or executed. This is
why we do the initialization in it.
Note DB from godoc
type DB struct {
// contains filtered or unexported fields
}
own pool of idle connections. Thus, the Open function should be called just once. It is rarely
necessary to close a DB.
92
Database Handling
err = db.Ping()
if err != nil {
//do something about it
}
The sql.DB object shoudn't be opened and closed frequently, it should be closed only when
we no longer need to access the database.
Please refer to the documentation of database/sql for more details.
Note exported and unexported variables
Exported: variable starts with a capital letter and is accessible for those who import this
package Unexported: variable is private for the package, not accessible outside it's
declaration.
We use the Query method to query the database when we expect some result from the
database.
93
Database Handling
94
Database Handling
The defer statement puts the function call at the bottom of the call stack, so whenever the
function returns, defer is triggered. One has to be careful with using defer, it can cause
difficult to find bugs.
file ~/main/main.go
Find and fix the bug:
package main
import (
_ "github.com/mattn/go-sqlite3"
"fmt"
)
var database *sql.DB
func init() {
defer database.Close()
database, err = sql.Open("sqlite3", "./tasks.db")
if err != nil {
fmt.Println(err)
}
}
//intentional bug exists, fix it
func main() {
getTaskSQL = "select id, title, content, created_date from task
where finish_date is null and is_deleted='N' order by created_date asc"
rows, err := database.Query(getTaskSQL)
if err != nil {
fmt.Println(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&TaskID, &TaskTitle, &TaskContent, &TaskCreated)
TaskContent = strings.Replace(TaskContent, "\n", "<br>", -1)
if err != nil {
fmt.Println(err)
}
fmt.Println(TaskID, TaskTitle, TaskContent, TaskCreated)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Always defer rows.Close() , to free the database connection in the pool. So long as rows
contains the result set, the database connection is in use and not available in the connection
pool.
95
Database Handling
When the rows.Next() function returns EOF (End of File), which means that it has reached
the end of records, it'll call rows.Close() for you, Close() can be called multiple times
without side effects.
Single-Row Queries
If a query returns at most one row, you can use a shortcut:
var taskDescription string
query:="select taskDescription from task where id = ?"
err = db.QueryRow(query, 1).Scan(&taskDescription)
if err != nil {
log.Fatal(err)
}
fmt.Println(taskDescription)
Errors from the query are deferred until Scan() is called, and then are returned from that.
You can also call QueryRow() on a prepared statement:
query :="select taskDescription from task where id = ?"
stmt, err := db.Prepare(query, 1).Scan(&taskDescription)
if err != nil {
log.Fatal(err)
}
...
var taskDescription string
err = stmt.QueryRow(1).Scan(&taskDescription)
if err != nil {
log.Fatal(err)
}
fmt.Println(taskDescription)
96
Database Handling
Prepare
Prepare creates a prepared statement for later queries or executions. Multiple queries or
executions may be run concurrently from the returned statement. The caller must call the
statement's Close method when the statement is no longer needed.
Note Error handling
Because of Go's unique method of handling errors, we can be stuck in the if err != nil land.
But, on the other hand it simplifies error handling as we just have to return the error
message.
An excellent resource about Go and databases can be found at http://go-database-sql.org
The fault in our code:
Fixing the intentional bug in the above code:
func init() {
defer database.Close()
database, err = sql.Open("sqlite3", "./tasks.db")
if err != nil {
fmt.Println(err)
}
}
97
Database Handling
Homework
See the /code/chapter-4/4.5database in our code repository and modify the file to insert
data from the 4.3formsupload folder. We have two working code set, one of printing form
values on the console and one of fetching db values and rendering a template. What you
have to do is based on this chapter, write methods to insert values from the form to the
database.
98
Webapp Example
An Example
Expected output:
open your task.db file in sqlite3 like this
[Tasks] $ sqlite3 task.db
sqlite> select title from task limit 1;
Publish on github
Now this output should match with the one we see at localhost:8080
After running the file, go to localhost:8080 and localhost:8080/add
file ~/main/main.go
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
"log"
"net/http"
"time"
)
var database *sql.DB
var err error
//Task is the struct used to identify tasks
type Task struct {
Id int
Title string
Content string
Created string
}
//Context is the struct passed to templates
type Context struct {
Tasks []Task
Navigation string
Search string
Message string
}
func init() {
99
Webapp Example
100
Webapp Example
Homework
The homework is to split the code into packages and get it to work, the type definition goes
into the types/types.go file, the handler definition goes into the views/views.go , the
database read and write methods go into the db/db.go . Make sure that after you refactor
the code, that the code runs.
101
Form handling
102
Form handling
When we are populating this HTML page, we can also populate it dynamically, without
getting bogged down by syntax, ignore templating for the while We have a variable called
Navigation and one called Categories, we loop through Categories and if the Navigation is
equal to that category then the checked value is true.
Category:
<select name="category" class="dropdown">
<option>---</option>
{{$navigation := .Navigation}} {{$categories := .Categories}}
{{range $cat := $categories}}
<option value="{{$cat.Name}}" {{if eq $cat.Name $navigation }} checked="checke
d" {{end}}> {{$cat.Name}} </option>
{{end}}
</select>
Here we have used a drop down box, you can use radio buttons like below
<input type="radio" name="gender" value="1">Female
<input type="radio" name="gender" value="2">Male
<input type="radio" name="gender" value="3">Other
Or checkboxes
<input type="checkbox" name="gender" value="female">Female
<input type="checkbox" name="gender" value="male">Male
<input type="checkbox" name="gender" value="other">Other
We get the value of a drop down box/radio button/checkbox on the server side by using the
name field like below:
value := r.Form.Get("gender")
value := r.FormValue("gender")
As we saw earlier, our webservers basically take a HTTP Request object and return an
HTTP Response Object, below is an example of a sample HTTP Request object.
The host is the IP address sending the req, User Agent: fingerprint of the machine, the
Accept- fields define various parts like the language, encoding Referer is which IP made the
call, Cookie is the value of the cookie stored on the system and Connection is the type of
connection.
In this request snapshot, we also have a file which we upload, for file upload, the content
type is multipart/form-data
103
Form handling
Request Header
Host: 127.0.0.1:8081
User-Agent: ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1:8081/
Cookie: csrftoken=abcd
Connection: keep-alive
Request Body
Content-Type: multipart/form-data;
boundary=---------------------------6299264802312704731507948053
Content-Length: 15031
-----------------------------6299264802312704731507948053
Content-Disposition: form-data; name="title"
working with forms
-----------------------------6299264802312704731507948053
Content-Disposition: form-data; name="CSRFToken"
abcd
-----------------------------6299264802312704731507948053
Content-Disposition: form-data; name="content"
finish the chapter working with forms
-----------------------------6299264802312704731507948053
Content-Disposition: form-data; name="uploadfile"; filename="2.4workingwithform.md"
Content-Type: text/x-markdown
--file content------------------------------6299264802312704731507948053
Content-Disposition: form-data; name="priority"
3
-----------------------------6299264802312704731507948053--
If you had wondered how Google's home page shows a pop up when you visit google.com
on IE of Firefox, it checks your User-Agent. The thing with HTTP request is that they can be
modified to any extent, the Chrome developer tools gives you quite sophisticated tools to
modify your requests, even User Agent spoofing is a default feature avaiable, this feature is
available so we can test our webapps in one window simulating many internet browsers at
one go.
104
Form handling
The basic part of working with forms is to identify which user that particular form belongs to,
there are ways to attain that, we can either have a stateful or a stateless web server.
A stateless server doesn't store sessions, it requires an authentication key for each request
while a stateful server stores sessions. For storing sessions a cookie is used, which is a file
which is stored in the private memory of the web browser which we use. Only the website
which created the cookie can access the cookie, no third party websites can access the
cookies, but the OS user can read/edit/delete cookies using the web browser.
CSRF
CSRF stands for Cross Request Site Forgery. Any website can send a POST request to
your web server, who sent the request can be found in the Referer field of your HTTP
response. It is a form of confused deputy attack in which the deputy is your web browser. A
malicious user doesn't have direct access to your website, so it makes use of your browser
to send a malicious request. Typically cookies enable your browser to authenticate itself to a
webserver, so what these malicious websites do is, they send in a HTTP request on behalf
of your browser.
We can thwart this attack by restricting the referer to your own domain, but it is quite easy to
manipulate the misspelt referer field of a HTTP request.
Another way is to use tokens. While rendering our form, we send in a hidden field with crypto
generated string of 256 characters, so when we process the POST request, we first check if
the token is valid or not and then decide if the data came from a genuine source or from a
malicious source. It doesn't have to be malicious actually, even if a legitimate user tried to
trick your webserver into accepting data, we shouldn't entertain it.
To check the csrf token, we serve the token to the form and store it in a cookie, when we get
the POST request, we check if both are equal or not. This is because a malicious person
might trick a user to click on a form but they can't set cookies for your webapplication.
A point to note here is that never trust user data. Always clean/sanitize data which you get
from the end user.
Note Javascript
If you are serious about web development, you ought to learn Javascript in detail. While
building a web app, there will be times when you would want to improve the UI of your
application, which would mean a change in the html page. Using JS is inevitable while
building beautiful webapps, while adding some new html feature, open the "web inspector"
present in the developer tools and dynamically add the html code. The web inspector allows
us to manipulate the CSS and HTML part. Now open the javascript console, that enables
105
Form handling
you to test the JS feature which you are willing to add. For instance, in the tasks application,
there was no provision to expand/contract the size of the task, so I added a button in the
web inspector,
<button class="toggle"></button>
This proved that it works, so now go to your template and actually add the code. Make sure
the html is correct because while running, the html files are parsed once, so for any html
change, you have to run the web app for each HTML change. So once the html is set up, if
you change the JS/CSS then you just have to refresh the page, because the html page gets
the JS/CSS each time the page is loaded.
As we saw in the above paragraph, for preventing CSRF, we need to generate a token and
send as a hidden field in the form and store it in a cookie, when we get the POST request
from the
Forms in Go
In the below function we set the cookie, we first generate a CSRF token, which'll be unique
for each HTTP request which we get and store it in a cookie.
//ShowAllTasksFunc is used to handle the "/" URL which is the default ons
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
context := db.GetTasks("pending") //true when you want non deleted notes
if message != "" {
context.Message = message
}
context.CSRFToken = "abcd"
message = ""
expiration := time.Now().Add(365 * 24 * time.Hour)
cookie := http.Cookie{Name: "csrftoken",Value:"abcd",Expires:expiration}
http.SetCookie(w, &cookie)
homeTemplate.Execute(w, context)
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
106
Form handling
The below handler handles the POST request sent by our form, it fetches the value of the
csrftoken cookie and gets the value of the hidden CSRFToken field of the add task form. If the
value of the cookie is equal to the value fetched by the form, then we allow it to go to the
database.
The call to ParseForm will parse the contents of the form into Gettable fields which we can
fetch using the Get function. This call is compulsory.
//AddTaskFunc is used to handle the addition of new task, "/add" URL
func AddTaskFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
r.ParseForm()
file, handler, err := r.FormFile("uploadfile")
if err != nil {
log.Println(err)
}
taskPriority, priorityErr := strconv.Atoi(r.FormValue("priority"))
if priorityErr != nil {
log.Print("unable to convert priority to integer")
}
priorityList := []int{1, 2, 3}
for _, priority := range priorityList {
if taskPriority != priority {
log.Println("incorrect priority sent")
//might want to log as security incident
taskPriority=1 //this defaults the priority to low
}
}
title := template.HTMLEscapeString(r.Form.Get("title"))
content := template.HTMLEscapeString(r.Form.Get("content"))
formToken := template.HTMLEscapeString(r.Form.Get("CSRFToken"))
cookie, _ := r.Cookie("csrftoken")
if formToken == cookie.Value {
if handler != nil {
r.ParseMultipartForm(32 << 20) //defined maximum size of file
defer file.Close()
f, err := os.OpenFile("./files/"+handler.Filename, os.O_WRONLY|os.O_CR
EATE, 0666)
if err != nil {
log.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
filelink :=
"<br> <a href=/https/www.scribd.com/files/" + handler.Filename + ">" + handler.Filename + "
</a>"
content = content + filelink
}
107
Form handling
Note Cookies
Cookie is a way to store data on the browser, HTTP is a stateless protocol, it wasn't built for
sessions, basically the Internet itself wasn't built considering security in mind since initially it
was just a way to share documents online, hence HTTP is stateless, when the web server
receives requests, it can't distinguish between two consequitive requests, hence the concept
of cookies were added, thus while starting a session, we generate a session ID and store it
on the database in memory or on the database and we store the same ID on a cookie on the
web browser and we validate both of them to authenticate them.
We have to note that, if we set an expiry date for a cookie, then it is stored on the filesystem
otherwise it is stored in memory in the browser. In the incognito mode, this is the case, all
the cookies are stored in memory and not in the filesystem.
HTTP Request
Host: localhost:8080
User-Agent: .....
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://localhost:8080/
Cookie: csrftoken=abcd
Connection: keep-alive
Cache-Control: max-age=0
108
Form handling
The browser, while sending a response appends all the cookie data stored in its memory or
file along with the other aspects of the HTTP request so we can access the cookie as
r.Cookie , it'll contain every cookie for that particular domain, we'd then loop through it to
fetch the data which we want. This is important for security reasons, suppose I set a cookie
on my imaginedomain.com and if it contains a csrf token and if that cookie is accessible to
some other webapp then it is horrible since they can masquerade as any legitmate user. But
this ain't possible, since a website can only access the cookie stored for its own domain,
plus we have the feature to set HTTP only value of a cookie as true, which says that even
javascript can't access the cookies.
HTTP Response
Content-Type: text/html; charset=utf-8
Date: Tue, 12 Jan 2016 16:43:53 GMT
Set-Cookie: csrftoken=abcd; Expires=Wed, 11 Jan 2017 16:43:53 GMT
Transfer-Encoding: chunked
When we set cookies, we write then to the HTTP response which we send to the client and
the browser reads the Cookie information and stores them in the memory or the filesystem
depending on the Expires field of the response.
From the go documentation:
type Cookie struct {
Name string
Value string
Path string // optional
Domain string // optional
Expires time.Time // optional
RawExpires string // for reading cookies only
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
Input Validation
109
Form handling
The basic aspect of web applications is that no data can be trusted, even if the user isn't
malicious herself, there are many ways to trick the browser into sending HTTP requests and
fooling the web server to respond to what seems like a legitimate request. Hence we have to
verify everything that comes from the user.
One might argue here that we do all sorts of validation using javascript, but there are ways
to evade JS validations, the simplest way is to disable JS and more sophisticated ways are
to manipulate the HTTP request before the browser sends it, it is literally trivial when we use
just the web developer tools that these days browsers provide.
if formToken == cookie.Value and title != nil and content!=nil
The title and content of the task is mandatory, hence we added the not nil part.
We do input validation when we don't want junk data to go into our database, suppose the
user hit the submit button twice, or some other scenario. But we also have to consider the
case where the user wants to run some script on our website, which is dangerous, so we
use the template.HTMLEscapeString method to escape what might run in memory of the
current browser session. Even the data which comes from your drop down list should be
validated.
Everything in a web form is sent via a Request as we saw in the above example, we use a
drop down list when we have are expecting a particular set of inputs but for someone who
knows what firefox dev tools are, can easily modify anything in the request, for example we
have the priority as drop down list we might think that since we have only three entries in the
drop down list, should we keep a check in our view handler to not accept anything but the
three values which are present in our template. We ought to keep a check like below:
priorityList := []int{1, 2, 3}
for _, priority := range priorityList {
if taskPriority != priority {
log.Println("incorrect priority sent")
//might want to log as security incident
}
}
110
Form handling
All a malicious user has to do is change this value and resend the request, they can literally
insert any value here, just think what if they send rm -fr *.* and accidentally enough this
command is executed on our server. This also brings in another aspect of security, never run
your machine in root mode, always keep the root mode for admin tasks and use a non root
mode. Even if that isn't the case, a less dangerous example will be sending a huge number
with the request, assuming that we have used integer as our variable, the program might
crash if it has to handle a number beyond its storage capacity. This might be termed as a
denial of service attack.
111
Uploading Files
Uploading files
Uploading files is the next step in form processing, in case of files, we send the entire file
data in the HTTP header, so we have to set the form encoding to enctype="multipart/formdata" . This will inform our server that we are going to get a file from the form along with the
We first provide the maximum size of the file which is 32 ^ 20, which is gigantic for our
webapp, not everyone has the Internet infrastructure to upload that big a file, but we want to
be flexible.
We basically get a file from the form request, in the form handler we open another file with
the same/different name and then read the file form the request and write it on the server.
We need to handle the scene where we name the file differently so we'd need to store the
old file name -> new file name relation somewhere, it can be a database table.
The file name should be scrubbed, since the user can give any malicious name to damage
our application.
We now want to randomize the file name of the files which the users upload. In your
AddTaskFunc add the following lines
112
Uploading Files
if handler != nil {
r.ParseMultipartForm(32 << 20) //defined maximum size of file
defer file.Close()
randomFileName := md5.New()
io.WriteString(randomFileName, strconv.FormatInt(time.Now().Unix(), 10))
io.WriteString(randomFileName, handler.Filename)
token := fmt.Sprintf("%x", randomFileName.Sum(nil))
f, err := os.OpenFile("./files/"+token, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
log.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
filelink := "<br> <a href=/https/www.scribd.com/files/" + token + ">" + handler.Filename + "</a
>"
content = content + filelink
fileTruth := db.AddFile(handler.Filename, token)
if fileTruth != nil {
message = "Error adding filename in db"
log.Println("error adding task to db")
}
}
file ~/Tasks/db/db.go
// AddFile is used to add the md5 of a file name which is uploaded to our applicat
ion
// this will enable us to randomize the URL without worrying about the file names
func AddFile(fileName, token string) error {
SQL, err := database.Prepare("insert into files values(?,?)")
if err != nil {
log.Println(err)
}
tx, err := database.Begin()
if err != nil {
log.Println(err)
}
_, err = tx.Stmt(SQL).Exec(fileName, token)
if err != nil {
log.Println(err)
tx.Rollback()
} else {
log.Println(tx.Commit())
}
return err
}
113
Uploading Files
table structure
CREATE TABLE files(name varchar(1000) not null, autoName varchar(255) not null);
114
Templates
Templates
package: text/template
In the first chapter we had a cursory glance over the concept of templates. This chapter is
dedicated entirely to templates. As we said previously, a web application responds to certain
URLs and gives an html page to the browser which the browser then interprets and shows to
the end user. This html page which is sent to the browser is what is called templates in the
backend for we have a template which stores some variables, and in real time data is
provided into the template which makes it a complete html page.
Let's take a practical example. Suppose we are building a micro blogging site. We would
start with creating the front end in html. Our microblogging site will show Hi User on the
right corner.
In our static html we write this <p>Hi User</p>
But if we serve this page on our webserver it'll not change anything, it'll show Hi User , the
name of the user won't come magically, we have to put it into the page somehow, here we
use a variable so in Go we'd approach this by using a variable named {{.Name}} so our
html now will be <p>Hi {{.Name}}</p> . The . is mandatory.
Now, this is the logic we apply to all our html pages, keeping such {{}} variable expansion
parameters and serving the value of the parameter while executing the template. If you are
wondering how we'd do that, this is the syntax
homeTemplate.Execute(w, context)
//Context is the struct passed to templates
type Context struct {
Tasks []Task
Name string
Search string
Message string
}
These are the three parts of using templates, first you need to create types like we have
created the Context type, then we need to read the template, then we need to use the
components of that type in our template file. So what remains now is passing an object of
that type during template execution.
Every template requires the context object because that is what defines the data to be
populated in the template.
115
Templates
Reading template:
templates, err = template.Must(template.ParseFiles(allFiles...))
Note: template.Must
Must is a helper that wraps a call to a function returning (*Template, error) and panics if the
error is non-nil Where allFiles is populated as below:
var allFiles []string
templatesDir := "./public/templates/"
files, err := ioutil.ReadDir(templatesDir)
if err != nil {
fmt.Println("Error reading template dir")
}
for _, file := range files {
filename := file.Name()
if strings.HasSuffix(filename, ".html") {
allFiles = append(allFiles, templatesDir+filename)
}
}
For the sake of demonstration of how to parse multiple files we have used the ParseFiles
method to parse all the .html files, you can use the ParseGlob method which is available
in the standard library.
template.Must(template.ParseGlob(templatesDir + "*.html"))
... operator : allFiles is a string slice and allFiles... passes the function a parameter
as a string.
2. ParseFiles performance:
There is one point to note here about performance in parsing files, typically a template
file won't change until there is some major change to the codebase so we should only
parse the files once, rather than keep this code in each view handler and doing a
116
Templates
template.ParseFiles("home.html")
template.Execute(w, context)
This block of code will unnecessarily read the html page each time while serving the
response of a request, which means if ten people are using our blogging site then for
each page they visit the code will read the html page, but there is no need to do things
this way, we can read the html files once at the start and then use the Lookup method
of the template class,
homeTemplate = templates.Lookup("home.html")
homeTemplate.Execute(w, context)
Sub templating
We learnt how to pass data to templates and display it in the html page, it so happens that a
lot of code is used in all templates suppose the navigation bar or the header, then we need
not write the same chunk everywhere, we can create a template to store that, and use sub
templating {{template "_head.html" .}}
Here, we have identified a chunk of code which we want to replicate and put it in
_head.html . Then we put the above statement in each template file where we wish to have
our header. This way templates become a lot smaller and don't contain replicated code
everywhere. Do note that the . before the first } is intentional and it means that all the
variables which were passed to the current template are passed to the sub template.
The sub templates which we create depends on our requirement, but the basic point behind
it is that if we are going to repeat a block of HTML code then we should form it as a
template.
Note Using Sub Templates
The main point to note over here is that when we are going to use our templates or sub
templates, all those html files need to be parsed. The basic point in templating is that we
have a variable which stores all templates even if we aren't going to refer to that directly in
our code using the Lookup method on the template variable. The lookup method takes the
name of the template. When the {{template _head.html .}} is evaluated, it goes to our
template variable and tries to find out the template parsed with the exact name, if it is not
present then it doesn't complain by default, we should use Must method if we want it to
complain.
117
Templates
Example
file views/views.go
package views
import (
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"text/template"
)
var (
homeTemplate *template.Template
deletedTemplate *template.Template
completedTemplate *template.Template
loginTemplate *template.Template
editTemplate *template.Template
searchTemplate *template.Template
templates *template.Template
message string
//message will store the message to be shown as notification
err error
)
//PopulateTemplates is used to parse all templates present in
//the templates folder
func PopulateTemplates() {
var allFiles []string
templatesDir := "./public/templates/"
files, err := ioutil.ReadDir(templatesDir)
if err != nil {
fmt.Println("Error reading template dir")
}
for _, file := range files {
filename := file.Name()
if strings.HasSuffix(filename, ".html") {
allFiles = append(allFiles, templatesDir+filename)
}
}
if err != nil {
fmt.Println(err)
os.Exit(1)
}
templates, err = template.Must(template.ParseFiles(allFiles...))
// templates, err := template.Must(template.ParseGlob(templatesDir+".html"
))
118
Templates
if err != nil {
fmt.Println(err)
os.Exit(1)
}
homeTemplate = templates.Lookup("home.html")
deletedTemplate = templates.Lookup("deleted.html")
editTemplate = templates.Lookup("edit.html")
searchTemplate = templates.Lookup("search.html")
completedTemplate = templates.Lookup("completed.html")
loginTemplate = templates.Lookup("login.html")
}
//ShowAllTasksFunc is used to handle the "/" URL
//TODO add http404 error
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
context := db.GetTasks("pending")
//true when you want non deleted notes
if message != "" {
context.Message = message
}
homeTemplate.Execute(w, context)
message = ""
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
119
Templates
The {{ if .Tasks}} block checks if the array is empty or not, if it is not then it'll go to the {{
.range .Tasks }} which will loop through the array, then the {{.Title}} will access the title
and {{.Content}} will access the Content of that particular Task instance and we'll see all
the tasks as a list.
Template variables
We have this scenario, we have a range of categories in the navigation drawer and if we visit
the /category/study , then our category name should be highlighted in the navigation
drawer. We do this by storing the value of the .Navigation field - which tells if it is a Edit
page/Trash page/Category page
{{$nav := .Navigation}}
{{ range $index, $cat := .Categories }}
<li class="sidebar-item">
<a href="/category/{{$cat.Name}}" {{ if eq $cat.Name $nav }} class="active" {{
end}}>
<span class="nav-item"> {{$cat.Name}}</span> <span class="badge pull-right
">{{$cat.Count}}</span></a>
</li>
{{end}}
Creating variables
{{$url := ""}} will create a blank variable, one has to not that all variables are practically
strings once they are rendered. {{$url := .Navigation}} will create a new variable and
initialize it with the value of .Navigation
For understanding why template variables are required, we need to go into the above block
of code, when we are using the range operator, we are parsing the array and the range
block gets the elements of the block by default.
My Context type is
type Context struct {
Tasks []Task
Navigation string
Search string
Message string
CSRFToken string
Categories []CategoryCount
Referer string
}
120
Templates
This will mark only that particular category as active and not all of them.
In templating logic the operator is first and then the two operands.
eq: equal, le: less than equal, ge: greater than equal
You can also use if else clause like below:
{{$url := ""}}
<div class="navbar-header">
{{if .Search}} <a class="navbar-brand"> Results for: {{.Search}}</a> {{else}} {{ i
f eq .Navigation "pending"}}
{{ $url:="" }} {{else if eq .Navigation "completed"}} {{ $url := "" }} {{else if e
q .Navigation "deleted"}}
{{$url := ""}} {{else if eq .Navigation "edit"}} {{$url := ""}} {{else}} {{$url :=
"/category"}}{{end}}
<p class="navbar-brand" href="{{$url}}/{{.Navigation}}">
{{if eq .Navigation "pending"}} Pending {{ else if eq .Navigation "completed"}
}Completed {{ else if eq .Navigation "deleted"}}Deleted
{{ else if eq .Navigation "edit"}} Edit {{else }} {{.Navigation}} {{end}} {{en
d}}
</p>
Here we had some complicated stuff, if our page is a search one, we had to show Results
for : <query> , pending, deleted,edit, completed for respective and /category/ if we are in
the category. So we defined an empty URL and assigned the URL values according to the
complicated if else structure.
Homework
121
Templates
122
User Authentication
Authentication
Authentication is used to verify if the users have access to that particular part of your web
application. For understanding how to implement authentication we need to understand what
happens behind the scenes of a browser. Suppose we run a bank webapplication. We want
only our legitimate users to access our webapplication. We set up a login page and provide
our users with their username and password which they can use to validate their claim to our
webapp.
When we submit the login form, the browser takes our username, password and sends a
POST request to the webserver, which again responds with a HTTP redirect response and
we are returned to our bank dashboard.
The HTTP protocol is stateless, which means every request is unique. There is no way for
identifying automatically if a request is related to another request. This brings about the
problem of authentication, how then can we validate if the users have access to our
webapp?
We can send the username along with each HTTP request, either in the URL via a GET
request or in the POST request. But this is inefficient since for each request, the webserver
would need to hit the database to validate the username, also this would mean weak
security since if I know your username, I can impersonate you pretty easily and the
webserver is helpless to identify this impersonation.
To solve this problems Sessions were invented, sessions need to use cookies on the
browser to function. The basic idea is that the server generates a sessionID and stores it in
a cookie. With subsequent requests, the browser will send the sessionID along with the
request, the webserver will then come to know from that sessionID if the request is a fake
one or not. Also we get to know who the user is from that.
Cookies
Cookies, as we saw in a previous chapter can be used to store a key,value pair. We used a
cookie to store the CSRF token, the cookie had the name as CSRF and value as the token.
Please don't confuse sessions with cookies, because sessions aren't a key,value pair.
Sessions are a way of working with cookies on the server side. There is a gap of the entire
Internet between sessions and cookies.
123
User Authentication
Cookies are stored in our browsers, for security reasons we need to enable the
"isHTTPOnly" field of our cookies, so only our webapplication can read the cookie.
Otherwise anyone javascript application can easily read our cookie defeating its purpose, we
might as well not keep an authentication mechanism for our webapp.
From the go documentation type Cookie struct { Name string Value string
Path string // optional
Domain string // optional
Expires time.Time // optional
RawExpires string // for reading cookies only
// MaxAge=0 means no 'Max-Age' attribute specified.
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
// MaxAge>0 means Max-Age attribute present and given in seconds
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
The domain of our cookie enables a restricted access to our cookie. A visitor goes to our
fictional bank website, sbank.com and enters a username and password, a cookie is stored
in the browser which only the sbank.com domain can access since we are security aware
and we have set the HttpOnly field to true while setting the cookie. This means some
malicious website which is set up by an attacker to intentionally target our bank isn't able to
access the cookie using javascript.
One has to remember that cookie is nothing but a file stored in a user's browser, if can be
accessed over HTTP by our webserver, a client browser does allow access to it through
browser settings or custom javascript. The browser, after all is a platform, and we have APIs
to that platform.
Sessions
A session is a series of actions performed between you and the webapp you are acting,
enabled by the browser and the Internet you are using. While generating a new session, we
need to check if a sessions is already active, if so we return the same session ID rather than
create a new one, if not, we generate a new session ID. Session IDs need to be sufficiently
random. Of course we can't generate something totally random, but we have to ensure to
generate something that nobody else can replicate, unless they have access to our private
key which we use to generate our random number.
124
User Authentication
125
User Authentication
The below file contains the code to login and logout of our application, we basically are
going to set the "loggedin" property of our cookiestore.
Path: ~/Tasks/Views/sessionViews.go
126
User Authentication
package views
import (
"net/http"
"github.com/thewhitetulip/Tasks/sessions"
)
//LogoutFunc Implements the logout functionality.
//WIll delete the session information from the cookie store
func LogoutFunc(w http.ResponseWriter, r *http.Request) {
session, err := sessions.Store.Get(r, "session")
if err == nil { //If there is no error, then remove session
if session.Values["loggedin"] != "false" {
session.Values["loggedin"] = "false"
session.Save(r, w)
}
}
http.Redirect(w, r, "/login", 302)
//redirect to login irrespective of error or not
}
//LoginFunc implements the login functionality, will
//add a cookie to the cookie store for managing authentication
func LoginFunc(w http.ResponseWriter, r *http.Request) {
session, err := sessions.Store.Get(r, "session")
if err != nil {
loginTemplate.Execute(w, nil)
// in case of error during
// fetching session info, execute login template
} else {
isLoggedIn := session.Values["loggedin"]
if isLoggedIn != "true" {
if r.Method == "POST" {
if r.FormValue("password") == "secret"
&& r.FormValue("username") == "user" {
session.Values["loggedin"] = "true"
session.Save(r, w)
http.Redirect(w, r, "/", 302)
return
}
} else if r.Method == "GET" {
loginTemplate.Execute(w, nil)
}
} else {
http.Redirect(w, r, "/", 302)
}
}
}
127
User Authentication
There is a better way of handling sessions using middleware, we'll introduce that concept in
the next chapter.
Users
Signing users up
The above example just hardcodes the username and password, of course we'd want
people to sign up to our service. We create a user table.
CREATE TABLE user (
id integer primary key autoincrement,
username varchar(100),
password varchar(1000),
email varchar(100)
);
For the sake of simplicity, it'll only contain ID, username, password and email.
There are two parts here, first one where we allow users to sign up, and another part where
we replace the hard coded username and password in our login logic.
file: ~/Tasks/main.go
http.HandleFunc("/signup/", views.SignUpFunc)
file: ~/Tasks/views/sessionViews.go
128
User Authentication
file: ~/Tasks/db/user.go
//CreateUser will create a new user, take as input the parameters and
//insert it into database
func CreateUser(username, password, email string) error {
err := taskQuery("insert into user(username, password, email) values(?,?,?)", user
name, password, email)
return err
}
Login
file ~/Tasks/views/sessionViews.go
129
User Authentication
//LoginFunc implements the login functionality, will add a cookie to the cookie store
for managing authentication
func LoginFunc(w http.ResponseWriter, r *http.Request) {
session, err := sessions.Store.Get(r, "session")
if err != nil {
log.Println("error identifying session")
loginTemplate.Execute(w, nil)
return
}
switch r.Method {
case "GET":
loginTemplate.Execute(w, nil)
case "POST":
log.Print("Inside POST")
r.ParseForm()
username := r.Form.Get("username")
password := r.Form.Get("password")
if (username != "" && password != "") && db.ValidUser(username, password) {
session.Values["loggedin"] = "true"
session.Values["username"] = username
session.Save(r, w)
log.Print("user ", username, " is authenticated")
http.Redirect(w, r, "/", 302)
return
}
log.Print("Invalid user " + username)
loginTemplate.Execute(w, nil)
}
}
file ~/Tasks/db/user.go
130
User Authentication
//ValidUser will check if the user exists in db and if exists if the username password
//combination is valid
func ValidUser(username, password string) bool {
var passwordFromDB string
userSQL := "select password from user where username=?"
log.Print("validating user ", username)
rows := database.query(userSQL, username)
defer rows.Close()
if rows.Next() {
err := rows.Scan(&passwordFromDB)
if err != nil {
return false
}
}
//If the password matches, return true
if password == passwordFromDB {
return true
}
//by default return false
return false
}
Note:
Since we are using gorilla/sessions, we just have to plugin the functionality that the package
provides and don't have to bother about the actual implementation of sessions handling, if
you are interested then by all means go ahead and checkout the source code of
gorilla/sessions!
We keep resetting the password as an exercise, formulate a mechanism to resetting the
password, requires us to create a user table and store some profile information, either emailID from where we'll send a security code or by doing something else! Brainstorm!
131
Files
JSON and XML are two of the most common ways to transmit data between web
applications. We'll use JSON for our configuration file.
For our webapplication we have a set of configuration values like the server port where our
application will run. Suppose you are developing your application in $GOPATH and also
using it somewhere else, then you can't run them in parallel because both sources use the
same port number. Naturally we want a way to parameterize that port number. The
parameter or configuration value list may contain more things like database connection
information. As of now we will use a config.json file and read the serverPort variable and
bind our server on that port.
Our configuration file uses a fixed structure, hence it is simple enough to UnMarshal it to a
struct type, we can use some advance concepts to accomodate unstructured JSON files,
because that is the whole point of JSON, we can have data in an unstructured format.
NoSQL has been famous lately, they are basically JSON document stores. We have projects
like boltdb which store data in a key value pair, ultimately in flat files or in memory.
file: $GOPATH/src/github.com/thewhitetulip/Tasks/config/config.go
132
package config
import (
"encoding/json"
"io/ioutil"
"log"
)
// Stores the main configuration for the application
// define a struct object containing
type Configuration struct {
ServerPort string
}
var err error
var config Configuration
// ReadConfig will read the config.json file to read the parameters
// which will be passed in the config object
func ReadConfig(fileName string) Configuration {
configFile, err := ioutil.ReadFile(fileName)
if err != nil {
log.Fatal("Unable to read log file")
}
//log.Print(configFile)
err = json.Unmarshal(configFile, &config)
if err != nil {
log.Print(err)
}
return config
}
file: $GOPATH/src/github.com/thewhitetulip/Tasks/config.json
{
"ServerPort": ":8081"
}
file: $GOPATH/src/github.com/thewhitetulip/Tasks/main.go
values := config.ReadConfig("config.json")
// values is the object now, we can use the
// below statement to access the port name
values.ServerPort
133
We use the json.Unmarshal to read the JSON file into our structure object. This is a very
simple and basic example of parsing JSON files, you can have nested structures of many
levels inside the main config object, but that is the features of Go, so long as it can be
represented as a JSON document you can use the Unmarshal method to translate the file
into an object which you can use in your program.
Homework
Alter the config.json file to take the name of the sqlite database as a configuration
parameter.
Read about the JSON library in godoc
134
Routing
Routing
Till now we used routing directly inside of our handlers. For a large application though, it'd be
better to have a router in place. We can either use a third party one with countless features
or the standard mux.
As our application matures, routing plays a big role into it. We intentionally avoided routing
till now because as a web developer, we must understand what happens in the background
of our application. Web frameworks allow us to build applications quickly, but the downside
of them is that they provide us with a framework as the name suggests, which means you
are totally restricted by the API which the framework will provide. Thus we need to know how
to implement bare bone stuff, so in future we might want to modify the framework we need,
or rather create our own one.
First of all we'd need to install the httprouter, do a go get -u
github.com/julienschmidt/httprouter
135
Routing
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
"log"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}
func main() {
router := httprouter.New() // creates a new router
router.GET("/", Index) // will direct the GET / request to the Index function
router.GET("/hello/:name", Hello) // will redirect the GET /name to Hello, stores
the name of the parameter
// in the a variable of httprouter.Params
log.Fatal(http.ListenAndServe(":8080", router))
}
136
Routing
rather than that we can have 3 different handlers one for when request comes via AJAX, one
for normal GET and one for normal POST. This way we do not have one function handler
checking the type of request and then writing three separate logic inside one function.
But that doesn't mean we have to use httprouter, if in an app there aren't much sophisticated
routing required then we can use the default Mux since it is good enough, but if we have
complicated routing then httprouter is the best.
Homework
Read the httprouter documentation and source code to get a deeper understanding of
routers, since routers are an integral part of any web application. Then rewrite our
application using the httprouter.
137
Middleware
Middlewares
Middleware is really anything that extends your webapp in a modular way. Most common
examples are probably parsing the request parameters/body and storing them in an easilyaccessible format so you don't have to do it in every single handler, or session handling as
we mentioned in the previous chapter. Other examples could be throttling or IP filtering,
which would also happen before you start building your response, or compression, which
would happen after you've built your response.
//RequiresLogin is a middleware which will be used for each
//httpHandler to check if there is any active session
func RequiresLogin(handler func(w http.ResponseWriter, r *http.Request))
func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if !sessions.IsLoggedIn(r) {
http.Redirect(w, r, "/login/", 302)
return
}
handler(w, r)
}
}
The above function counts as middleware - it doesn't know anything about your app except
how you handle sessions. If you're not logged in, it redirects, otherwise it doesn't do anything
and passes along to the next handler. That next handler might be where you actually build
your response, or it could be another middleware component that does something else first.
To know someone's logged in, yes, you want to create a session identifier and store that
somewhere on the server side (in memory or a database) and also set it in the user's
cookies. Your session IDs should be sufficiently random and long that they couldn't be easily
guessed. I think a common way of satisfying that is creating a UUID and then base64
encode that. Or, you could just generate a bunch of random bytes.
To know which user is logged in, the session ID should be the key that maps to a user ID.
So, you'd make a map of Session ID => User ID, or something similar in your database.
Then, before every request, you'd
1. Check user's cookies for Session ID. If none, user is not logged in.
2. Check your store for user's Session ID. If it's not found, then it's invalid - user is not
logged in.
3. If you found it, use it to look up the user's ID. User is now logged in.
138
Middleware
4. Now you've got the user ID and can use it as a filter when querying your DB if you only
want to show that user's tasks.
Example
Without middleware:
//IsLoggedIn will check if the user has an active session and return True
func IsLoggedIn(r *http.Request) bool {
session, _ := Store.Get(r, "session")
if session.Values["loggedin"] == "true" {
return true
}
return false
}
//SearchTaskFunc is used to handle the /search/ url, handles the search function
func SearchTaskFunc(w http.ResponseWriter, r *http.Request) {
if sessions.IsLoggedin(r) {
if r.Method == "POST" {
r.ParseForm()
query := r.Form.Get("query")
context := db.SearchTask(query)
categories := db.GetCategories()
context.Categories = categories
searchTemplate.Execute(w, context)
}
} else {
http.Redirect(w, r, "/login/", 302)
}
With Middleware:
http.HandleFunc("/", views.RequiresLogin(views.ShowAllTasksFunc))
//SearchTaskFunc is used to handle the /search/ url,
//handles the search function
func SearchTaskFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
r.ParseForm()
query := r.Form.Get("query")
context := db.SearchTask(query)
categories := db.GetCategories()
context.Categories = categories
searchTemplate.Execute(w, context)
}
}
139
Middleware
This way, we do not have to repeat the if sessions.IsLoggedin() block in each of our view
which requires authentication. In this example we have used it for session handling, but it
can be used for any purpose which requires some kind of pre handling of any view.
140
Building an API
Building an API
API stands for Application Programming Interface, it is just an interface to the webapp.
When we use a browser to access a web application, we interact in HTTP and get back
HTML pages, which the browser will render for us. Let's say we want to interact with our web
app to get some data out of it using a host programming language like Go or Python. We'd
have to maintain cookies in Python, or write a python module as an extension of a browser
to handle this scenario.
There is a simple way out of this, we equip our web application itself to interact with any host
language which can talk in HTTP. This way developers can easily get access to our system,
using valid credentials, of course.
Browser:
1. We send the username,password and get a cookie stored on our machine.
2. We use the token in the cookie until it is valid to send HTTP requests.
3. The browser is responsible for rendering the HTML pages sent by the server.
API:
1. We send the username, password and get a token.
2. We send this token in each of our request to the server
Typically we send the token in a custom HTTP header called token.
When we use a browser, the server stores our information as a session, when we send it a
request, it is aware of our session. A web app typically uses cookies to store the session ID,
which is used to identify the user. Such a server is called a stateful server.
When we write APIs, they are stateless servers, they do not store sessions information
anywhere on the server. To it, each request is unique. Which is why, we need to pass along
the authentication token in each request.
Note: Don't mess around with tokens
There are apps where "single sign in" feature is available, the user has to log in only once
and they are logged in forever, this is very dangerous. Because if a malicious person gets
their hands on the security token, they can send malicious requests for data which look
genuine and are impossible to clasify as malicious. Don't do this, always have some
expiration time for security tokens, depends on your application really, two hours, six hours,
but never infinite hours.
141
Building an API
JWT
Javascript Web Tokens is a standard for generating tokens. We will use the jwt-go library.
Lets start by defining our routes
http.HandleFunc("/api/get-task/", views.GetTasksFuncAPI)
http.HandleFunc("/api/get-deleted-task/", views.GetDeletedTaskFuncAPI)
http.HandleFunc("/api/add-task/", views.AddTaskFuncAPI)
http.HandleFunc("/api/update-task/", views.UpdateTaskFuncAPI)
http.HandleFunc("/api/delete-task/", views.DeleteTaskFuncAPI)
http.HandleFunc("/api/get-token/", views.GetTokenHandler)
http.HandleFunc("/api/get-category/", views.GetCategoryFuncAPI)
http.HandleFunc("/api/add-category/", views.AddCategoryFuncAPI)
http.HandleFunc("/api/update-category/", views.UpdateCategoryFuncAPI)
http.HandleFunc("/api/delete-category/", views.DeleteCategoryFuncAPI)
file: main.go
We will have the same URLs for the API, but it'll start with /api/
Our logic is that we will send the username and password in a POST request to /api/gettoken/ that will return the token for us.
file: views/api.go
142
Building an API
import "github.com/dgrijalva/jwt-go"
var mySigningKey = []byte("secret")
//GetTokenHandler will get a token for the username and password
func GetTokenHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
//specify the algorithm to generate token
token := jwt.New(jwt.SigningMethodHS256)
r.ParseForm()
username := r.Form.Get("username")
password := r.Form.Get("password")
if db.ValidUser(username, password) {
/* Set token claims like the usernamd
and the expiration time*/
token.Claims["username"] = username
token.Claims["exp"] = time.Now().Add(time.Hour * 2).Unix()
/* Sign the token with our secret which is a global
variable in the same file*/
tokenString, _ := token.SignedString(mySigningKey)
/* Finally, write the token to the browser window */
w.Write([]byte(tokenString))
} else {
w.Write([]byte("Authentication failed"))
}
}
}
143
Building an API
We'll call the Parse method on the token which we receive as a parameter in the function
call. The token.Valid field is a boolen variable which is true if the token is valid and false
otherwise.
144
Building an API
During an API call, we send data in JSON format, for that, we need to set our content-type
as application/json, by doing this, even a web browser will detect that it is getting a JSON
document. When we need to write a JSON document to the response writer object, we use
145
Building an API
Testing API
We'll use Firefox and RestClient extension to test our API. RestClient allows us to send
various requests to our API server, if you are on Chrome, POSTman is the best alternative.
For RestClient to send Form data, set a custom header Name: Content-Type Value:
application/x-www-form-urlencoded
Otherwise you'll be sending blank POST requests all the time. The server needs to
understand the content type of the data it is getting form the client.
To send the actual form data, example: we have three fields, username, password and
name. Then we write it in the body section like this:
username=thewhitetulip&password=password&name=thewhitetulip
146
Building an API
Also set a custom HTTP header by the Name: token Value: the token which you get in
/api/get-token/ call
147
Contributors
1.
148