Định tuyến

Vai trò của route trong framework Go Fiber là ràng buộc các URL khác nhau với các chức năng xử lý, nhằm hỗ trợ việc xử lý yêu cầu HTTP. Nó phục vụ như điểm nhập cho tất cả các yêu cầu framework web.

Xử Lý Route (Các Hàm)

Đăng ký các route được ràng buộc với các phương thức HTTP cụ thể.

Lưu ý: Trong một số framework, route handlers được gọi là controllers, có ý nghĩa chung là xác định hàm (phương thức) nào để xử lý yêu cầu HTTP đến.

Chữ ký của hàm:

// Các phương thức 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 cho phép bạn chỉ định các phương thức như là các giá trị
func (app *App) Add(method, path string, handlers ...Handler) Router

// All đăng ký route trên tất cả các phương thức HTTP
// Tương tự như app.Use nhưng không ràng buộc một tiền tố
func (app *App) All(path string, handlers ...Handler) Router

Ví dụ:

// Xử lý GET đơn giản
app.Get("/api/list", func(c *fiber.Ctx) error {
  return c.SendString("Đây là yêu cầu GET!")
})

// Xử lý POST đơn giản
app.Post("/api/register", func(c *fiber.Ctx) error {
  return c.SendString("Đây là yêu cầu POST!")
})

Use được sử dụng để tải middleware và tiền tố URL interceptors. Những route này chỉ phù hợp với phần đầu mỗi đường dẫn, ví dụ, /john sẽ phù hợp với /john/doe, /johnnnnn, v.v.

Chữ ký của hàm tùy chỉnh middleware:

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

Ví dụ:

// Phù hợp với bất kỳ yêu cầu nào
app.Use(func(c *fiber.Ctx) error {
    return c.Next()
})

// Phù hợp với yêu cầu bắt đầu bằng /api
app.Use("/api", func(c *fiber.Ctx) error {
    return c.Next()
})

// Phù hợp với yêu cầu bắt đầu bằng /api hoặc /home (hỗ trợ nhiều tiền tố)
app.Use([]string{"/api", "/home"}, func(c *fiber.Ctx) error {
    return c.Next()
})

// Thêm nhiều handlers
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()
})

Đường dẫn

Đường dẫn route, cùng với phương thức yêu cầu, xác định các điểm cuối mà có thể truy cập. Đường dẫn route có thể là chuỗi hoặc mẫu chuỗi.

Các ví dụ về đường dẫn route dựa trên chuỗi

// Đường dẫn route này sẽ phù hợp với yêu cầu đến đường dẫn gốc, "/":
app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("Đường dẫn gốc")
})

// Đường dẫn route này sẽ phù hợp với yêu cầu đến "/about":
app.Get("/about", func(c *fiber.Ctx) error {
  return c.SendString("Về chúng tôi")
})

// Đường dẫn route này sẽ phù hợp với yêu cầu đến "/random.txt":
app.Get("/random.txt", func(c *fiber.Ctx) error {
  return c.SendString("Random.txt")

Tương tự như framework expressJs, thứ tự mà các route được khai báo là quan trọng. Khi nhận một yêu cầu, các route sẽ được kiểm tra theo thứ tự chúng được khai báo.

Vui lòng lưu ý: Đảm bảo rằng route với các tham số biến được viết sau route chứa các phần cố định để ngăn chặn những phần biến này bị phù hợp một cách nhầm lẫn, điều này có thể dẫn đến hành vi không mong muốn.

Tham số Đường dẫn

Tham số đường dẫn là các yếu tố động trong một đường dẫn và có thể là named (được đặt tên) hoặc unnamed segments (các đoạn không có tên). Những đoạn này được sử dụng để bắt giữ các giá trị tại các vị trí cụ thể trong URL. Bạn có thể sử dụng hàm Params để truy xuất những giá trị này, trong đó tham số của hàm là tên tham số đường dẫn trong đường dẫn, hoặc đối với các tham số không có tên, đó là các ký tự (*, +) và số lần xuất hiện của chúng.

Ký tự đại diện (wildcard) (*) hoặc dấu cộng (+) đều đại diện cho các tham số tham lam (greedy parameters).

Các đoạn đường cũng cung cấp khả năng sử dụng các tham số tùy chọn. Đối với các tham số được đặt tên, những tham số này được theo sau bởi dấu hỏi (?), trong khi dấu cộng không phải tùy chọn, và bạn có thể sử dụng ký tự đại diện (wildcard) để đại diện cho một phạm vi tham số tùy chọn và tham lam (greedy).

Ví dụ về định nghĩa một đường dẫn với các tham số đường dẫn

// Tham số
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
})
// Cộng - tham lam - không tùy chọn
app.Get("/user/+", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("+"))
})

// Tham số tùy chọn
app.Get("/user/:name?", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("name"))
})

// Wildcard - tham lam - tùy chọn
app.Get("/user/*", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("*"))
})

// Đoạn đường này sẽ khớp với yêu cầu "/v1/some/resource/name:customVerb" vì các ký tự tham số đã được đặt tên đã được thoát
app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error {
    return c.SendString("Xin chào, Cộng đồng")
})

Vì dấu gạch ngang (-) và dấu chấm (.) được diễn giải theo nghĩa đen, chúng có thể được sử dụng cùng với các tham số đường dẫn để đạt được các mục đích hữu ích.

