สรุปสั้น ๆ
สวัสดีครับ! วันนี้เราจะมาใช้ Go เจ้าภาษาที่มีความนิยมในการนำมาพัฒนาเว็บต่าง ๆ ด้วยฟีเจอร์หลายอย่างที่ออกแบบมาสำหรับการทำ Web Service (แต่ไม่ได้ใช้ทำได้แค่ Web Service เท่านั้นนะ จะเอาไปทำ CLI แอป โปรแกรมที่ใช้บนคลาวด์ หรือ ด้าน Network ก็ได้) ในบทความนี้ เราจะมาดูว่าถ้าเป็นมือใหม่หัด Dev อยากลองทำ Web Service ด้วย Go จะเริ่มยังไงดี มาเริ่มกันเล้ย!
เขียนโดย
Sirasit Boonklang (Aeff)
Tech and Coding Consultant
บทความนี้ตีพิมพ์ และ เผยแพร่เมื่อ 16 กุมภาพันธ์ 2566
ก่อนที่เราจะเป็นวัยรุ่นใจร้อนเขียนโค้ดเลย เรามาทำความเข้าใจสิ่งสำคัญก่อนว่า Web Service คืออะไร
Web Service คือ วิธีสำหรับสองโปรแกรมที่แตกต่างกันในการสื่อสารระหว่างกันผ่านอินเทอร์เน็ต
โดยในการสร้าง Web Service ด้วย Go สามารถทำได้หลายแบบไม่ว่าจะเป็นใช้ Framework หรือจะใช้เป็น Standard Package อย่าง net/http ที่มาพร้อมกับ Go อยู่แล้วก็ได้ ในแพ็คเกจนี้มีเครื่องมือที่จำเป็นทั้งหมดในการสร้าง Web Service รวมถึงการกำหนด Route การให้บริการไฟล์ และการจัดการ HTTP requests/responses ต่าง ๆ
เมื่อเราได้รู้จักกับ Web Service และเหตุผลในการใช้ Go เบื้องต้นกันไปแล้ว มา! มาลุยโค้ดกัน 🚀
ในการสร้างโปรเจกต์ Go เราจะต้องสร้างไฟล์ go.mod ในการเก็บ Path หรือเวอร์ชันต่าง ๆ ก่อนโดยใช้คำสั่ง go mod init <path_name> เช่น go mod init github.com/<username>/simple-web-service-go หลังจากนั้นจะได้ไฟล์ go.mod ขึ้นมา
จากนั้นเรามาเริ่มจากการสร้างเว็บเซิร์ฟเวอร์พื้นฐานที่ return คำว่า “Hello, World!” เมื่อเราเรียกใช้ผ่าน HTTP ที่เป็น localhost:8081 เพื่อเอาฤกษ์เอาชัยกันก่อน สร้างไฟล์ main.go แล้วเขียนโค้ดตามด้านล่างเลย
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
http.ListenAndServe(":8081", nil)
}
โค้ดด้านบนคือเอาไว้ทำ HTTP server ที่รอการทำงานเมื่อมีการร้องขอมาที่พอร์ต 8081 ส่วนของ func http.HandleFunc คือฟังก์ชันที่ใช้จัดการกับ request และ response
โดยเมื่อเราเราเขียนโค้ดเสร็จรันด้วยคำสั่ง go run main.go
เดี๋ยวผมขอกลับมาอธิบาย ฟังก์ชัน http.HandleFunc ให้ละเอียดยิ่งขึ้นหน่อยนะครับ ฟังก์ชันนี้ใช้สอง Argument ที่เป็น string มันจะแสดง Route URL ที่จะจับคู่กันกับฟังก์ชัน เมื่อมี Request ที่เข้ามาตรงกับ Route หรือ Path ที่ตั้งไว้มันก็จะทำงานในฟังก์ชันนั้น โดยฟังก์ชันนี้ เรากำลังใช้ Route URL ที่เป็น Root หรือ (”/”) นั้นเอง และ ฟังก์ชันรับ http.ResponseWriter และ http.Request เป็นอาร์กิวเมนต์
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request)
http.ResponseWriter ใช้เพื่อเขียนการตอบกลับกลับไปยัง Client ในกรณีนี้เรากำลังใช้ฟังก์ชัน
fmt.Fprintf เพื่อเขียนข้อความ “Hello, World!” ตอบกลับ
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
http.Request เมื่อมีข้อมูลเกี่ยวกับคำขอที่เข้ามา เช่น HTTP method (GET, POST เป็นต้น) Header และข้อมูลใด ๆ ที่ส่งมาพร้อมกับ Request
ต่อมาเรามาเรามาสร้าง Path เพิ่มเติม
ตอนนี้เรามีเว็บเซิร์ฟเวอร์พื้นฐานและทำงานแล้ว เรามาเพิ่มฟังก์ชันกันหน่อย มาสร้างอีก Endpoint ที่เป็น Path สำหรับทักทาย แล้วรับค่าจาก user มาด้วยกันหน่อยดีกว่า
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
http.HandleFunc("/greet/", func(w http.ResponseWriter, r *http.Request) {
name := r.URL.Path[len("/greet/"):]
fmt.Fprintf(w, "Hello, %s!", name)
})
http.ListenAndServe(":8080", nil)
}
ในโค้ดนี้ เราได้เพิ่ม Endpoint ใหม่ที่ชื่อว่า (“/greet/”) มีการรับ Parameter ใน URL ด้วย โดยการใช้ตัวแปร r.URL.Path สำหรับเอา URL ของ Route แบบเต็มมาก่อน จากนั้นใช้ฟังก์ชัน len เพื่อรับความยาวของ “/greet/” จากนั้นเแบ่งส่วนเพื่อรับชื่อจาก URL Route
เราสามารถทดสอบได้โดยเข้าไปที่ http://localhost:8081/greet/borntodev ในเว็บเบราว์เซอร์ แล้วระบบจะการตอบกลับว่า “Hello, borntoDev!”
ต่อมาเรามาเพิ่มฟีเจอร์เข้าไปเพิ่มสักหน่อย อย่างเช่น Middleware, Goroutine และการสร้างหน้า HTML ขึ้นมา
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("[%s] %s %s\n", time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL)
next.ServeHTTP(w, r)
})
}
แต่ละพีเจอร์ที่เพิ่มเข้ามามีอะไรบ้างมาดูกันครับ
- Middleware: มิดเดิลแวร์เป็นฟีเจอร์ที่ช่วยเป็นเหมือนกำแพงในการสกัดกั้น Request ที่เข้ามาและไปประมวลผลต่อเพิ่มเติมก่อนที่จะส่งต่อไปยัง main handler function เช่น เราสามารถใช้มิดเดิลแวร์เพื่อ แสดง Log ของ Request ที่เข้ามา ใช้ทำ Authentication หรือ Authorization ในการยืนยันตัวหรือการตรวจสอบสิทธิ์ ไปจนถึงแก้ไขส่วน Request header
- Goroutines: Goroutines สามารถใช้จัดการ Request หลายรายการพร้อมกันในส่วนประมวลผลเดียวกัน ทำให้เราสามารถรีดประสิทธิภาพ และ ความสามารถในการสเกล Web Service ของเราได้ใน Go
- Templates: เราสามารถสร้างหน้า HTML แบบไดนามิกและเนื้อหาอื่นๆ ในหน้าเว็บได้ โดยใช้เทมเพลตเพื่อสร้างเลย์เอาต์ที่ใช้ซ้ำได้บนหน้าเว็บ และเติมข้อมูลจากโค้ด Go
แล้วโค้ดแต่ละบรรทัดคืออะไรหละ
loggingMiddleware: เป็นฟังก์ชันมิดเดิลแวร์ที่ใช้ http.Handler เป็น Argument และส่งคืน http.Handler ใหม่ ฟังก์ชันนี้มันจะ Log request ที่เข้ามามาแสดงในคอนโซล จากนั้นส่ง Request ไปยัง Handler ถัดไปและเชื่อมต่อโดยใช้ next.ServeHTTP(w, r) การทำแบบนี้มันจะทำให้เราสามารถประมวลผล Request ที่เข้ามาก่อนที่จะส่งต่อไปยัง handler function
package main import ( "fmt" "html/template" "net/http" "time" ) func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Printf("[%s] %s %s\\n", time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL) next.ServeHTTP(w, r) }) } func greetHandler(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/greet/"):] tmpl, err := template.New("greet").Parse("<html><body><h1>Hello, {{.}}!</h1></body></html>") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = tmpl.Execute(w, name) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!") }) mux.HandleFunc("/greet/", greetHandler) server := &http.Server{ Addr: ":8080", Handler: loggingMiddleware(mux), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } err := server.ListenAndServe() if err != nil { fmt.Println(err) } }
greetHandler: เป็นฟังก์ชันตัวจัดการที่ใช้ http.ResponseWriter และ http.Request เป็น Argument ฟังก์ชันนี้จะแยกพารามิเตอร์ name มาจาก URL โดยใช้ r.URL.Path[len(“/greet/”):] จากนั้นใช้แพ็คเกจ html/ template เพื่อสร้างหน้า HTML พร้อมคำทักทาย ผลลัพธ์จะถูกเขียนโดยใช้ tmpl.Execute(w, name)
func greetHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Path[len("/greet/"):]
tmpl, err := template.New("greet").Parse("<html><body><h1>Hello, {{.}}!</h1></body></html>")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = tmpl.Execute(w, name)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
main: ในฟังก์ชัน main จะมีการใช้ http.NewServeMux() เพื่อสร้าง ServerMux หรือ HTTP request multiplexer สำหรับจัดการ HTTP Request และกำหนด Route จากนั้นเพิ่ม HandleFunc สองตัวให้กับ mux โดยใช้ mux.HandleFunc() ตัวจัดการแรกเพียงแค่ Reture ข้อความกลับไปว่า “Hello, World!” เมื่อมี Requtest เข้ามาที่ (“/”) ในขณะที่ตัวจัดการที่สองเรียกฟังก์ชันที่มี Request เข้ามาที่ (“/greet/”)
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
mux.HandleFunc("/greet/", greetHandler)
server := &http.Server{
Addr: ":8082",
Handler: loggingMiddleware(mux),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server: http.Server เป็นตัวกำหนดตัวเลือกเพิ่มเติมสำหรับเว็บเซิร์ฟเวอร์ เช่น พอร์ต (:8080) หมดเวลาการอ่าน 5 วินาที และหมดเวลาเขียน 10 วินาที นอกจากนี้ยังระบุฟังก์ชัน loggingMiddleware เป็นตัวจัดการหลักสำหรับ Request ที่เข้ามา ซึ่งหมายความว่า Request ทั้งหมดจะได้รับการประมวลผลโดยมิดเดิลแวร์ก่อนที่จะส่งต่อไปยังฟังก์ชันตัวจัดการหลักนั้นเอง
server := &http.Server{
Addr: ":8082",
Handler: loggingMiddleware(mux),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
err := server.ListenAndServe()
if err != nil {
fmt.Println(err)
}
เมื่อลองทำการรันและทดสอบดูก็จะเห็นได้ว่าจะมี log แสดงในส่วนของ console เรียบร้อยแล้ว
หากใครที่จะเริ่มต้นเรียนรู้ภาษา Go และเรียนรู้การใช้งาน Azure App Service อยากจะบอกดังๆว่า เรามีคอร์สฟรีที่ไม่ควรพลาด
ระบบฝึกทักษะ การเขียนโปรแกรม
ที่พร้อมตรวจผลงานคุณ 24 ชั่วโมง
- โจทย์ปัญหากว่า 200 ข้อ ที่รอท้าทายคุณอยู่
- รองรับ 9 ภาษาโปรแกรมหลัก ไม่ว่าจะ Java, Python, C ก็เขียนได้
- ใช้งานได้ฟรี ! ครบ 20 ข้อขึ้นไป รับ Certificate ไปเลย !!