Отдых с Express.js вложенным маршрутизатором

Предположим, что я хочу иметь конечные точки REST, которые выглядят примерно так:

/user/
/user/user_id 

/user/user_id/items/
/user/user_id/items/item_id

CRUD на каждом, если имеет смысл. Например, POST пользователя создает нового пользователя, GET выбирает всех пользователей. /user/user _id GET выбирает только одного пользователя.

Элементы являются специфичными для пользователя, поэтому я помещаю их под user_id, который является конкретным пользователем.

Теперь, чтобы сделать модульную экспресс-маршрутизацию, я сделал несколько экземпляров маршрутизатора. Для пользователя есть маршрутизатор и маршрутизатор для этого элемента.

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-адрес item - это потомки иерархии URL-адреса user. Теперь, как мне получить URL с /users независимо от userRouter, но более конкретный маршрут /user/*user_id*/items/ в itemRouter? Кроме того, я бы хотел, чтобы user_id был доступен для itemRouter, если это возможно.

Ответ 1

Вы можете встраивать маршрутизаторы, прикрепляя их как промежуточное программное обеспечение на другом маршрутизаторе, с или без params.

Вы должны передать {mergeParams: true} дочернему маршрутизатору, если хотите получить доступ к params от родительского маршрутизатора.

mergeParams был введен в Экспресс 4.5.0 (5 июля 2014 г.)

В этом примере itemRouter привязывается к userRouter на трассе /:userId/items

Это приведет к следующим возможным маршрутам:

GET /userhello user
GET /user/5hello user 5
GET /user/5/itemshello items from user 5
GET /user/5/items/6hello item 6 from user 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);

Ответ 2

управляемые вложенные маршруты...

Мне нужен конкретный пример выполнения вложенных маршрутов очень удобным способом в express 4, и это был лучший результат поиска для "вложенных маршрутов в экспресс". Здесь API, который будет иметь много маршрутов, которые нужно будет разбить, например.

./index.js:

var app = require('express')();

// anything beginning with "/api" will go into this
app.use('/api', require('./routes/api'));

app.listen(3000);

<сильные > ./маршруты/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;

<сильные > ./маршруты/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;

Пример размещения в структуре папок

Я заметил некоторые комментарии к "структуре папок вложенности". Это подразумевается в этом, однако не очевидно, поэтому я добавил раздел ниже. Здесь приведен конкретный пример структуры вложенных папок для маршрутов.

index.js
/api
  index.js
  /admin
    index.js
    /users
      index.js
      list.js
    /permissions
      index.js
      list.js

Это более общий пример того, как работает node. Если вы используете "index.js" в папках так же, как "index.html" работает на веб-страницах для каталога по умолчанию, это будет легко масштабировать вашу организацию на основе рекурсии, не изменяя точки входа в код. "index.js" - это документ по умолчанию, доступ к которому при использовании требует в каталоге.

содержимое index.js

const express = require('express');
const router = express.Router();
router.use('/api', require('./api'));
module.exports = router;

содержимое /api/index.js

const express = require('express');
const router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;

содержимое /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;

содержимое /api/admin/users/index.js

const express = require('express');
const router = express.Router();
router.get('/', require('./list'));
module.exports = router;

Здесь возможно несколько СУХИХ проблем, но он хорошо подходит для инкапсуляции проблем.

FYI, недавно я попал в actionhero и нашел его полнофункциональными w/сокетами и задачами, более похожими на настоящую фреймворк "все в одном", который перевернул парадигму REST на голове. Вероятно, вы должны проверить это, когда будете голыми w/express.

Ответ 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);

Ключом ко второй части вашего вопроса является использование опции mergeParams

var itemRouter = require('express').Router({ mergeParams: true }); 

От /user/jordan/item/cat Я получаю ответ:

{"user_id":"jordan","item_id":"cat"}

Ответ 4

Использование решения @Jason Sebring и адаптация для Typescript.

server.ts

import Routes from './api/routes';
app.use('/api/', Routes);

/api/index.ts

import { Router } from 'express';
import HomeRoutes from './home';

const router = Router();

router.use('/', HomeRoutes);
// add other routes...

export default router;

/api/home.ts

import { Request, Response, Router } from 'express';

const router = Router();

router.get('/', (req: Request, res: Response) => {
  res.json({
    message: 'Welcome to API',
  });
});

export default router;

Ответ 5

Вам нужен только один маршрутизатор и используйте его следующим образом:

router.get('/users');
router.get('/users/:user_id');

router.get('/users/:user_id/items');
router.get('/users/:user_id/items/:item_id');

app.use('api/v1', router);