Tất cả các ký tự tham số đặc biệt cũng có thể được thoát bằng cách sử dụng dấu gạch chéo () và các giá trị của chúng sẽ được bỏ qua, vì vậy nếu bạn muốn sử dụng chúng trong đường dẫn. Đề xuất sử dụng dấu huyền `` để luôn đảm bảo không có mâu thuẫn, và các ký tự thoát không vô tình ảnh hưởng đến mẫu biểu thức chính quy.

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

Hệ thống định tuyến thông minh của chúng tôi nhận ra rằng các ký tự tham số được giới thiệu trong trường hợp này nên là một phần của yêu cầu đường dẫn và có thể xử lý nó như vậy.

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

Ngoài ra, các đoạn đường có thể sử dụng nhiều tham số và nhiều ký tự tham số không có tên liên tục, chẳng hạn như các ký tự đại diện (wildcard) hoặc tham số cộng, mở rộng đáng kể các khả năng cho người dùng trong các đoạn đường.

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

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

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

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

Chúng tôi đã điều chỉnh hệ thống định tuyến để tương thích với định tuyến của Express, nhưng hiện tại chưa hỗ trợ biểu thức chính quy vì chúng tương đối chậm.

Ràng buộc

Khi có sự khớp với URL đầu vào và đường dẫn URL được chia thành các giá trị route thông qua các tham số, ràng buộc sẽ được thực thi. Tính năng này đã được giới thiệu từ phiên bản v2.37.0 và được lấy cảm hứng từ .NET Core.

Ràng buộc không dành cho việc xác thực tham số. Nếu ràng buộc không hợp lệ đối với giá trị tham số, Fiber sẽ trả về một trình xử lý 404.

Ràng buộc Ví dụ Ví dụ Khớp
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 (ít nhất 4 ký tự)
maxLen(value) :filename<maxLen(8)> MyFile (tối đa 8 ký tự)
len(length) :filename<len(12)> somefile.txt (12 ký tự)
min(value) :age<min(18)> 19 (giá trị số nguyên phải ít nhất là 18)
max(value) :age<max(120)> 91 (giá trị số nguyên không thể vượt quá 120)
range(min,max) :age<range(18,120)> 91 (giá trị số nguyên phải ít nhất là 18 nhưng không thể vượt quá 120)
alpha :name Rick (chuỗi phải bao gồm một hoặc nhiều ký tự chữ cái, không phân biệt hoa thường, a-z)
datetime :dob<datetime(2006\\-01\\-02)> 2005-11-01
regex(expression) :date<regex(\d{4}-\d{2}-\d{2})> 2022-08-27 (phải khớp với biểu thức chính quy)

Các Ví dụ

  • Một Ràng buộc Đơn
  • Nhiều Ràng buộc
  • Ràng buộc Biểu thức Chính quy
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 // Không tìm thấy GET /1


Bạn có thể sử dụng `;` để thêm nhiều ràng buộc.

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

// curl -X GET http://localhost:3000/120000 // Không tìm thấy GET /120000

// curl -X GET http://localhost:3000/1 // Không tìm thấy GET /1

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


Fiber tiền biên dịch các truy vấn biểu thức chính quy khi đăng ký các route, vì vậy không có overhead hiệu suất cho các ràng buộc biểu thức chính quy.

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

// curl -X GET http://localhost:3000/125 // Không tìm thấy GET /125

// curl -X GET http://localhost:3000/test // Không tìm thấy GET /test

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


> Khi sử dụng ràng buộc ngày-giờ (`*`,`+`,`?`,`:`,`/`,``,``,`>`,';`,`(`,`)`), hãy sử dụng `\\` trước khi sử dụng các ký tự đặc biệt trong route để tránh hiểu lầm.

**Ví dụ Tham số Tùy chọn**

Bạn cũng có thể thêm ràng buộc vào các tham số tùy chọn.
```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
// Không tìm thấy GET /7.0

Middleware

Các chức năng được thiết kế để thay đổi yêu cầu hoặc phản hồi được gọi là các chức năng middleware. Next là một chức năng định tuyến của Fiber mà khi gọi, thực thi chức năng next phù hợp với tuyến đường hiện tại.

Ví dụ về Chức năng Middleware

app.Use(func(c *fiber.Ctx) error {
  // Đặt một header tùy chỉnh trong tất cả các phản hồi:
  c.Set("X-Custom-Header", "Xin chào, thế giới")

  // Tiếp tục tới middleware tiếp theo:
  return c.Next()
})

app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("Xin chào, thế giới!")
})

Đường dẫn cho phương thức Use có thể là một đường dẫn mount hoặc đường dẫn tiền tố, và nó hạn chế middleware chỉ được áp dụng cho các yêu cầu có đường dẫn bắt đầu bằng đường dẫn đó.

Thêm Ràng Buộc Tuyến Đường Tại Thời Gian Chạy

Do thiết kế và hiệu suất, việc thêm động các tuyến đường sau khi khởi động ứng dụng không được hỗ trợ. Vui lòng đảm bảo rằng tất cả các tuyến đường được xác định trước khi ứng dụng bắt đầu.

Nhóm

Nếu bạn có nhiều đầu cuối, bạn có thể sử dụng Group để tổ chức các tuyến đường.

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