【问题标题】:Rest with Express.js nested router使用 Express.js 嵌套路由器休息
【发布时间】:2014-10-05 07:36:20
【问题描述】:

假设我想拥有大致如下所示的 REST 端点:

/user/
/user/user_id 

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

每个 if 的 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);

item 的 URL 是 user 的 URL 层次结构的后代。现在我如何获取带有/users 的URL 到userRouter,但更具体的路由/user/*user_id*/items/ 到itemRouter?另外,如果可能的话,我希望 itemRouter 也可以访问 user_id。

【问题讨论】:

  • 关于使用 Express 解决这个问题的好答案。但是,您可以使用 Loopback(基于 Express 构建)来实现基于 Swagger 的 API 并添加模型之间的关系以按照您的要求执行 CRUD。好的是在初始学习曲线之后,组装起来要快得多。 loopback.io

标签: javascript node.js express


【解决方案1】:

你只需要一个路由器,像这样使用它:

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 items 属于users 对,为什么要分开呢?如果需要,您可以在仍然使用同一路由器的不同文件中定义它们。
  • 它属于用户,但我希望能够轻松插入或拔出而不影响用户。目前我有每个路由器用于不同的 URL 端点。这种风格似乎受到 express-generator 的鼓励。如果不可能,那么是的,也许我应该将路由器实例发送到不同的文件?但这与原始结构不一致。
  • 是否可以在另一个路由器下面添加一个路由器?由于 Express 中间件架构似乎由下面的路由器处理(我不完全确定是否如此)我认为这是可能的。
  • -1 这不是在回答关于嵌套路由器的问题
【解决方案2】:

您可以通过将它们作为middleware 附加到另一个路由器上来嵌套路由器,无论有没有params

如果您想从父路由器访问params,您必须将{mergeParams: true} 传递给子路由器。

mergeParamsExpress 4.5.0 中引入(2014 年 7 月 5 日)

在此示例中,itemRouter 附加到 /:userId/items 路由上的 userRouter

这将导致以下可能的路线:

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

【讨论】:

  • 感谢您的回答。您在此处使用的路由器比 Jordonias 共享的路由器更明确地嵌套。但它在引擎盖下的工作原理是否相同?我想为你提供全面的奖励,但我要等几个小时后才能这样做。
  • 感谢您的回答。是否有类似的方法可以从子路由获取父路由的 query 参数?
  • 如果它们在任何路线上都不可用,我会感到惊讶,因为查询参数与特定的任何路线无关......
  • 非常彻底的回答!一个问题:为了封装和分离用户路由器和项目路由器之间的知识,是否有一种声明方式来指定子路由器需要参数?换句话说,是否有一种明确的方式来编写注册或访问调用,以便项目路由器让我们知道它希望传递一个用户 ID?示例情况,项目路由器完全在另一个文件中,在结构上不清楚它是否需要用户,除非您进入它的调用,并且仅在用户路由器中清楚它会传递用户 ID
  • 这不再比“标准”使用路由器更清晰,我正在寻找一种在查看代码时可视化嵌套的方法。
【解决方案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"}

【讨论】:

  • 酷。你和威廉的方法都适用于我想要的。我会检查他的全面性,但我也会标记你。非常感谢。您的方法看起来并不嵌套,但它几乎可以满足我的要求,我想我什至更喜欢您的方法。谢谢。
  • mergeParams 选项是这里的关键!
【解决方案4】:

可管理的嵌套路由...

我想要一个在 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;

文件夹结构中的嵌套示例

我注意到一些关于“嵌套文件夹结构”的 cmets。这暗示了这一点,但并不明显,所以我添加了下面的部分。这是路由的嵌套文件夹结构的具体示例。

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 问题,但它确实很适合封装关注点。

仅供参考,最近我接触了 actionhero,发现它功能齐全,带有套接字和任务,更像是一个真正的一体化框架,颠覆了 REST 范式。您可能应该检查一下裸体 w/ express。

【讨论】:

  • 我知道这是如何拆分路由的,但它如何解决嵌套问题?
  • 完美....而且很有意义。这是一个可扩展的选项。我很想知道 op 将如何实现版本控制(v1、v2 等)
  • 5 年过去了,看起来仍然不错,谢谢!
【解决方案5】:

使用@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吗?
  • @Julian:我已经修复了文件位置。 ./api/routes 有两个文件 index.tshome.ts。第一个由server.ts 使用。希望对您有所帮助。
【解决方案6】:

尝试添加{ mergeParams: true } 看看它的中间件在控制器文件getUser 中与postUser 相同使用它的简单示例

const userRouter = require("express").Router({ mergeParams: true });
export default ()=>{
  userRouter
    .route("/")
    .get(getUser)
    .post(postUser);

  userRouter.route("/:user_id").get(function () {});
}

【讨论】:

    【解决方案7】:

    本着 Express 模块化路由器的精神,我们应该为用户和物品有一个单独的路由器。该路由器不是我们顶级应用程序逻辑的一部分。我们可以将其嵌套在用户的路由器中。

    用户路由器

    const users = require('express').Router();
    const items = require('./items');
    
    //...
    
    // Our root route to /users
    albums.get('/', function(req, res, next) {
      // res.send() our response here
    });
    
    // A route to handle requests to any individual user, identified by an user id
    users.get('/:userId', function(req, res, next) {
      let userId = req.params.userId;
      // retrieve user from database using userId
      // res.send() response with user data
    });
    
    // Note, this route represents /users/:userId/items because our top-level router is already forwarding /users to our Users router!
    users.use('/:userId/items', items);
    
    //...
    
    module.exports = users;
    

    物品路由器

    // We need to merge params to make userId available in our Items router
    const items = require('express').Router({ mergeParams: true });
    
    //...
    
    // The root router for requests to our items path
    items.get('/', function(req, res, next) {
      let userId = req.params.userId; // Here is where mergeParams makes its magic
    
      // retrieve user's track data and render items list page
    });
    
    // The route for handling a request to a specific item
    items.get('/:itemId', function(req, res, next) {
      let userId = req.params.userId; // <-- mergeParams magic
      let itemId = req.params.itemId;
    
      // retrieve individual item data and render on single item page
    });
    
    //...
    
    module.exports = items;
    

    Source

    【讨论】:

      【解决方案8】:

      Express router(express.Router()) 将参数分开,因此您必须明确告诉 express 合并这些参数。 例如: express.Router({ mergeParams: true })

      //上面是对你问题的回答。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-22
        • 2019-02-18
        • 2020-08-22
        • 2021-01-16
        • 2016-08-18
        • 2014-09-03
        • 1970-01-01
        相关资源
        最近更新 更多