เว็บแอปพลิเคชันพื้นฐาน
Web Application (เว็บ แอพพลิเคชั่น) คือ Application ที่ถูกเขียนขึ้นมาเพื่อเป็น Browser (บราวเซอร์) สำหรับการใช้งานเว็บเพจต่างๆ ซึ่งถูกปรับแต่งให้แสดงผลแต่ส่วนที่จำเป็น เพื่อเป็นการลดทรัพยากรในการประมวลผล ของตัวเครื่องสมาร์ทโฟน หรือ แท็บเล็ต ทำให้โหลดหน้าเว็บไซต์ได้เร็วขึ้น อีกทั้งผู้ใช้งานยังสามารถใช้งานผ่าน อินเตอร์เน็ตและอินทราเน็ตได้ โดยการสร้าง Web Application สิ่งที่ต้องเรียนรู้ คือ ภาษา HTML, CSS, Go และภาษา SQL เป็นต้น
ภาษา HTML นั้นเป็นภาษาที่ใช้ในการออกแบบและตกแต่งหน้าตาของ From ในเว็บไซต์ หรือ Web Application เช่น การแสดงรูปแบบตัวอักษรที่เป็นแบบ ตัวหนา เอียง ขีดเส้นใต้ หรือจะเป็นการแสดงแบบรูปภาพ หรือตาราง เป็นต้น
ภาษา Go เป็นภาษาที่คอยประมวลผล โดยทำหน้าที่รับหรืออ่านคำสั่งที่คุณเขียนโค้ดไว้ด้วยภาษา Go แล้วทำงานตามคำสั่งเหล่านั้น เช่น การรับค่าแล้วเก็บค่าลงในตัวแปร การตัดสินใจ(การทำซ้ำ, การวนลูป) หรืออาจจะทำงานที่ซับซ้อนขึ้น เช่น การอัพไฟล์ข้อมูล การติดต่อกับฐานข้อมูล เป็นต้น
ภาษา SQL เป็นภาษามาตรฐานสำหรับการทำงานร่วมกับฐานข้อมูลเชิงสัมพันธ์ ซึ่งใช้ในการจัดการฐานข้อมูลของ Web Application ที่เราสร้างขึ้นมานั่นเอง
ข้อกำหนดเบื้องต้น
คุณต้องติดตั้ง Go บนคอมพิวเตอร์ของคุณตามบทความ ติดตั้ง Go และ ทดสอบ Hello World และ คุณคุ้นเคยกับรูปแบบและโครงสร้างของมัน ด้วยการผ่านการเรียนรู้บทความ Workshop-2 New Deck & Print มาก่อน
สร้างโปรเจคใหม่
สร้างโปรเจคใหม่ โดยไปที่ File -> Open Folder -> New Folder ตั้งชื่อเป็น modern-web
สร้าง Go Modules คือสิ่งที่สร้างขึ้นมาเพื่อจัดการ packages ต่างๆ สามารถกำหนดเวอร์ชั่นได้และมีการจัดการที่มีประสิทธิภาพมากขึ้น
สร้างไฟล์ Go Modules ด้วยคำสั่ง
go mod init modern-web

สร้างแพคเกจ main
สร้างโฟลเดอร์ cmd แล้วสร้างโฟลเดอร์ web ภายในโฟลเดอร์ cmd และสร้างไฟล์ main.go เขียนโค้ดสร้างแพคเกจ main และ ฟังก์ชัน main
package main
func main() {
}

สร้างเว็บเซิร์ฟเวอร์
สร้างเว็บเซิร์ฟเวอร์ ด้วยการนำเข้าแพคเกจ “net/http”
import (
"net/http"
)
const คือการประกาศค่าคงที่ ชื่อ portNumber มีค่าเป็น :8080
const portNumber = ":8080"
ListenAndServe รับ Parameter 2 ตัว คือ
Port ที่เราต้องการให้ Request จาก Client ส่งเข้ามา ณ ที่นี้คือ Port 8080 ตามค่าของ portNumber
Option สำหรับเลือกใช้ตัว Handle Request ถ้าใส่เป็น nil คือ “ว่างเปล่า” คือจะใช้ Default ในการ Handle Request
_ = http.ListenAndServe(portNumber, nil)

