Go HTTP web server návod časť 3 - statické stránky so šablónami

Sunday, Sep 13, 2020 by Nelo

Dnes si vytvoríme jednoduchý webserver statických stránok so šablónami

Go webserver so šablónami

Go obsahuje balíček html/template, ktorý implementuje prácu so šablonami pre generovanie HTML na základe šablony a dynamických dát. Šablony sú zabezpečené proti vložení a spustení kódu, predpokladajú, že tvorca šablon je dôveryhodný, ale ale kto ju spúšťa s dátami tak nie, preto použité dynamické dáta budú sanitizované a escapované špecifické znaky pre HTML, CSS, JavaScript a URI.

Podstránky

  • / vráti stránku vyskladanú z layout.html + index.html
  • /hello.html vráti stránku vyskladanú z layout.html + hello.html
  • /static/… vráti statický súbor so správnym content-type

Adresárová štruktúra:

1
2
3
4
5
6
7
8
9
$ ls -R
.:
main.go  static/  templates/

./static:
main.css

./templates:
hello.html  index.html  layout.html

Statické súbory

Najskôr si vytvoríme statické súbory:

/static/main.css

1
body {color: #0099cc}

/templates/layout.html

Aby sme nemuseli pre každú stránku kopírovať všetok spoločný obsah, použijeme podporu šablón z balíčka Go template. Layout súbor bude obsahovať spoločné prvky pre všetky stránky a jeho názov bude “layout”:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{{define "layout"}}
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>{{template "title"}}</title>
    <link rel="stylesheet" href="/static/main.css">
</head>
<body>
    <a href="/">Home</a> | <a href="/hello.html">Hello</a>
    {{template "body"}}
    <footer>created by Go templates at {{.}}</footer>
</body>
</html>
{{end}}

/templates/index.html

Index.html sa zobrazí buď keď priamo zavoláme /index.html alebo /.

1
2
3
4
{{define "title"}}Index{{end}}
{{define "body"}}
<h1>This is index page</h1>
{{end}}

/templates/hello.html

1
2
3
4
{{define "title"}}Template page{{end}}
{{define "body"}}
<h1>Hello from the other world</h1>
{{end}}

Main

Http balíček obsahuje funkciu FileServer, tá vracia handler, ktorý obsluhuje HTTP request vrátením statického súboru z určenej podadresárovej štruktúry. Tento handler nastavíme na obsluhu cesty /static. Ostatné HTTP requesty bude obsluhovať funkcia serveTemplate, ktorú si definujeme nižšie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func main() {
	fs := http.FileServer(http.Dir("./static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.HandleFunc("/", serveTemplate)

	log.Println("Listening on :8000...")
	err := http.ListenAndServe(":8000", nil)
	if err != nil {
		log.Fatal(err)
	}
}

serveTemplate handler

Riadky:

  • 2-3: vytvoria premenné s cestou na layout a volaný súbor
  • 6: os.Stat vracia FileInfo štruktúru popisujúcu súbor alebo chybu ak neexistuje
  • 7-10: ak súbor existuje ale je to adresár, tak prepíše volanie na index.html z daného adresára
  • 13-18: ak súbor neexistuje vráti 404
  • 20-27: vytvorí Template objekt zo šablón alebo vráti chybu 500
  • 29-33: na výstup vyrenderuje šablónu “layout” a doplní aktuálny čas a dátum, v prípade chyby vráti 500
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func serveTemplate(w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(r.URL.Path))

	// Return the index page if the request is for a directory
	info, err := os.Stat(fp)
	if err == nil && info.IsDir() {
		fp = filepath.Join("templates", filepath.Clean(r.URL.Path+"/index.html"))
		info, err = os.Stat(fp)
	}

	// Return a 404 if the template doesn't exist
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		// Log the detailed error
		log.Println(err.Error())
		// Return a generic "Internal Server Error" message
		http.Error(w, http.StatusText(500), 500)
		return
	}

	err = tmpl.ExecuteTemplate(w, "layout", time.Now().Format("2006.01.02 15:04:05"))
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

Celý zdrojový kód

Zdrojový kód môžete uložiť do súboru “main.go” a spustiť príkazom “go run main.go”, následne v prehliadači otvoriť http://localhost:8000:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main

import (
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

func main() {
	fs := http.FileServer(http.Dir("./static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.HandleFunc("/", serveTemplate)

	log.Println("Listening on :8000...")
	err := http.ListenAndServe(":8000", nil)
	if err != nil {
		log.Fatal(err)
	}
}

func serveTemplate(w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(r.URL.Path))

	// Return the index page if the request is for a directory
	info, err := os.Stat(fp)
	if err == nil && info.IsDir() {
		fp = filepath.Join("templates", filepath.Clean(r.URL.Path+"/index.html"))
		info, err = os.Stat(fp)
	}

	// Return a 404 if the template doesn't exist
	if err != nil {
		if os.IsNotExist(err) {
			http.NotFound(w, r)
			return
		}
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		// Log the detailed error
		log.Println(err.Error())
		// Return a generic "Internal Server Error" message
		http.Error(w, http.StatusText(500), 500)
		return
	}

	err = tmpl.ExecuteTemplate(w, "layout", time.Now().Format("2006.01.02 15:04:05"))
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

Referencie

Vaše otázky, návrhy a komentáre

Verím, že vás tento návod inšpiroval a budem vďačný ak dáte spätnú väzbu a pomôže mi zamerať sa na to čo by vás zaujímalo.

TAK ČO HOVORÍŠ ?

Kontaktuj nás ak potrebuješ pomoc, veľmi radi pomôžeme.

Kontaktuj nás