HttpRouter
ที่อยู่ repository: julienschmidt/httprouter: A high performance HTTP request router that scales well (github.com)
คอมโพเนนต์การกำหนดเส้นทางของ Gin ใช้ HttpRouter ซึ่งเป็นคอมโพเนนต์การกำหนดเส้นทางที่เบาและมีประสิทธิภาพสูง ทั้งคอมโพเนนต์มีเพียงสามไฟล์ .go โค้ดมีความกระชับมาก โดยมีคุณสมบัติหลักดังนี้
การจับคู่หนึ่งต่อหนึ่ง: คำขอหนึ่งรายการสามารถจับคู่กับศูนย์หรือหนึ่งเส้นทางเท่านั้น และเป็นประโยชน์ต่อการทำ SEO
การแก้ไขเส้นทางอัตโนมัติ: เลือกสไตล์ URL ที่คุณชอบ แม้จะมีสแลชเกินหรือขาดไปหนึ่งตัว ก็จะทำการเปลี่ยนเส้นทางอัตโนมัติ หากมีข้อผิดพลาดตัวพิมพ์ใหญ่เล็ก ก็จะละเว้นตัวพิมพ์ใหญ่เล็กและทำการเปลี่ยนเส้นทางที่ถูกต้อง
การแยกวิเคราะห์พารามิเตอร์เส้นทางอัตโนมัติ: เพียงตั้งชื่อส่วนพาธ Router จะส่งค่าไดนามิกให้คุณ เนื่องจากดีไซน์ของ Router การแยกวิเคราะห์พารามิเตอร์เส้นทางใช้ทรัพยากรต่ำมาก
Zero Garbage: ในกระบวนการจัดสรรและกำหนดเส้นทาง不会产生任何内存垃圾
รองรับ RestfulAPI: การออกแบบ Router ส่งเสริมการสร้าง Restful API แบบ分层อย่างสมเหตุสมผล
การจัดการข้อผิดพลาด: สามารถตั้งค่า error handler เพื่อจัดการข้อผิดพลาดในคำขอ Router จะจับและบันทึก แล้วเปลี่ยนเส้นทางไปยังหน้าข้อผิดพลาด
การใช้งานพื้นฐาน
เหมือนกับ springboot หนึ่งฟังก์ชันผูกกับหนึ่ง URL และสอดคล้องกับหนึ่ง processor
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprintf(w, "<h1>Hello World!")
}
func main() {
router := httprouter.New()
router.GET("/hello", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}จากนั้นใช้เบราว์เซอร์หรือเครื่องมือทดสอบอินเทอร์เฟซใดๆ ป้อน 127.0.0.1:8080 จะเห็นเนื้อหาที่ถูกต้อง เราจะเห็นว่า HttpRouter ทำเพียงการกำหนดเส้นทางเท่านั้น ที่จริงแล้วยังใช้คอมโพเนนต์เริ่มต้นของ net/http เช่นเดียวกับ gin เพียงแต่การ encapsulate ลึกกว่าเล็กน้อย
พารามิเตอร์ที่ตั้งชื่อ
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "<h1>Hello World!")
}
func main() {
router := httprouter.New()
router.GET("/hello/:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}ครั้งนี้ด้านหลังเส้นทางเพิ่ม :name หนึ่งตัว name คือพารามิเตอร์ที่ตั้งชื่อ สามารถเข้าถึง slice พารามิเตอร์ผ่าน httprouter.Params สามารถรับพารามิเตอร์ผ่านดัชนีหรือ ByName(name) เช่นเดียวกัน คุณสามารถใช้ http.Handler และ http.HandlerFunc เป็น httprouter.Handle ได้ เส้นทางได้ implement interface แล้ว เช่นตัวอย่างด้านล่าง
func Hello(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
//params := r.Context().Value(httprouter.ParamsKey) ก็ได้
fmt.Fprintf(w, "hello, %s!\n", params.ByName("name"))
}เมื่อเส้นทางที่ method ผูกคือ /user/:user สถานการณ์การจับคู่ URL หลายแบบด้านล่าง
/user/gordon จับคู่
/user/you จับคู่
/user/gordon/profile ไม่จับคู่
/user/ ไม่จับคู่คุณไม่สามารถลงทะเบียน /user/new และ /user/:user ไว้ที่ request method เดียวกัน แต่ละ request method ควรเป็นอิสระต่อกัน
จับพารามิเตอร์ทั้งหมด
ประเภทที่สองคือการจับพารามิเตอร์ทั้งหมด กล่าวคือ จับคู่ทุกอย่าง ดังนั้นต้องอยู่ที่ส่วนท้ายของ Pattern
Pattern: /src/*filepath
/src/ จับคู่
/src/somefile.go จับคู่
/src/subdir/somefile.go จับคู่หลักการทำงานของ HttpRouter คือการสร้าง prefix tree จำนวนมาก ผู้ที่สนใจสามารถศึกษาเพิ่มเติม: httprouter package - github.com/julienschmidt/httprouter - Go Packages
OPTIONS & CORS
บางคนอาจต้องการแก้ไขการตอบสนอง OPTIONS อัตโนมัติและตั้งค่า response headers บางตัวเพื่อรองรับการ preflight request ของ CORS ความต้องการเหล่านี้สามารถทำได้โดยใช้ handler Router.GlobalOPTIONS
router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Access-Control-Request-Method") != "" {
// ตั้งค่า CORS response headers
header := w.Header()
header.Set("Access-Control-Allow-Methods", r.Header.Get("Allow"))
header.Set("Access-Control-Allow-Origin", "*")
}
// รองรับ status code 204
w.WriteHeader(http.StatusNoContent)
})NOT FOUND Handler
TIP
อาจต้องปิด Router.HandleMethodNotAllowed เพื่อหลีกเลี่ยงปัญหาบางอย่าง
router.NotFound = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
// logic ของคุณ
})การตรวจสอบพื้นฐาน
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// รับข้อมูลประจำตัวพื้นฐาน
user, password, hasAuth := r.BasicAuth()
if hasAuth && user == requiredUser && password == requiredPassword {
// มอบหมายคำขอไปยัง handler ที่กำหนด
h(w, r, ps)
} else {
// มิเช่นนั้นขอการรับรองความถูกต้อง
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
}
}
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Not protected!\n")
}
func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Protected!\n")
}
func main() {
user := "gordon"
pass := "secret!"
router := httprouter.New()
router.GET("/", Index)
router.GET("/protected/", BasicAuth(Protected, user, pass))
log.Fatal(http.ListenAndServe(":8080", router))
}