从快速中间件中排除路由

Posted

技术标签:

【中文标题】从快速中间件中排除路由【英文标题】:Exclude route from express middleware 【发布时间】:2015-01-22 21:40:27 【问题描述】:

我有一个节点应用程序,就像防火墙/调度程序一样位于其他微服务前面,它使用如下中间件链:

...
app.use app_lookup
app.use timestamp_validator
app.use request_body
app.use checksum_validator
app.use rateLimiter
app.use whitelist
app.use proxy
...

但是对于特定的 GET 路由,我想跳过除 rateLimiter 和代理之外的所有路由。他们是一种使用 :except/:only 设置像 Rails before_filter 这样的过滤器的方法吗?

【问题讨论】:

你可以使用 'express-unless' npm 来达到这个目的。 【参考方案1】:

即使 expressjs 中没有内置的中间件过滤系统,您至少可以通过两种方式实现这一点。

第一种方法是挂载所有要跳转到正则表达式路径的中间件,而不是包含否定查找:

// Skip all middleware except rateLimiter and proxy when route is /example_route
app.use(/\/((?!example_route).)*/, app_lookup);
app.use(/\/((?!example_route).)*/, timestamp_validator);
app.use(/\/((?!example_route).)*/, request_body);
app.use(/\/((?!example_route).)*/, checksum_validator);
app.use(rateLimiter);
app.use(/\/((?!example_route).)*/, whitelist);
app.use(proxy);

第二种方法,可能更易读和更简洁,是用一个小的辅助函数包装你的中间件:

var unless = function(path, middleware) 
    return function(req, res, next) 
        if (path === req.path) 
            return next();
         else 
            return middleware(req, res, next);
        
    ;
;

app.use(unless('/example_route', app_lookup));
app.use(unless('/example_route', timestamp_validator));
app.use(unless('/example_route', request_body));
app.use(unless('/example_route', checksum_validator));
app.use(rateLimiter);
app.use(unless('/example_route', whitelist));
app.use(proxy);

如果您需要比简单的path === req.path 更强大的路由匹配,您可以使用 Express 内部使用的path-to-regexp module。

更新:- 在express 4.17 req.path 中只返回'/',所以使用req.baseUrl

var unless = function(path, middleware) 
    return function(req, res, next) 
        if (path === req.baseUrl) 
            return next();
         else 
            return middleware(req, res, next);
        
    ;
;

【讨论】:

此处链接的 path-to-regexp 模块已弃用。这是正确的:github.com/pillarjs/path-to-regexp 我知道这很旧,但我想知道为什么有人指出正则表达式已损坏。当路由为/example_route 时,您的正则表达式仍匹配/,因此不会跳过路由。 @guillaume 在下面给出了正确的几个答案:/^\/(?!path1|pathn).*$/【参考方案2】:

这里有很多很好的答案。不过我需要一个稍微不同的答案。

我希望能够从所有 HTTP PUT 请求中排除中间件。所以我创建了一个更通用的unless 函数版本,它允许传入谓词:

function unless(pred, middleware) 
    return (req, res, next) => 
        if (pred(req)) 
            next(); // Skip this middleware.
        
        else 
            middleware(req, res, next); // Allow this middleware.
        
    

示例用法:

app.use(unless(req => req.method === "PUT", bodyParser.json()));

【讨论】:

【参考方案3】:

我实现这一点的方法是为这样的特定路径设置中间件

app.use("/routeNeedingAllMiddleware", middleware1);
app.use("/routeNeedingAllMiddleware", middleware2);
app.use("/routeNeedingAllMiddleware", middleware3);
app.use("/routeNeedingAllMiddleware", middleware4);

然后像这样设置我的路线

app.post("/routeNeedingAllMiddleware/route1", route1Handler);
app.post("/routeNeedingAllMiddleware/route2", route2Handler);

对于不需要所有中间件的其他特殊路由,我们像这样设置另一个路由

app.use("/routeNeedingSomeMiddleware", middleware2);
app.use("/routeNeedingSomeMiddleware", middleware4);

然后像这样设置相应的路由

app.post("/routeNeedingSomeMiddleware/specialRoute", specialRouteHandler);

这方面的 Express 文档可在 here 获得

【讨论】:

【参考方案4】:

这是一个使用path-to-regexp 的示例,正​​如@lukaszfiszer 的回答所建议的那样:

import  RequestHandler  from 'express';
import pathToRegexp from 'path-to-regexp';

const unless = (
  paths: pathToRegexp.Path,
  middleware: RequestHandler
): RequestHandler => 
  const regex = pathToRegexp(paths);
  return (req, res, next) =>
    regex.exec(req.url) ? next() : middleware(req, res, next);
;

export default unless;

【讨论】:

【参考方案5】:

基于@lukaszfiszer 的回答,因为我希望排除不止一条路线。 您可以在此处添加任意数量。

var unless = function(middleware, ...paths) 
  return function(req, res, next) 
    const pathCheck = paths.some(path => path === req.path);
    pathCheck ? next() : middleware(req, res, next);
  ;
;

app.use(unless(redirectPage, "/user/login", "/user/register"));

抱歉,无法添加为评论。

【讨论】:

很好的答案。应该是解决方案。 我同意这值得更多的支持。简洁明了。 只是将其全部转换为 ES6 和单行:const unless = (middleware, ...paths) => (req, res, next) => paths.some(path => path === req.path) ? next() : middleware(req, res, next); 漂亮的解决方案。如果它接受/users/:id/login,那就太好了 @EricGuan 您可以通过对路径检查稍作调整来实现这一点。而不是path === req.path,您可以使用一些逻辑来检查路径的动态部分并将其剥离或忽略。也许正则表达式会做得最好【参考方案6】:

你可以像下面这样定义一些路由。

 app.use(/\/((?!route1|route2).)*/, (req, res, next) => 

    //A personal middleware
    //code

    next();//Will call the app.get(), app.post() or other
 );

【讨论】:

您好,请为您的答案添加一些解释并格式化您的代码。请参阅***.com/help/formatting 了解更多信息。【参考方案7】:

我成功地使用了这个正则表达式:/^\/(?!path1|pathn).*$/

【讨论】:

我喜欢这个!但我会尽量不要将它用于负载较重的服务器。我会尝试在路线上省略正则表达式。 RegExp匹配确实很快,但是字符串比较快很多。【参考方案8】:

您也可以通过在 req.originalUrl 上设置条件来跳过这样的路线:

app.use(function (req, res, next) 

    if (req.originalUrl === '/api/login') 
    return next();
     else 
         //DO SOMETHING
    

【讨论】:

技术上没有错,但并没有真正涵盖动态路径,例如/:user/stuff

以上是关于从快速中间件中排除路由的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 和 Express 排除路由表单 JWT 中间件

Node.js和Express排除路由形式JWT中间件

为啥我不能在我的快速路由中使用中间件?

如何从路由器访问应用程序级中间件?

将 Auth 中间件应用于所有 Laravel 路由

JWT 不适用于快速路由器