Skip to content

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

go
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 ลึกกว่าเล็กน้อย

พารามิเตอร์ที่ตั้งชื่อ

go
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 แล้ว เช่นตัวอย่างด้านล่าง

go
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

go
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

go
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 เพื่อหลีกเลี่ยงปัญหาบางอย่าง

go
router.NotFound = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
  // logic ของคุณ
})

การตรวจสอบพื้นฐาน

go
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))
}

Golang by www.golangdev.cn edit