More Related Content Similar to entwickler.de Go Day: Go Web Development 101 (20) More from Jan Stamer (10) entwickler.de Go Day: Go Web Development 1012. 3 MONTHS to be productive
74% write API SERVICES
45% write WEB APPS
*
Go Developer Survey 2020 Results
4. HELLO GOPHER
package main
import "fmt"
func main() {
fmt.Println("Hello Gopher!")
}
Run Code
go build hellogopher.go // 1. Compile code
./hellogopher // 2. Run binary
go run hellogopher.go // Compile code + run binary
6. 5 FACTS ABOUT GO
1 static type system
2 garbage collection
3 no inheritance
4 built-in concurrency
5 native execution
Linux,
Win, z/OS, 386, amd64, ARM, wasm, ...
7. VARIABLES, SLICES, LOOPS
// Variable
var kitty string = "Kitty"
bella := "Bella"
1
2
3
4
// Array (fixed length)
5
namesArray := [3]string{kitty, bella, "Cleo"}
6
7
// Slice (variable length)
8
namesSlice := make([]string, 2)
9
namesSlice[0] = kitty
10
11
// Loop
12
for i, name := range namesSlice {
13
fmt.Println("Hello " + name + "!")
14
}
15
// Array (fixed length)
namesArray := [3]string{kitty, bella, "Cleo"}
// Variable
1
var kitty string = "Kitty"
2
bella := "Bella"
3
4
5
6
7
// Slice (variable length)
8
namesSlice := make([]string, 2)
9
namesSlice[0] = kitty
10
11
// Loop
12
for i, name := range namesSlice {
13
fmt.Println("Hello " + name + "!")
14
}
15
// Slice (variable length)
namesSlice := make([]string, 2)
namesSlice[0] = kitty
// Variable
1
var kitty string = "Kitty"
2
bella := "Bella"
3
4
// Array (fixed length)
5
namesArray := [3]string{kitty, bella, "Cleo"}
6
7
8
9
10
11
// Loop
12
for i, name := range namesSlice {
13
fmt.Println("Hello " + name + "!")
14
}
15
// Loop
for i, name := range namesSlice {
fmt.Println("Hello " + name + "!")
}
// Variable
1
var kitty string = "Kitty"
2
bella := "Bella"
3
4
// Array (fixed length)
5
namesArray := [3]string{kitty, bella, "Cleo"}
6
7
// Slice (variable length)
8
namesSlice := make([]string, 2)
9
namesSlice[0] = kitty
10
11
12
13
14
15
8. STRUCT
type Cat struct {
Name string
}
1
2
3
4
func main() {
5
c := Cat{Name: "Kitty"}
6
fmt.Println("Hello " + c.Name + "!")
7
}
8
c := Cat{Name: "Kitty"}
fmt.Println("Hello " + c.Name + "!")
type Cat struct {
1
Name string
2
}
3
4
func main() {
5
6
7
}
8
type Cat struct {
Name string
}
func main() {
c := Cat{Name: "Kitty"}
fmt.Println("Hello " + c.Name + "!")
}
1
2
3
4
5
6
7
8
9. ERRORS
// Error as return value
func (c Cat) feed(food string) error {
if c.Name == "Kitty" && food != "Steak Tartare" {
return errors.New("Won't eat!")
}
return nil
}
1
2
3
4
5
6
7
8
func main() {
9
c := Cat{Name: "Kitty"}
10
11
// Handle error
12
err := c.feed("Caviar")
13
if err != nil {
14
fmt.Printf("%v won't eat it.", c.Name)
15
}
16
}
17
// Handle error
err := c.feed("Caviar")
if err != nil {
fmt.Printf("%v won't eat it.", c.Name)
}
}
// Error as return value
1
func (c Cat) feed(food string) error {
2
if c.Name == "Kitty" && food != "Steak Tartare" {
3
return errors.New("Won't eat!")
4
}
5
return nil
6
}
7
8
func main() {
9
c := Cat{Name: "Kitty"}
10
11
12
13
14
15
16
17
13. SET UP CATS APP
# Create directory
mkdir cats
cd cats
# Enable dependency tracking
go mod init goday.com/cats
# Create go file
touch main.go
14. CAT API SIMPLE
func main() {
http.HandleFunc("/api/cats", catAPIHandler)
http.ListenAndServe(":8080", nil)
}
func catAPIHandler(w http.ResponseWriter, r *http.Request)
1
fmt.Fprintf(w, "Meow!")
2
w.WriteHeader(http.StatusOK)
3
}
4
5
6
7
8
9
func catAPIHandler(w http.ResponseWriter, r *http.Request)
fmt.Fprintf(w, "Meow!")
w.WriteHeader(http.StatusOK)
}
1
2
3
4
5
func main() {
6
http.HandleFunc("/api/cats", catAPIHandler)
7
http.ListenAndServe(":8080", nil)
8
}
9
15. CAT API WITH JSON
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
cats := make([]Cat, 1)
cats[0] = Cat{Name: "Ginger"}
json.NewEncoder(w).Encode(c)
type Cat struct {
1
Name string
2
}
3
4
5
6
7
8
}
9
10
func main() {
11
http.HandleFunc("/api/cats", catAPIHandler)
12
http.ListenAndServe(":8080", nil)
13
}
14
type Cat struct {
Name string
}
1
2
3
4
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
5
cats := make([]Cat, 1)
6
cats[0] = Cat{Name: "Ginger"}
7
json.NewEncoder(w).Encode(c)
8
}
9
10
func main() {
11
http.HandleFunc("/api/cats", catAPIHandler)
12
http.ListenAndServe(":8080", nil)
13
}
14
type Cat struct {
Name string
}
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
cats := make([]Cat, 1)
cats[0] = Cat{Name: "Ginger"}
json.NewEncoder(w).Encode(c)
1
2
3
4
5
6
7
8
}
9
10
func main() {
11
http.HandleFunc("/api/cats", catAPIHandler)
12
http.ListenAndServe(":8080", nil)
13
}
14
type Cat struct {
Name string
}
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
cats := make([]Cat, 1)
cats[0] = Cat{Name: "Ginger"}
json.NewEncoder(w).Encode(c)
}
func main() {
http.HandleFunc("/api/cats", catAPIHandler)
http.ListenAndServe(":8080", nil)
1
2
3
4
5
6
7
8
9
10
11
12
13
}
14
16. CAT API WITH JSON
# Query cat API
curl -s http://localhost:8080/api/cats | jq
1
2
[
3
{
4
"Name": "Ginger"
5
}
6
]
7
[
{
"Name": "Ginger"
}
]
# Query cat API
1
curl -s http://localhost:8080/api/cats | jq
2
3
4
5
6
7
17. CAT API WITH JSON
type Cat struct {
Name string
}
func catAPIHandler(w http.ResponseWriter, r *http.Request) {
cats := make([]Cat, 1)
cats[0] = Cat{Name: "Ginger"}
json.NewEncoder(w).Encode(cats)
1
2 `json:"name"`
3
4
5
6
7
8
}
9
10
func main() {
11
http.HandleFunc("/api/cats", catAPIHandler)
12
http.ListenAndServe(":8080", nil)
13
}
14
18. TEST CAT API cat_api_test.go
func TestCatAPIHandler(t *testing.T) {
}
1
// 1. Create test request
2
req, _ := http.NewRequest("GET", "/api/cats", nil)
3
4
// 2. Create recorder (which satisfies http.ResponseWriter)
5
recorder := httptest.NewRecorder()
6
7
// 3. Invoke handler
8
catAPIHandler(recorder, req)
9
10
// 4. Check the response
11
if recorder.Code != http.StatusOK {
12
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
13
}
14
15
// 1. Create test request
req, _ := http.NewRequest("GET", "/api/cats", nil)
func TestCatAPIHandler(t *testing.T) {
1
2
3
4
// 2. Create recorder (which satisfies http.ResponseWriter)
5
recorder := httptest.NewRecorder()
6
7
// 3. Invoke handler
8
catAPIHandler(recorder, req)
9
10
// 4. Check the response
11
if recorder.Code != http.StatusOK {
12
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
13
}
14
}
15
// 2. Create recorder (which satisfies http.ResponseWriter)
recorder := httptest.NewRecorder()
func TestCatAPIHandler(t *testing.T) {
1
// 1. Create test request
2
req, _ := http.NewRequest("GET", "/api/cats", nil)
3
4
5
6
7
// 3. Invoke handler
8
catAPIHandler(recorder, req)
9
10
// 4. Check the response
11
if recorder.Code != http.StatusOK {
12
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
13
}
14
}
15
// 3. Invoke handler
catAPIHandler(recorder, req)
func TestCatAPIHandler(t *testing.T) {
1
// 1. Create test request
2
req, _ := http.NewRequest("GET", "/api/cats", nil)
3
4
// 2. Create recorder (which satisfies http.ResponseWriter)
5
recorder := httptest.NewRecorder()
6
7
8
9
10
// 4. Check the response
11
if recorder.Code != http.StatusOK {
12
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
13
}
14
}
15
// 4. Check the response
if recorder.Code != http.StatusOK {
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
}
func TestCatAPIHandler(t *testing.T) {
1
// 1. Create test request
2
req, _ := http.NewRequest("GET", "/api/cats", nil)
3
4
// 2. Create recorder (which satisfies http.ResponseWriter)
5
recorder := httptest.NewRecorder()
6
7
// 3. Invoke handler
8
catAPIHandler(recorder, req)
9
10
11
12
13
14
}
15
func TestCatAPIHandler(t *testing.T) {
// 1. Create test request
req, _ := http.NewRequest("GET", "/api/cats", nil)
// 2. Create recorder (which satisfies http.ResponseWriter)
recorder := httptest.NewRecorder()
// 3. Invoke handler
catAPIHandler(recorder, req)
// 4. Check the response
if recorder.Code != http.StatusOK {
t.Errorf("Wrong status: got %v expected %v", recorder.Code, http.Sta
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
19. RUN TEST
# Run tests
go test -v ./...
1
2
=== RUN TestCatAPIHandler
3
--- PASS: TestCatAPIHandler (0.00s)
4
PASS
5
coverage: 50.0% of statements
6
ok devopscon.com/cats 0.127s coverage: 50.0% of stateme
7
=== RUN TestCatAPIHandler
--- PASS: TestCatAPIHandler (0.00s)
PASS
coverage: 50.0% of statements
ok devopscon.com/cats 0.127s coverage: 50.0% of statem
# Run tests
1
go test -v ./...
2
3
4
5
6
7
20. BUILD WITH Makefile
build:
go build -o dist/"${BIN_FILE}"
.DEFAULT_GOAL := build
1
BIN_FILE=cats
2
3
4
5
6
test:
7
go test -v ./...
8
9
run:
10
./"${BIN_FILE}"
11
12
clean:
13
go clean
14
test:
go test -v ./...
.DEFAULT_GOAL := build
1
BIN_FILE=cats
2
3
build:
4
go build -o dist/"${BIN_FILE}"
5
6
7
8
9
run:
10
./"${BIN_FILE}"
11
12
clean:
13
go clean
14
run:
./"${BIN_FILE}"
.DEFAULT_GOAL := build
1
BIN_FILE=cats
2
3
build:
4
go build -o dist/"${BIN_FILE}"
5
6
test:
7
go test -v ./...
8
9
10
11
12
clean:
13
go clean
14
clean:
go clean
.DEFAULT_GOAL := build
1
BIN_FILE=cats
2
3
build:
4
go build -o dist/"${BIN_FILE}"
5
6
test:
7
go test -v ./...
8
9
run:
10
./"${BIN_FILE}"
11
12
13
14
.DEFAULT_GOAL := build
BIN_FILE=cats
build:
go build -o dist/"${BIN_FILE}"
test:
go test -v ./...
run:
./"${BIN_FILE}"
clean:
go clean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
23. CATS HANDLER BASIC TEMPLATE SETUP
func indexHandler(w http.ResponseWriter, r *http.Request) {
var tpl = template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, nil)
1
2
3
}
4
5
func main() {
6
http.HandleFunc("/", indexHandler)
7
http.ListenAndServe(":8080", nil)
8
}
9
func indexHandler(w http.ResponseWriter, r *http.Request) {
var tpl = template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, nil)
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe(":8080", nil)
}
1
2
3
4
5
6
7
8
9
func indexHandler(w http.ResponseWriter, r *http.Request) {
var tpl = template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, nil)
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe(":8080", nil)
}
1
2
3
4
5
6
7
8
9
24. CATS HANDLER WITH MULTIPLEXER
mux := http.NewServeMux()
mux.HandleFunc("/", indexHandler)
func indexHandler(w http.ResponseWriter, r *http.Request) {
1
var tpl = template.Must(template.ParseFiles("index.html"))
2
tpl.Execute(w, nil)
3
}
4
5
func main() {
6
7
8
9
http.ListenAndServe(":8080", mux)
10
}
11
http.ListenAndServe(":8080", mux)
func indexHandler(w http.ResponseWriter, r *http.Request) {
1
var tpl = template.Must(template.ParseFiles("index.html"))
2
tpl.Execute(w, nil)
3
}
4
5
func main() {
6
mux := http.NewServeMux()
7
mux.HandleFunc("/", indexHandler)
8
9
10
}
11
func indexHandler(w http.ResponseWriter, r *http.Request) {
var tpl = template.Must(template.ParseFiles("index.html"))
tpl.Execute(w, nil)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", indexHandler)
http.ListenAndServe(":8080", mux)
}
1
2
3
4
5
6
7
8
9
10
11
25. SERVE FILES FROM FILESYSTEM
// Serve files
fs := http.FileServer(http.Dir("assets"))
mux.Handle("/assets/", http.StripPrefix("/assets/", fs))
func main() {
1
mux := http.NewServeMux()
2
mux.HandleFunc("/", indexHandler)
3
4
5
6
7
8
http.ListenAndServe(":8080", mux)
9
}
10
26. SERVE FILES EMBEDDED IN BINARY
// Serve files
mux.Handle("/assets/", http.FileServer(http.FS(assets)))
//go:embed assets
1
var assets embed.FS
2
3
func main() {
4
mux := http.NewServeMux()
5
mux.HandleFunc("/", indexHandler)
6
7
8
9
10
http.ListenAndServe(":8080", mux)
11
}
12
//go:embed assets
var assets embed.FS
// Serve files
mux.Handle("/assets/", http.FileServer(http.FS(assets)))
1
2
3
func main() {
4
mux := http.NewServeMux()
5
mux.HandleFunc("/", indexHandler)
6
7
8
9
10
http.ListenAndServe(":8080", mux)
11
}
12
mux.HandleFunc("/", indexHandler)
mux.Handle("/assets/", http.FileServer(http.FS(assets)))
//go:embed assets
1
var assets embed.FS
2
3
func main() {
4
mux := http.NewServeMux()
5
6
7
// Serve files
8
9
10
http.ListenAndServe(":8080", mux)
11
}
12
28. CATS HANDLER TEMPLATE WITH DATA
main.go
type Cat struct {
Name string
}
func indexHandler(w ResponseWriter, r *Request) {
// Create cat slice
cat := make([]Cat, 1)
// Add cat ginger
cat[0] = Cat{Name: "Ginger"}
// Render template
tpl.Execute(w, cat)
}
index.html
<body>
<h1>Cats App</h1>
{{ range. }}
<h2>{{ .Name }}</h2>
{{ end }}
</body>
30. CATS HANDLER WITH CATS API
Query Cats API
GET https://api.thecatapi.com/v1/breeds?limit=5
[
{
"id": "abys",
"name": "Abyssinian",
"image": {
"url": "https://cdn2.thecatapi.com/images/0XYvRd7oD.jpg
}
},
{
"id": "aege",
"name": "Aegean",
"image": {
"url": "https://cdn2.thecatapi.com/images/ozEvzdVM-.jpg
}
},
...
]
Map JSON
type Cat struct {
ID string `json:"id"`
Name string `json:"name"`
Image struct {
URL string `json:"url"`
} `json:"image"`
}
32. CATS HANDLER WITH CATS API
resp, err := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
if err != nil {
http.Error(w, "Cats API error", http.StatusInternalServerError)
return
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
1
2
3
4
5
6
7
// Create cat slice
8
cat := make([]Cat, 5)
9
10
// Read and parse body
11
defer resp.Body.Close()
12
body, _ := ioutil.ReadAll(resp.Body)
13
json.Unmarshal(body, &cat)
14
15
tpl.Execute(w, cat)
16
}
17
// Create cat slice
cat := make([]Cat, 5)
func indexHandler(w http.ResponseWriter, r *http.Request) {
1
resp, err := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
2
if err != nil {
3
http.Error(w, "Cats API error", http.StatusInternalServerError)
4
return
5
}
6
7
8
9
10
// Read and parse body
11
defer resp.Body.Close()
12
body, _ := ioutil.ReadAll(resp.Body)
13
json.Unmarshal(body, &cat)
14
15
tpl.Execute(w, cat)
16
}
17
// Read and parse body
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(body, &cat)
func indexHandler(w http.ResponseWriter, r *http.Request) {
1
resp, err := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
2
if err != nil {
3
http.Error(w, "Cats API error", http.StatusInternalServerError)
4
return
5
}
6
7
// Create cat slice
8
cat := make([]Cat, 5)
9
10
11
12
13
14
15
tpl.Execute(w, cat)
16
}
17
func indexHandler(w http.ResponseWriter, r *http.Request) {
resp, err := http.Get("https://api.thecatapi.com/v1/breeds?limit=5")
if err != nil {
http.Error(w, "Cats API error", http.StatusInternalServerError)
return
}
// Create cat slice
cat := make([]Cat, 5)
// Read and parse body
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(body, &cat)
tpl.Execute(w, cat)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
33. MIDDLEWARE
cross cutting functionality for all requests
(e.g. logging, authentication)
#
create a chain of handlers
router => middleware handler => application handler
#
satisfy the interface http.Handler
#
34. MIDDLEWARE TO LOG REQUESTS
http.ListenAndServe(":8080", loggingMiddleware(mux))
func loggingMiddleware(next http.Handler) http.Handler {
1
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Reque
2
log.Printf("%v requested URL %v", r.Host, r.URL)
3
next.ServeHTTP(w, r)
4
})
5
}
6
7
func main() {
8
mux := http.NewServeMux()
9
mux.HandleFunc("/", indexHandler)
10
11
12
}
13
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Reque
log.Printf("%v requested URL %v", r.Host, r.URL)
next.ServeHTTP(w, r)
})
}
1
2
3
4
5
6
7
func main() {
8
mux := http.NewServeMux()
9
mux.HandleFunc("/", indexHandler)
10
11
http.ListenAndServe(":8080", loggingMiddleware(mux))
12
}
13
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Reque
log.Printf("%v requested URL %v", r.Host, r.URL)
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", indexHandler)
http.ListenAndServe(":8080", loggingMiddleware(mux))
1
2
3
4
5
6
7
8
9
10
11
12
}
13
36. BASICS
Dev Setup
Variables,
Slices, Loops
Struct
Errors
#
#
#
#
CATS APP
API
Handler (with
JSON)
Http Test
Build
#
#
#
CATS APP
WEB
Multiplexer
(Router)
File Server
Template
Middleware
#
#
#
#