Menu

Routing trong Express: route, tham số và Router

Một route trong Express gồm phương thức, đường dẫn và handler: app.get('/users/:id', handler). Gom các route liên quan vào một express.Router() và gắn nó dưới một tiền tố bằng app.use('/api', router).

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Routing trong Express ánh xạ một phương thức HTTP và một đường dẫn tới một hàm handler: app.get('/users', handler) trả lời các request GET đến /users, còn app.post(), app.put() và app.delete() làm điều tương tự cho các phương thức khác. Một đoạn bắt đầu bằng dấu hai chấm, như trong /users/:id, là tham số route, và giá trị của nó nằm trong req.params. Để giữ một app đang lớn dần cho gọn gàng, hãy đặt các route liên quan vào một express.Router() và gắn nó dưới một tiền tố bằng app.use('/api', router).

GET /users/2 trả về {"id":2,"name":"Linus"}. Tham số route luôn là chuỗi, vì vậy handler so sánh với Number(req.params.id). Gửi POST /users với body {"name": "Grace"} để nhận 201, sau đó gửi DELETE /users/1 và GET /users để xem danh sách thay đổi.

express.Router() với tiền tố

Router là một app thu nhỏ với route và middleware riêng. Đường dẫn của nó là tương đối: một route viết là /:id trong router được gắn ở /api/books sẽ trả lời /api/books/:id. Trong dự án thật, mỗi router nằm trong một tệp riêng và kết thúc bằng module.exports = router.

GET /api/books/1 trả về {"id":1,"title":"Dune"}. router.route('/:id') chỉ cần viết đường dẫn một lần rồi nối các handler theo từng phương thức; gửi PUT /api/books/1 thì bạn nhận {"updated":"1"}. Đổi đường dẫn gắn thành /v2/books thì cùng router đó sẽ trả lời ở đường dẫn mới.

Thứ tự route: route khớp đầu tiên thắng

Express thử các route theo thứ tự bạn đăng ký và dừng ở route đầu tiên gửi response. Một route có tham số bắt mọi giá trị ở vị trí đó, nên một đường dẫn cố định đăng ký sau nó sẽ không bao giờ được chạy tới:

GET /users/new trả về {"route":"/users/:id","id":"new"}: route có tham số được đăng ký trước nên đã nhận request này. Chuyển route /users/new lên trên /users/:id rồi chạy lại để nhận {"route":"/users/new"}. Sau đó gửi GET /books để đến handler 404 ở cuối.

Handler 404

Express trả lời request không khớp route nào bằng trang HTML của riêng nó, Cannot GET /books. Với một API, bạn thường muốn trả về JSON, nên hãy thêm một app.use() không có đường dẫn làm route cuối cùng, như trong đoạn code ở trên. Nó chỉ chạy khi mọi route phía trước đều đã chuyển request đi tiếp. Lỗi do handler ném ra là một trường hợp khác, có handler bốn đối số riêng, được trình bày trong xử lý lỗi trong Express.

Đường dẫn route trong Express 5

Express 5 đã thay đổi cú pháp đường dẫn, trong khi phần lớn đoạn code trên mạng được viết cho Express 4:

MẫuExpress 4Express 5
Mọi đường dẫnapp.get('*', fn)app.get('/*splat', fn)
Tham số không bắt buộc/users/:id?/users{/:id}
Mẫu cho tham số/users/:id(\\d+)Không hỗ trợ; kiểm tra giá trị trong handler

Một dấu * đứng riêng hoặc dấu ? sau tham số sẽ ném TypeError ngay khi route được đăng ký, nên app không khởi động được.

Lỗi thường gặp

Đăng ký handler 404 đầu tiên. app.use() không có đường dẫn khớp với mọi thứ, nên request nào cũng nhận 404. Hãy giữ nó ở cuối.

Quên rằng tiền tố bị bỏ đi. Bên trong router được gắn ở /api, req.path không chứa /api. Dùng req.originalUrl khi bạn cần đường dẫn đầy đủ.

So sánh tham số với số. req.params.id === 1 luôn sai vì req.params.id là chuỗi '1'.

Câu hỏi thường gặp

app.use và app.get khác nhau thế nào?

app.get('/users', fn) chỉ chạy fn cho request GET có đường dẫn đúng bằng /users. app.use('/users', fn) chạy cho mọi phương thức và mọi đường dẫn bắt đầu bằng /users, vì vậy nó được dùng để gắn router và middleware.

Làm sao để route trong Express phân biệt chữ hoa chữ thường?

Mặc định route không phân biệt hoa thường, nên /Users khớp với /users. Tắt điều này bằng app.set('case sensitive routing', true), hoặc cho từng router bằng express.Router({ caseSensitive: true }).

Vì sao req.params rỗng bên trong router lồng nhau?

Một router chỉ thấy tham số trong các đường dẫn của chính nó. Nếu router được gắn ở /users/:userId/posts, hãy tạo nó bằng express.Router({ mergeParams: true }) để dùng được req.params.userId từ đường dẫn cha.

Có nên để mọi route trong một tệp không?

Không, khi bạn đã có nhiều hơn vài route. Tạo một router cho mỗi tài nguyên (routes/users.js, routes/orders.js), export nó bằng module.exports = router, rồi gắn từng router trong tệp chính bằng app.use().

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu