javascript - REST với Express.js router lồng nhau
Giả sử tôi muốn có các điểm kết REST trông đại khái như thế này:
/user/
/user/user_id
/user/user_id/items/
/user/user_id/items/item_id
Thực hiện CRUD trên từng cái là hợp lý. Ví dụ, /user POST tạo một người dùng mới, GET lấy tất cả người dùng. /user/user_id GET chỉ lấy đúng người dùng đó.
Các mục là cụ thể cho người dùng nên tôi đặt chúng dưới user_id, đó là một người dùng cụ thể.
Bây giờ để làm cho việc định tuyến Express trở nên mô-đun, tôi đã tạo một vài thể hiện router. Có một router cho người dùng, và một router cho mặt hàng.
var userRouter = require('express').Router();
userRouter.route('/')
.get(function() {})
.post(function() {})
userRouter.route('/:user_id')
.get(function() {})
var itemRouter = require('express').Router();
itemRouter.route('/')
.get(function() {})
.post(function() {})
itemRouter.route('/:item_id')
.get(function() {})
app.use('/users', userRouter);
// Now how to add the next router?
// app.use('/users/', itemRouter);
URL tới item là các hậu duệ của cấu trúc URL của user. Bây giờ làm thế nào để tôi nhận được URL với /users bất kỳ tới userRouter nhưng tuyến đường cụ thể hơn của /user/*user_id*/items/ tới itemRouter? Và cũng vậy, tôi muốn user_id có thể truy cập được cho itemRouter nếu có thể.
Câu trả lời #1
Bạn có thể lồng các bộ định tuyến bằng cách gắn chúng làm middleware trên một bộ định tuyến khác, có hoặc không có params.
Bạn phải truyền {mergeParams: true} cho router con nếu bạn muốn truy cập params từ router cha.
mergeParams được giới thiệu trong Express 4.5.0 (5 tháng 7, 2014)
Trong ví dụ này, itemRouter được gắn vào userRouter trên tuyến /:userId/items
Điều này sẽ dẫn đến các đường đi có thể sau đây:
GET /user -> xin chào người dùng
GET /user/5 -> xin chào người dùng 5
GET /user/5/items -> xin chào các mục từ người dùng 5
GET /user/5/items/6 -> xin chào mục 6 từ người dùng 5
var express = require('express');
var app = express();
var userRouter = express.Router();
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var itemRouter = express.Router({mergeParams: true});
// you can nest routers by attaching them as middleware:
userRouter.use('/:userId/items', itemRouter);
userRouter.route('/')
.get(function (req, res) {
res.status(200)
.send('hello users');
});
userRouter.route('/:userId')
.get(function (req, res) {
res.status(200)
.send('hello user ' + req.params.userId);
});
itemRouter.route('/')
.get(function (req, res) {
res.status(200)
.send('hello items from user ' + req.params.userId);
});
itemRouter.route('/:itemId')
.get(function (req, res) {
res.status(200)
.send('hello item ' + req.params.itemId + ' from user ' + req.params.userId);
});
app.use('/user', userRouter);
app.listen(3003);
Câu trả lời #2
các tuyến lồng nhau có thể quản lý được...
Tôi muốn một ví dụ cụ thể về cách thực hiện các route lồng nhau một cách rất dễ quản lý trong Express 4 và đây là kết quả tìm kiếm hàng đầu cho 'nested routes in express'. Đây là một API sẽ có nhiều route cần được chia nhỏ ví dụ.
./index.js:
var app = require('express')();
// anything beginning with "/api" will go into this
app.use('/api', require('./routes/api'));
app.listen(3000);
./routes/api/index.js:
var router = require('express').Router();
// split up route handling
router.use('/products', require('./products'));
router.use('/categories', require('./categories'));
// etc.
module.exports = router;
./routes/api/products.js:
var router = require('express').Router();
// api/products
router.get('/', function(req, res) {
res.json({ products: [] });
});
// api/products/:id
router.get('/:id', function(req, res) {
res.json({ id: req.params.id });
});
module.exports = router;
Ví dụ lồng nhau trong cấu trúc thư mục
Tôi nhận thấy một số bình luận về "cấu trúc thư mục lồng nhau". Điều này được ngụ ý nhưng không rõ ràng nên tôi đã thêm phần dưới đây. Đây là một ví dụ cụ thể về cấu trúc thư mục lồng nhau cho các tuyến đường.
index.js
/api
index.js
/admin
index.js
/users
index.js
list.js
/permissions
index.js
list.js
Đây là một ví dụ chung về cách node hoạt động. Nếu bạn sử dụng "index.js" trong các thư mục tương tự như cách "index.html" hoạt động trên các trang web cho một thư mục mặc định, điều này sẽ dễ dàng mở rộng tổ chức của bạn dựa trên đệ quy mà không cần thay đổi điểm nhập của mã. "index.js" là tài liệu mặc định được truy cập khi sử dụng require trong một thư mục.
nội dung của index.js
const express = require('express');
const router = express.Router();
router.use('/api', require('./api'));
module.exports = router;
nội dung của /api/index.js
const express = require('express');
const router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;
nội dung của /api/admin/index.js
const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/permissions', require('./permissions'));
module.exports = router;
nội dung của /api/admin/users/index.js
const express = require('express');
const router = express.Router();
router.get('/', require('./list'));
module.exports = router;
Có thể có một số vấn đề về DRY ở đây nhưng nó thực sự phù hợp với việc đóng gói các mối quan tâm.
Để bạn biết, gần đây tôi đã bắt đầu sử dụng actionhero và nhận thấy nó đầy đủ tính năng với sockets và tasks, giống như một framework thực thụ tất cả trong một, đảo ngược cách tiếp cận REST. Bạn có lẽ nên thử nó thay vì dùng express thuần túy.
Câu trả lời #3
var userRouter = require('express').Router();
var itemRouter = require('express').Router({ mergeParams: true });
userRouter.route('/')
.get(function(req, res) {})
.post(function(req, res) {})
userRouter.route('/:user_id')
.get(function() {})
itemRouter.route('/')
.get(function(req, res) {})
.post(function(req, res) {})
itemRouter.route('/:item_id')
.get(function(req, res) {
return res.send(req.params);
});
app.use('/user/', userRouter);
app.use('/user/:user_id/item', itemRouter);
Chìa khóa cho phần thứ hai của câu hỏi của bạn là việc sử dụng tùy chọn mergeParams
var itemRouter = require('express').Router({ mergeParams: true });
Từ /user/jordan/item/cat tôi nhận được phản hồi:
{"user_id":"jordan","item_id":"cat"}
