Відпочивайте з вкладеним маршрутизатором Express.js


136

Припустимо, я хочу мати кінцеві точки REST, які виглядають приблизно так:

/user/
/user/user_id 

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

CRUD на кожен, якщо має сенс. Наприклад, / user POST створює нового користувача, GET отримує всіх користувачів. / user / user_id GET отримує лише одного користувача.

Елементи є специфічними для користувача, тому я ставлю їх під user_id , який є конкретним користувачем.

Тепер, щоб зробити маршрутизацію Express швидкішою, я зробив кілька екземплярів маршрутизатора. Існує маршрутизатор для користувача та маршрутизатор для елемента.

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будь-яким користувачемRouter, але більш конкретним маршрутом /user/*user_id*/items/до itemRouter? А також, я хотів би, щоб користувальницький доступ був доступний і для itemRouter, якщо це можливо.


Чудові відповіді вже про використання Express для вирішення цього питання. Однак ви можете використовувати Loopback (побудований на Express) для реалізації API на основі Swagger та додавання відносин між моделями для виконання CRUD так, як ви просили. Приємно, що після початкової кривої навчання, це набагато швидше збирати. loopback.io
Майк С.

Відповіді:


278

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

Вам потрібно перейти {mergeParams: true}до дочірнього маршрутизатора, якщо ви хочете отримати доступ paramsдо батьківського маршрутизатора.

mergeParamsбула представлена ​​в Експресі4.5.0 (5 липня 2014 р.)

У цьому прикладі itemRouterприв'язується до userRouterна /:userId/itemsмаршруті

Це призведе до наступних можливих маршрутів:

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

3
Дякую за відповідь. Маршрутизатор, який ви використовуєте тут, явніше вкладений, ніж той, яким поділяється Йордонія. Але чи працює це так само під кришкою? Я хотів би надати вам нагороду за всебічність, але я не можу зробити це лише через кілька годин.
huggie

Дякую за відповідь. Чи існує подібний спосіб отримати від дочірнього маршруту параметри запитів батьківського маршруту?
кумедний

1
Мене здивує, якщо вони не доступні жодним маршрутом, оскільки парам-запит не прив’язаний ні до якого конкретного маршруту ...
Willem D'Haeseleer

Дуже ретельна відповідь! Одне запитання: задля інкапсуляції та розділення знань між маршрутизатором користувача та маршрутизатором елемента, чи є декларативний спосіб вказати, що підпрограму потрібен параметр? Іншими словами, чи є явний спосіб записати реєстраційні дзвінки або отримати доступ до дзвінків таким чином, щоб маршрутизатор елемента дав нам знати, що він очікує передачі ідентифікатора користувача? Прикладом ситуації, маршрутизатор елемента є в іншому файлі, структурно не зрозуміло, що він вимагає від користувача, якщо ви не
входите

Це не більш розбірливо, ніж "стандартне" використання маршрутизаторів, я шукаю спосіб візуалізації гніздування під час перегляду коду.
DrewInTheMountains

127

керовані вкладені маршрути ...

Я хотів конкретного прикладу виконання вкладених маршрутів дуже керованим способом у експресі 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);

./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;

Приклад введення в структуру папок

Я помітив деякі коментарі щодо "структури папки". Мається на увазі це, однак не очевидно, тому я додав розділ нижче. Ось конкретний приклад структури вкладеної папки для маршрутів .

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

Це більше загальний приклад того, як працює вузол. Якщо ви використовуєте "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 і виявив, що це повнофункціональний ж / сокет і завдання, більше схожі на справжню рамку все-в-одному, перегортаючи парадигму REST на голові. Ви, ймовірно, повинні перевірити це над тим, як вийти голий в / експрес.


11
Я бачу, як це розбиває маршрути, але як це стосується гніздування?
1252748

досконалий .... і має сенс. Це масштабований варіант. Мені буде цікаво дізнатись, як операційна система буде реалізовувати версії (v1, v2 тощо)
Kermit_ice_tea

8
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я отримую réponse:

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

Класно. І ваш, і метод Віллема працює для того, що я хотів. Я перевірю його на всебічність, але також відзначу вас. Дуже дякую. Ваш метод не виглядає вкладеним, але він значною мірою робить те, що я хотів, я думаю, що я навіть віддаю перевагу вашому. Дякую.
huggie

тут є ключовим параметром mergeParams!
MrE

2

Використання рішення @Jason Sebring та адаптація до Typescript.

server.ts

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

/api/routes/index.ts

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

const router = Router();

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

export default router;

/api/routes/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;

Не могли б ви надати ./api/routes?
Джуліан

1
@Julian: Я виправив розташування файлів. ./api/routesмає два файли index.tsі home.ts. Перший використовується server.ts. Сподіваюся, це допоможе вам.
П’єр РА

0
try to add  { mergeParams: true } look to simple example  which it middlware use it in controller file getUser at the same for  postUser
    const userRouter = require("express").Router({ mergeParams: true });
    export default ()=>{
    userRouter
      .route("/")
      .get(getUser)
      .post(postUser);
    userRouter.route("/:user_id").get(function () {});
    
    
    }

-9

Вам потрібен лише один маршрутизатор, і використовувати його так:

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);

Так, але я хочу розділити логіку між елементами та користувачами, і тому я вважаю за краще розділити їх. Я не знаю, чи це можливо.
huggie

@huggie itemsналежать usersправо, навіщо це потрібно розділяти ? ви можете визначити їх у різних файлах, все ще використовуючи один і той же маршрутизатор, якщо ви цього хочете.
eguneys

Він належить користувачеві, але я хочу мати можливість легко підключати його чи виключати, не впливаючи на користувача. І в даний час у мене є кожен маршрутизатор для різних кінцевих точок URL. Здається, стиль стимулює експрес-генератор. Якщо це неможливо, то так, можливо, я повинен надіслати екземпляр маршрутизатора до різних файлів? Але це не відповідає оригінальним структурам.
huggie

Чи можна додати один маршрутизатор під інший? Оскільки архітектура програмного забезпечення Express Software обробляється маршрутизатором (я не зовсім впевнений, що це так), я думаю, що це можливо.
huggie

2
-1 Це не відповідає на питання, яке стосується вкладених маршрутизаторів
Willem D'Haeseleer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.