【问题标题】:Express routes for API - URL handlers when you have sub resourcesAPI 的快速路由 - 当您有子资源时的 URL 处理程序
【发布时间】:2016-08-30 03:56:28
【问题描述】:

我有两个资源,员工和员工组。我正在尝试实现一个不错的 URL 结构,例如:

  • GET /employees 列出员工。
  • GET /employees/123 获取员工 123。
  • GET /employees/groups 列出员工组。
  • GET /employees/groups/123获取员工组123。

使用 ExpressJS 我有:

router.get('/employees', (req, res, next) => { next(); });
router.get('/employees/:id', (req, res, next) => { next(); });
router.get('/employees/groups', (req, res, next) => { next(); });
router.get('/employees/groups/:id', (req, res, next) => { next(); });
router.all('*', (req, res) => { res.send('...'); });

这不起作用,因为 Express 无法区分 /employees/:id/employees/groups。它认为groupsid,因为/employees/:id 排在第一位。

我确实有类似的 URL:

  • GET /employees
  • GET /employees/123
  • GET /employees-groups
  • GET /employees-groups/123

哪个有效,但没有很好的资源/子资源格式。 groupsemployees 的组,所以我希望 URL 与之匹配。

如果我要为员工获取组,那很好 (/employees/:id/groups),但我会获取所有组,即员工组。

如何设置 Express 路由以正确路由,同时仍保持我想要的 URL 结构..?

我想我需要一种方法让 Express 区分 id 和子资源。有没有办法做到这一点..?

更新

我显然应该说我在每个处理程序中使用next(),因为我需要 Express 转移到另一个中间件函数,一个控制所有请求响应的中间件函数。实际发送响应的是另一个中间件函数。所以我需要:

  1. 路由处理程序。
  2. 所有请求的处理程序。

【问题讨论】:

  • 让我补充一点,/nesting/like/this 并没有让它变得任何 更好/employees-groups/ 很有意义。看看你给出的解释:get employee group 123。这显然是一种不同的资源。给它自己的网址
  • 我猜“更好”是主观的。我觉得这样更好,你不知道。我希望 URL 与资源的层次结构相匹配——这些是员工组——所以它们是相关的,如果 URL 显示了这一点,那就太好了 :-)。
  • 肯定是 :) 让你的 url 映射代码做到这一点,而不是对非常规情况进行修复可能会更好:)

标签: javascript node.js rest express


【解决方案1】:

RobbyD 让我走上了正轨。这就是我最终得到的结果:

index.js

router.all('*', setupHandler);
router.get('/employees', getEmployees);
router.get('/employees/groups', getGroups);
router.get('/employees/groups/:id', getGroup);
router.get('/employees/:id', getEmployee);
router.use(errorHandler);

setupHandler()

function setupHandler(req, res, next) {
    res.locals.standardRes = {
        "some": "data"
    };
    res.locals.doResponse = (res) => {
        // ...
        res.json(res.locals.standardRes);
    };
    next();
}

getEmployees()

function getEmployees(req, res, next) {

    somethingThatReturnsAPromise().then(data => {
        // add to res.locals.standardRes here
        res.locals.doResponse(res);
    }).catch(err => {
        next(err);
    });

}

errorHandler()

function errorHandler(err, req, res, next) {
    console.log('err', err);
    // add to res.locals.standardRes here
    // set correct res.status here
    res.locals.doResponse(res);
}

因此,处理程序按照 RobbyD 的回答顺序排列。我使用res.locals 来保存响应函数(doResponse(res))以从每个处理程序中调用。如果出现错误,我会照常调用next(err) 以转移到errorHandler()

我想这一切都是为了获得从中间件到中间件的正确流程并在正确的时间发送响应。

【讨论】:

    【解决方案2】:

    Express 搜索第一个匹配的路由并使用提供的函数对其进行处理。

    换一种方式试试:

    router.get('/employees', (req, res) => {});
    router.get('/employees/groups', (req, res) => {});
    router.get('/employees/groups/:id', (req, res) => {});
    router.get('/employees/:id', (req, res) => {});
    

    现在 express 将通过路由工作,'/employees/123' 将仅匹配最后一个路由,因此 express 将使用一个。 '/employees/groups' 将很快被第二条路由匹配,并且将使用该路由。

    非常简单,但这些事情可能会花费您一些时间来弄清楚。

    【讨论】:

    • 感谢 RobbyD!我已经更新了我的问题,因为我错过了重要的一点......对不起!如果我不使用next(),您的回答确实有效。
    • 为什么不传递用于显式处理所有请求的中间件?我认为只需调用 next() 您就可以让 express 继续,从而通过以下路线进行工作。相反,如果您将中间件分配给一个变量,然后调用它而不是下一个,您会得到您想要的吗? var myMiddleware = require('crazymiddleware').然后只需: router.get('/employees/groups', (req, res, myMiddleware) => {});
    • 你让我走上了正轨!我已经发布了我是如何实际解决这个问题的(使用res.locals)。我不确定尝试替换 next() 是否可行,我认为您可能只是将 next() 重命名为 myMiddleware(),而不是调用其他函数。
    猜你喜欢
    • 2018-03-04
    • 2018-10-07
    • 2018-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多