การเส้นทาง (Routing)

บทบาทของการเส้นทางในกรอบ Go Fiber คือการผูก URL ต่าง ๆ กับฟังก์ชันการจัดการเพื่อรองรับการประมวลผลคำขอ HTTP ซึ่งเป็นจุดเริ่มต้นสำหรับการขอข้อมูลเว็บโฟร์เฟรมทั้งหมด

ผู้จัดการเส้นทาง (Functions)

ลงทะเบียนเส้นทางที่ผูกกับเมธอด HTTP ที่แน่นอน

หมายเหตุ: ในบางกรอบการทำงาน เส้นทางผู้จัดการถูกเรียกว่าตัวควบคุม (controllers) ซึ่งมักจะมีความหมายเดียวกัน คือ การกำหนดฟังก์ชัน (เมธอด) ที่จะจัดการคำขอ HTTP เข้า

ลายเซ็นเจ้าของฟังก์ชัน:

// เมธอด HTTP
func (app *App) Get(path string, handlers ...Handler) Router
func (app *App) Head(path string, handlers ...Handler) Router
func (app *App) Post(path string, handlers ...Handler) Router
func (app *App) Put(path string, handlers ...Handler) Router
func (app *App) Delete(path string, handlers ...Handler) Router
func (app *App) Connect(path string, handlers ...Handler) Router
func (app *App) Options(path string, handlers ...Handler) Router
func (app *App) Trace(path string, handlers ...Handler) Router
func (app *App) Patch(path string, handlers ...Handler) Router

// Add ช่วยให้คุณระบุเมธอดเป็นค่า
func (app *App) Add(method, path string, handlers ...Handler) Router

// ทั้งหมดจดทะเบียนเส้นทางทั้งหมดของ HTTP เมธอด
// คล้ายกับ app.Use แต่ไม่ผูกคำนำหน้า
func (app *App) All(path string, handlers ...Handler) Router

ตัวอย่าง:

// ผู้จัดการ GET ง่าย ๆ
app.Get("/api/list", func(c *fiber.Ctx) error {
  return c.SendString("นี่คือคำขอ GET!")
})

// ผู้จัดการ POST ง่าย ๆ
app.Post("/api/register", func(c *fiber.Ctx) error {
  return c.SendString("นี่คือคำขอ POST!")
})

Use ถูกใช้ในการโหลด middleware และฟังก์ชันป้อนตัวกระทา พวกระทดีแค่ตรงเริงแต่แถินของแต่ละเส้นทางเท่านั้น เช่น /john จะตรงกับ /john/doe, /johnnnnn, และอื่น ๆ

ลายเซ็น middleware ที่กำหนดเอง:

func (app *App) Use(args ...interface{}) Router

ตัวอย่าง:

// ตรงกับคำขอใด ๆ
app.Use(func(c *fiber.Ctx) error {
    return c.Next()
})

// ตรงกับคำขอที่เริ่มด้วย /api
app.Use("/api", func(c *fiber.Ctx) error {
    return c.Next()
})

// ตรงกับคำขอที่เริ่มด้วย /api หรือ /home (รองรับคำนำหน้าหลายตำนะ)
app.Use([]string{"/api", "/home"}, func(c *fiber.Ctx) error {
    return c.Next()
})

// เพิ่มผู้จัดการหลายคน
app.Use("/api", func(c *fiber.Ctx) error {
  c.Set("X-Custom-Header", random.String(32))
  return c.Next()
}, func(c *fiber.Ctx) error {
  return c.Next()
})

เส้นทาง (Path)

เส้นทางเส้นทางร่วมกับเมธอดการขอไลน์นั้นกำหนดจุดสิ้นสุดที่สามารถเข้าถึงได้ เส้นทางเส้นทางอาจเป็น สตริง หรือ รูปแบบสตริง

ตัวอย่างเส้นทางที่ใช้สตริงเป็นพื้นฐาน

// เส้นทางนี้จะตรงกับคำขอไปยังเส้นหลัก, "/":
app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("เส้นทางหลัก")
})

// เส้นทางนี้จะตรงกับคำขอไปยัง "/about":
app.Get("/about", func(c *fiber.Ctx) error {
  return c.SendString("เกี่ยวกับ")
})