สร้าง Template
ในการพัฒนาระบบ Web Aplication ด้วย Go สามารถพัฒนาร่วมกับ html template ได้ มีลักษณะเช่นเดียวกับการใช้ Template engine ต่างๆ แต่ Go นั้นมี package html/template มาให้เราใช้งานอยู่แล้ว ดังนั้นมาดูกันว่าเราจะพัฒนากันอย่างไร
สร้างโฟลเดอร์ templates แล้ว สร้างไฟล์ base.layout.tmpl มีโค้ดดังนี้
{{define "base"}}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l"
crossorigin="anonymous">
{{block "css" .}}
{{end}}
</head>
<body>
{{block "content" .}}
{{end}}
{{block "js" .}}
{{end}}
</body>
</html>
{{end}}
สร้างไฟล์ home.page.tmpl มีโค้ดดังนี้
{{template "base" .}}
{{define "content"}}
<div class="container">
<div class="row">
<div class="col">
<h1>This is the home page</h1>
<p>This is some text</p>
</div>
</div>
</div>
{{end}}
สร้างไฟล์ about.page.tmpl มีโค้ดดังนี้
{{template "base" .}}
{{define "content"}}
<div class="container">
<div class="row">
<div class="col">
<h1>This is the about page</h1>
</div>
</div>
</div>
{{end}}

สร้างแพคเกจ handlers
สร้างโฟลเดอร์ pkg แล้วสร้างโฟลเดอร์ handlers ภายในโฟลเดอร์ pkg และสร้างไฟล์ handlers.go เขียนโค้ดดังนี้
package handlers
import (
"modern-web/pkg/render"
"net/http"
)
// Home is the handler for the home page
func Home(w http.ResponseWriter, r *http.Request) {
render.RenderTemplate(w, "home.page.tmpl")
}
// About is the handler for the about page
func About(w http.ResponseWriter, r *http.Request) {
render.RenderTemplate(w, "about.page.tmpl")
}

สร้างแพคเกจ render
สร้างโฟลเดอร์ render ภายในโฟลเดอร์ pkg และสร้างไฟล์ render.go เขียนโค้ดดังนี้
package render
import (
"bytes"
"fmt"
"html/template"
"log"
"net/http"
"path/filepath"
)
var functions = template.FuncMap{}
// RenderTemplate renders a template
func RenderTemplate(w http.ResponseWriter, tmpl string) {
// get the template cache from the app config
tc, err := CreateTemplateCache()
if err != nil {
log.Fatal(err)
}
t, ok := tc[tmpl]
if !ok {
log.Fatal(err)
}
buf := new(bytes.Buffer)
_ = t.Execute(buf, nil)
_, err = buf.WriteTo(w)
if err != nil {
fmt.Println("error writing template to browser", err)
}
}
// CreateTemplateCache creates a template cache as a map
func CreateTemplateCache() (map[string]*template.Template, error) {
myCache := map[string]*template.Template{}
pages, err := filepath.Glob("./templates/*.page.tmpl")
if err != nil {
return myCache, err
}
for _, page := range pages {
name := filepath.Base(page)
ts, err := template.New(name).Funcs(functions).ParseFiles(page)
if err != nil {
return myCache, err
}
matches, err := filepath.Glob("./templates/*.layout.tmpl")
if err != nil {
return myCache, err
}
if len(matches) > 0 {
ts, err = ts.ParseGlob("./templates/*.layout.tmpl")
if err != nil {
return myCache, err
}
}
myCache[name] = ts
}
return myCache, nil
}

นำเข้าแพคเกจ handlers
แก้ไขไฟล์ main.go นำเข้าแพคเกจ handlers เพื่อจัดการเส้นทาง ด้วยคำสั่ง
"modern-web/pkg/handlers"
จัดการเส้นทาง 2 เส้นทาง ไปยัง หน้า Home และ หน้า About ด้วยคำสั่ง
http.HandleFunc("/", handlers.Home)
http.HandleFunc("/about", handlers.About)

นำเข้าแพคเกจ “fmt”
นำเข้าแพคเกจ “fmt” ซึ่งภายใต้ fmt มีฟังก์ชันชื่อ Println ให้เราสามารถพิมพ์ข้อความได้ ด้วยคำสั่ง
fmt.Println(fmt.Sprintf("Staring application on port %s", portNumber))

ทดสอบการทำงาน
ทดสอบการทำงาน ด้วยคำสั่ง
go run ./cmd/web/ .

ทดสอบการทำงานโดยไปที่ หน้า Home http://localhost:8080/

และเมื่อเปลี่ยนไปที่ หน้า About http://localhost:8080/about

การแชร์ข้อมูลกับเทมเพลต
สร้างโฟลเดอร์ config ภายในโฟลเดอร์ pkg และสร้างไฟล์ config.go เขียนโค้ดดังนี้
package config
import (
"html/template"
"log"
)
// AppConfig holds the application config
type AppConfig struct {
UseCache bool
TemplateCache map[string]*template.Template
InfoLog *log.Logger
}

