与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 Routing模块化,我制作了一些路由器实例。有一个供用户使用的路由器,以及一个用于物品的路由器。

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 to item是的URL层次结构的后代user。现在,我如何获取带有/usersuserRouter的URL,但具有到itemRouter的更具体路由/user/*user_id*/items/的URL?而且,如果可能的话,我也希望itemRouter也可以访问user_id。


使用Express解决此问题的绝佳答案。但是,您可以使用Loopback(基于Express构建)来实现基于Swagger的API,并在模型之间添加关系以执行CRUD(如您所要求的)。好东西是经过初步学习之后,组装起来要快得多。loopback.io
Mike S.

Answers:


277

您可以通过将路由器作为中间件附加到其他路由器上(带有或不带有)来嵌套路由器params

{mergeParams: true}如果params要从父路由器访问,则必须传递到子路由器。

mergeParamsExpress4.5.0中引入(2014年7月5日)

在此示例中,路线上的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
感谢你的回答。您使用的路由器比Jordonias共享的路由器更明确地嵌套。但是它在引擎盖下的作用相同吗?我想奖励您全面性,但是直到几个小时后我才能做到。
huggie 2014年

感谢你的回答。有没有类似的方法可以从子路径获取父路径的查询参数?
cwarny14年

1
如果它们在任何路径上都不可用,这将令我感到惊讶,因为查询参数没有与特定路径上的任何路径绑定...
Willem D'Haeseleer 2014年

很彻底的答案!一个问题:为了将用户路由器和项目路由器之间的知识进行封装和分离,是否存在一种声明性方法来指定子路由器需要参数?换句话说,是否有一种明确的方式来编写注册或访问调用,以便项目路由器让我们知道它希望传递用户ID?例如,项目路由器完全放在另一个文件中,从结构
上讲,

这比路由器的“标准”用法更不清晰,我正在寻找一种在查看代码时可视化嵌套的方法。
DrewInTheMountains

127

可管理的嵌套路线...

我想要一个在Express 4中以一种非常易于管理的方式进行嵌套路由的特定示例,这是“ Express中的嵌套路由”的顶部搜索结果。这是一个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;

文件夹结构中的嵌套示例

我注意到有关“嵌套文件夹结构”的一些评论。这暗示了这一点,但并不明显,因此我添加了以下部分。这是route嵌套文件夹结构的特定示例。

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

这是节点如何工作的更一般的示例。如果您在文件夹中使用“ index.js”,与在目录默认情况下在网页中使用“ index.html”的方式类似,则可以轻松地基于递归来扩展组织,而无需更改代码的入口点。“ index.js”是在目录中使用require时访问的默认文档。

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;

这里可能存在一些DRY问题,但确实可以很好地封装关注点。

仅供参考,最近我进入了动作英雄的行列,发现它具有完整的功能(包括套接字和任务),更像是一个真正的框架,将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我得到一个回应:

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

凉。您的方法和Willem的方法都可以满足我的需求。我会检查他的全面性,但也会为您加分。非常感谢。您的方法看起来并不嵌套,但几乎可以满足我的需求,我想我甚至更喜欢您的方法。谢谢。
huggie 2014年

mergeParams选项在这里很关键!
MrE '16

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
@朱利安:我已经固定文件位置。./api/routes有两个文件index.tshome.ts。第一个用于server.ts。希望对您有帮助。
皮埃尔·RA

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 2014年

@huggie items属于users权利,为什么您需要将其分开?您可以根据需要在仍使用同一路由器的不同文件中定义它们。
eguneys 2014年

它属于用户,但我希望能够方便地将其插入或拔出而不会影响用户。目前,我每个路由器都有一个不同的URL端点。这种样式似乎受到快递发电机的鼓励。如果不可能,是的,也许我应该将路由器实例发送到其他文件?但这与原始结构不一致。
huggie 2014年

是否可以在另一个路由器下方添加一个路由器?由于Express中间件体系结构似乎由下面的路由器处理(我不确定是否确实如此),我认为这是可能的。
huggie 2014年

2
-1这不能回答有关嵌套路由器的问题
Willem D'Haeseleer 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.