// เส้นทางนี้จะตรงกับคำขอไปยัง "/random.txt":
app.Get("/random.txt", func(c *fiber.Ctx) error {
  return c.SendString("Random.txt")

เหมือนกับกรอบ expressJs ลำดับ ในการประกาศเส้นทางมีความสำคัญ เมื่อได้รับคำขอ จะตรวจสอบเส้นทางในลำดับที่ประกาศ

โปรดทราบ: ตรวจสอบว่าเส้นทางที่มีพารามิเตอร์แปรต้องเขียนหลังเส้นทางที่มีส่วนคงที่เพื่อป้องกันไม่ให้ชิ่วของพารามิเตอร์ดังกล่าวถูกตรงตราไปที่ส่วนที่คงที่ซึ่งอาจเป็นสาเหตุของการทำงานที่ไม่คาดคิด และอาจนำไปสู่พฤติกรรมที่ไม่คาดคิด

พารามิเตอร์ของเส้นทาง

พารามิเตอร์ของเส้นทางเป็นองค์ประกอบที่เปลี่ยนไปในเส้นทางและสามารถเป็นได้ทั้ง ชื่อ หรือ คนละหมู่ ที่ใช้เพื่อจับค่าที่ตำแหน่งที่ระบุใน URL คุณสามารถใช้ฟังก์ชัน Params เพื่อเรียกดึงค่าเหล่านี้ ซึ่งพารามิเตอร์ของฟังก์ชันคือชื่อของพารามิเตอร์ของเส้นทางในเส้นทาง หรือสำหรับพารามิเตอร์ที่ไม่มีชื่อ คือตัวอักษร (*, +) และจำนวนของมัน

Wildcard (*) หรือเครื่องหมายพลัส (+) แทนพารามิเตอร์ที่เรียกร้อย

เส้นทางยังมีความเป็นไปได้ในการใช้พารามิเตอร์ที่เป็นไปได้ สำหรับพารามิเตอร์ที่มีชื่อ พารามิเตอร์เหล่านี้ตามหลังโดยคำถาม (?) ในขณะที่เครื่องหมายพลัสไม่ใช่ได้เลือกใช้ตัวอักษรสุ่มเพื่อแทนพารามิเตอร์ที่เป็นไปได้และเรียกร้อย

ตัวอย่างการกำหนดเส้นทางพร้อมพารามิเตอร์ของเส้นทาง

// พารามิเตอร์
app.Get("/user/:name/books/:title", func(c *fiber.Ctx) error {
    fmt.Fprintf(c, "%s\n", c.Params("name"))
    fmt.Fprintf(c, "%s\n", c.Params("title"))
    return nil
})
// พลัส - เรียกร้อย - ไม่เลือกใช้
app.Get("/user/+", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("+"))
})

// พารามิเตอร์ที่เป็นไปได้
app.Get("/user/:name?", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("name"))
})

// พร้อมพารามิเตอร์ ที่เรียกร้อย ที่เป็นไปได้
app.Get("/user/*", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("*"))
})

// เส้นทางนี้จะตรงกับคำขอ "/v1/some/resource/name:customVerb" เพราะว่าตัวอักษรพารามิเตอร์ถูกหนีบ
app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error {
    return c.SendString("สวัสดี ชุมชน")
})

เนื่องจากเครื่องหมายขีดล่าง (-) และจุด (.) ถูกตีความไร้อยุำจริง พวกเครื่องหมายเหล่านี้สามารถใช้ร่วมกับพารามิเตอร์ของเส้นทางเพื่อให้ได้ประโยชน์

ตัวอักษรพารามิเตอร์ทางพิเศษทั้งหมดยังสามารถหนีลํเวยใช้ \ และค่าของพวกนี้จะถูกละเลย เพื่อนถ้าคุณต้องการใช้พวกมันในเส้นทาง แนะนำให้ใช้เป็น backticks ``, ตามเอกสารของ Go regular expression พวกมันถูกใช้เสมอเพื่อให้แน่ใจว่าไม่มีความกำกวม และตัวอักษรหนีไม่มีผลต่อเครื่องหมายของ regular expression pattern