สร้างโฟลเดอร์ models ภายในโฟลเดอร์ pkg และสร้างไฟล์ templatedata.go เขียนโค้ดดังนี้
package models
// TemplateData holds data sent from handlers to templates
type TemplateData struct {
StringMap map[string]string
IntMap map[string]int
FloatMap map[string]float32
Data map[string]interface{}
CSRFToken string
Flash string
Warning string
Error string
}

ไฟล์ render.go แก้ไขดังนี้
package render
import (
"modern-web/pkg/config"
"modern-web/pkg/models"
"bytes"
"fmt"
"html/template"
"log"
"net/http"
"path/filepath"
)
var functions = template.FuncMap{}
var app *config.AppConfig
// NewTemplates sets the config for the template package
func NewTemplates(a *config.AppConfig) {
app = a
}
func AddDefaultData(td *models.TemplateData) *models.TemplateData {
return td
}
// RenderTemplate renders a template
func RenderTemplate(w http.ResponseWriter, tmpl string, td *models.TemplateData) {
var tc map[string]*template.Template
if app.UseCache {
// get the template cache from the app config
tc = app.TemplateCache
} else {
tc, _ = CreateTemplateCache()
}
t, ok := tc[tmpl]
if !ok {
log.Fatal("Could not get template from template cache")
}
buf := new(bytes.Buffer)
td = AddDefaultData(td)
_ = t.Execute(buf, td)
_, err := buf.WriteTo(w)
if err != nil {
fmt.Println("error writing template to browser", err)
}
}
// CreateTemplateCache creates a template cache as a map
func CreateTemplateCache() (map[string]*template.Template, error) {
myCache := map[string]*template.Template{}
pages, err := filepath.Glob("./templates/*.page.tmpl")
if err != nil {
return myCache, err
}
for _, page := range pages {
name := filepath.Base(page)
ts, err := template.New(name).Funcs(functions).ParseFiles(page)
if err != nil {
return myCache, err
}
matches, err := filepath.Glob("./templates/*.layout.tmpl")
if err != nil {
return myCache, err
}
if len(matches) > 0 {
ts, err = ts.ParseGlob("./templates/*.layout.tmpl")
if err != nil {
return myCache, err
}
}
myCache[name] = ts
}
return myCache, nil
}
ไฟล์ handlers.go แก้ไขดังนี้
package handlers
import (
"modern-web/pkg/config"
"modern-web/pkg/models"
"modern-web/pkg/render"
"net/http"
)
// Repo the repository used by the handlers
var Repo *Repository
// Repository is the repository type
type Repository struct {
App *config.AppConfig
}
// NewRepo creates a new repository
func NewRepo(a *config.AppConfig) *Repository {
return &Repository{
App: a,
}
}
// NewHandlers sets the repository for the handlers
func NewHandlers(r *Repository) {
Repo = r
}
// Home is the handler for the home page
func (m *Repository) Home(w http.ResponseWriter, r *http.Request) {
render.RenderTemplate(w, "home.page.tmpl", &models.TemplateData{})
}
// About is the handler for the about page
func (m *Repository) About(w http.ResponseWriter, r *http.Request) {
// perform some logic
stringMap := make(map[string]string)
stringMap["test"] = "Hello, again"
// send data to the template
render.RenderTemplate(w, "about.page.tmpl", &models.TemplateData{
StringMap: stringMap,
})
}
ไฟล์ about.page.tmpl แก้ไขดังนี้
{{template "base" .}}
{{define "content"}}
<div class="container">
<div class="row">
<div class="col">
<h1>This is the about page</h1>
<p>This is a paragraph of text</p>
<p>This is a paragraph of text</p>
<p>This came from the template: {{index .StringMap "test"}}</p>
</div>
</div>
</div>
{{end}}
ไฟล์ main.go แก้ไขดังนี้
package main
import (
"modern-web/pkg/config"
"modern-web/pkg/handlers"
"modern-web/pkg/render"
"fmt"
"log"
"net/http"
)
const portNumber = ":8080"
// main is the main function
func main() {
var app config.AppConfig
tc, err := render.CreateTemplateCache()
if err != nil {
log.Fatal("cannot create template cache")
}
app.TemplateCache = tc
app.UseCache = false
repo := handlers.NewRepo(&app)
handlers.NewHandlers(repo)
render.NewTemplates(&app)
http.HandleFunc("/", handlers.Repo.Home)
http.HandleFunc("/about", handlers.Repo.About)
fmt.Println(fmt.Sprintf("Staring application on port %s", portNumber))
_ = http.ListenAndServe(portNumber, nil)
}
ทดสอบการทำงาน ไปที่ หน้า About http://localhost:8080/about

credit : https://www.udemy.com/course/building-modern-web-applications-with-go/