【问题标题】: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 来达到这个目的。

标签: javascript node.js express coffeescript middleware


【解决方案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
         });
        

        【讨论】:

        【解决方案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
        猜你喜欢
        • 2018-07-15
        • 1970-01-01
        • 2017-03-05
        • 2018-09-11
        • 2016-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-03
        相关资源
        最近更新 更多