// http://localhost:3000/plantae/prunus.persica
app.Get("/plantae/:genus.:species", func(c *fiber.Ctx) error {
    fmt.Fprintf(c, "%s.%s\n", c.Params("genus"), c.Params("species"))
    return nil // prunus.persica
})
// http://localhost:3000/flights/LAX-SFO
app.Get("/flights/:from-:to", func(c *fiber.Ctx) error {
    fmt.Fprintf(c, "%s-%s\n", c.Params("from"), c.Params("to"))
    return nil // LAX-SFO
})

สเมารท์เราท์ของเราจะรับรู้ว่าตัวอักษรพารามิเตอร์ที่ถูกเสนอในกรณีนี้ควรเป็นส่วนหนึ่งของเส้นทาไฟน์และสามารถจัดการได้แบบนั้น

// http://localhost:3000/shop/product/color:blue/size:xs
app.Get("/shop/product/color::color/size::size", func(c *fiber.Ctx) error {
    fmt.Fprintf(c, "%s:%s\n", c.Params("color"), c.Params("size"))
    return nil // blue:xs
})

นอกจากนี้ เส้นทางสามารถใช้พารามิเตอรที่หลายตัวและตัวอักษรพารามิเตอร์ที่ไร้ชื่อหลายตัวติดต่อกันได้ เช่น ตัวอักษรสุ่มหรือเครื่องหมายพลัส ทำให้เป็นไปได้ที่มากขึ้นสำหรับผู้ใช้ในเส้นทาง

// GET /@v1
// Params: "sign" -> "@", "param" -> "v1"
app.Get("/:sign:param", handler)

// GET /api-v1
// Params: "name" -> "v1" 
app.Get("/api-:name", handler)

// GET /customer/v1/cart/proxy
// Params: "*1" -> "customer/", "*2" -> "/cart"
app.Get("/*v1*/proxy", handler)

// GET /v1/brand/4/shop/blue/xs
// Params: "*1" -> "brand/4", "*2" -> "blue/xs"
app.Get("/v1/*/shop/*", handler)

เราได้ปรับการสะดวกใช้เส้นทางไปให้รอบ Express แล้วแต่เรายังไม่รองรับเข้าไปที่ regular expressions เพราะพวกมันเร็วน้อยลง

ข้อจำกัด

เมื่อ URL ขาเข้ามีการตรงกันและเส้นทาง URL ถูกแบ่งเป็นค่าเส้นทางผ่านพารามิเตอร์มูล, เงื่อนไขจะถูกดำเนินการ คุณลักษณะนี้ถูกนำเสนอใน v2.37.0 และได้รับแรงบัลลาดจาก .NET Core.

เงื่อนไขนั้นไม่ได้ใช้สำหรับการตรวจสอบพารามิเตอร์ หากเงื่อนไขไม่ถูกต้องสำหรับค่าพารามิเตอร์, Fiber จะส่งกลับ 404 handler.

เงื่อนไข ตัวอย่าง ตัวอย่างที่ตรงกัน
int :id 123456789, -123456789
bool :active true, false
guid :id CD2C1638-1638-72D5-1638-DEADBEEF1638
float :weight 1.234, -1,001.01e8
minLen(value) :username<minLen(4)> test (อย่างน้อย 4 ตัวอักษร)
maxLen(value) :filename<maxLen(8)> MyFile (สูงสุด 8 ตัวอักษร)
len(length) :filename<len(12)> somefile.txt (12 ตัวอักษร)
min(value) :age<min(18)> 19 (ค่าจำนวนเต็มต้องอยู่อย่างน้อยที่ 18)
max(value) :age<max(120)> 91 (ค่าจำนวนเต็มไม่สามารถเกิน 120)
range(min,max) :age<range(18,120)> 91 (ค่าจำนวนเต็มจำเป็นต้องอยู่อย่างน้อยที่ 18 แต่ไม่สามารถเกิน 120)
alpha :name Rick (สตริงจะต้องประกอบด้วยตัวอักษรอักษรตัวพิมพ์เล็กหรือใหญ่ ที่มีแทนกันขึ้นอย่างน้อยหนึ่งตัว)
datetime :dob<datetime(2006\\-01\\-02)> 2005-11-01
regex(expression) :date<regex(\d{4}-\d{2}-\d{2})> 2022-08-27 (จะต้องตรงกับ regular expression)

ตัวอย่าง

  • เงื่อนไขเดี่ยว
  • เงื่อนไขหลายอัน
  • เงืนไข regular expression
app.Get("/:test", func(c *fiber.Ctx) error {
  return c.SendString(c.Params("test"))
})

// curl -X GET http://localhost:3000/12 // 12

// curl -X GET http://localhost:3000/1 // ไม่พบ GET /1


คุณสามารถใช้ `;` เพื่อเพิ่มเงื่อนไขหลายอัน

app.Get("/:test", func(c *fiber.Ctx) error { return c.SendString(c.Params("test")) })

// curl -X GET http://localhost:3000/120000 // ไม่พบ GET /120000

// curl -X GET http://localhost:3000/1 // ไม่พบ GET /1

// curl -X GET http://localhost:3000/250 // 250


Fiber ทำการคอมไพล์ regular expression queries ล่วงหน้าเมื่อลงทะเบียนเส้นทาง, ทำให้ไม่มีความล่าช้าในประสิทธิภาพสำหรับเงื่อนไข regular expression.

```go
app.Get(`/:date`, func(c *fiber.Ctx) error {
  return c.SendString(c.Params("date"))
})

// curl -X GET http://localhost:3000/125 // ไม่พบ GET /125

// curl -X GET http://localhost:3000/test // ไม่พบ GET /test

// curl -X GET http://localhost:3000/2022-08-27 // 2022-08-27


> เมื่อใช้เงื่อนไข date-time (`*`,`+`,`?`,`:`,`/`, `_`,``,`>`,';`,`(`,`)`), ให้ใช้ `\\` ก่อนการใช้ตัวอักษรพิเศษในเส้นทางเพื่อหลีกเลี่ยงการเข้าใจผิดพลาด.

**ตัวอย่างพารามิเตอร์ที่เลือกได้**

คุณยังสามารถเพิ่มเงื่อนไขให้กับพารามิเตอร์ที่เลือกได้.
```go
app.Get("/:test?", func(c *fiber.Ctx) error {
  return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/42
// 42
// curl -X GET http://localhost:3000/
//
// curl -X GET http://localhost:3000/7.0
// ไม่พบ GET /7.0

มิดเดิลแวร์ (Middleware)

ฟังก์ชันที่ออกแบบมาเพื่อจัดการกับคำขอหรือการตอบกลับเรียกว่า middleware functions Next เป็นฟังก์ชันเส้นทางของ Fiber ที่เมื่อเรียกใช้งานจะ execute next ฟังก์ชันที่ตรงกับเส้นทางปัจจุบัน

ตัวอย่างของฟังก์ชัน Middleware

app.Use(func(c *fiber.Ctx) error {
  // Set a custom header in all responses:
  c.Set("X-Custom-Header", "Hello, world")

  // Proceed to the next middleware:
  return c.Next()
})

app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("Hello, world!")
})

เส้นทางสำหรับเมทอด Use สามารถเป็น mount path หรือ prefix และจำกัด middleware ที่จะใช้กับคำขอเฉพาะที่มีเส้นทางเริ่มต้นด้วยเส้นทางนั้นเท่านั้น

เพิ่มข้อจำกัดของเส้นทางระหว่างระบบในระหว่างการทำงาน

เนื่องจากการออกแบบและประสิทธิภาพ การเพิ่มเส้นทางไดนามิกหลังจากเริ่มแอปพลิเคชันไม่ได้รับการสนับสนุน กรุณาตรวจสอบให้แน่ใจว่าเส้นทางทั้งหมดถูกกำหนดก่อนที่แอปพลิเคชันจะเริ่ม

การจัดกลุ่ม (Grouping)

หากคุณมีเส้นทางมากมาย คุณสามารถใช้ Group เพื่อจัดระเบียบเส้นทาง

func main() {
  app := fiber.New()

  api := app.Group("/api", middleware) // /api

  v1 := api.Group("/v1", middleware)   // /api/v1
  v1.Get("/list", handler)             // /api/v1/list
  v1.Get("/user", handler)             // /api/v1/user

  v2 := api.Group("/v2", middleware)   // /api/v2
  v2.Get("/list", handler)             // /api/v2/list
  v2.Get("/user", handler)             // /api/v2/user

  log.Fatal(app.Listen(":3000"))
}