【问题标题】:Express always matches the HTTP path to the last configured routeExpress 始终将 HTTP 路径与最后配置的路由匹配
【发布时间】:2015-08-09 20:05:50
【问题描述】:

我有这个问题,其中配置了一堆 Express 路由,最后一个路径是 /admin。当我从服务器请求任何路径时,只会调用 /admin 路由的回调。

这是我的主要 Node.js 源文件,可以查看如何设置路由:

require('babel/register')({
    stage: 0
});

var React = require('react/addons'),
    express = require('express'),
    bodyParser = require('body-parser'),
    path = require('path'),
    logger = require('morgan'),
    pg = require('pg'),
    pgpromise = require('./server/pg-promise'),
    Page = require('./ui/js/Page'),
    pgrest = require('./server/pgrest'),
    App = React.createFactory(require('./ui/js/App'));

const CONNECTION_STRING = 'postgres://foobar:foobar@localhost/postgres';

var app = express();

app.use(bodyParser.json());
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '..', 'dist')));
app.set('views', path.join(__dirname, 'ui'));
app.set('view engine', 'jade');

pgrest.init(
    app,
    CONNECTION_STRING
).then(function(api) {
    // React server-side rendering
    for (var pageKey in Page) {
        if (Page.hasOwnProperty(pageKey)) {
            var page = Page[pageKey];
            if (page.serverRendered) {
                app.get(page.path, function(req, res) {

                    console.log(req.path + ' matched ' + page.path);

                    function render(data) {
                        var reactHtml = React.renderToString(App({
                            initialPageName: page.name,
                            initialPageParams: req.params,
                            initialPageQuery: req.query,
                            pageData: data
                        }));

                        res.render('index', { reactHtml: reactHtml });
                    }

                    if (page.restPath) {
                        var restFunction = api.get[page.restPath];
                        if (restFunction) {
                            restFunction(req).then(render).fail(function(err) {
                                if (err.status == 401 && !err.loggedIn) {
                                    // Redirect to the login page if the REST status is 401 and the user isn't logged in.
                                    res.redirect('/login');
                                } else {
                                    // Render the requested page with the error details shown.
                                    res.render('index', {
                                        reactHtml: React.renderToString(App({
                                            initialPageName: page.name,
                                            initialPageParams: req.params,
                                            initialPageQuery: req.query,
                                            notFound: err.status == 404,
                                            notAuthorized: err.status == 401,
                                            unknownError: [401, 404].indexOf(err.status) < 0 ? (err.message || 'An unknown error occurred.') : null
                                        }))
                                    });
                                }
                            });
                        } else {
                            render();
                        }
                    } else {
                        render();
                    }
                });
            }

            console.log(page.name + ' => ' + page.path);
        }
    }

    // 404
    app.use(function(req, res, next) {
        var err = new Error('Not Found');
        err.status = 404;
        next(err);
    });

    app.listen(3001);
}).fail(function(err) {
    console.error('Error: ' + JSON.stringify(err));
});

这里是Page

module.exports = {
    Home: {
        name: 'Home',
        path: '/',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Home'),
        restPath: null,
        dataTarget: null
    },
    Search: {
        name: 'Search',
        path: '/search',
        pageParams: [],
        queryParams: ['q'],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Search'),
        restPath: null,
        dataTarget: null
    },
    Register: {
        name: 'Register',
        path: '/register',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Register'),
        restPath: null,
        dataTarget: null
    },
    Activate: {
        name: 'Activate',
        path: '/activate',
        pageParams: [],
        queryParams: ['email', 'code'],
        clientRendered: false,
        serverRendered: true,
        component: require('./pages/Activate'),
        restPath: null,
        dataTarget: null
    },
    ForgotPassword: {
        name: 'ForgotPassword',
        path: '/forgot-password',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/ForgotPassword'),
        restPath: null,
        dataTarget: null
    },
    ResetPassword: {
        name: 'ResetPassword',
        path: '/reset-password',
        pageParams: [],
        queryParams: ['email', 'code'],
        clientRendered: false,
        serverRendered: true,
        component: require('./pages/ResetPassword'),
        restPath: null,
        dataTarget: null
    },
    Login: {
        name: 'Login',
        path: '/login',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Login'),
        restPath: null,
        dataTarget: null
    },
    Profile: {
        name: 'Profile',
        path: '/profile',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Profile'),
        restPath: '/api/1/profile',
        dataTarget: 'profile'
    },
    Member: {
        name: 'Member',
        path: '/member/:id',
        pageParams: ['id'],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Member'),
        restPath: '/api/1/member/:id',
        dataTarget: 'member'
    },
    Inbox: {
        name: 'Inbox',
        path: '/inbox',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Inbox'),
        restPath: null,
        dataTarget: null
    },
    Administration: {
        name: 'Administration',
        path: '/admin',
        pageParams: [],
        queryParams: [],
        clientRendered: true,
        serverRendered: true,
        component: require('./pages/Administration'),
        restPath: null,
        dataTarget: null
    }
};

当我启动 Node 应用程序时,我得到以下输出:

Home => /
Search => /search
Register => /register
Activate => /activate
ForgotPassword => /forgot-password
ResetPassword => /reset-password
Login => /login
Profile => /profile
Member => /member/:id
Inbox => /inbox
Administration => /admin

当我在浏览器中访问一个页面时,我会得到这个控制台输出:

/ matched /admin

这显然是不正确的。

知道这里发生了什么吗?

【问题讨论】:

  • 这里是在黑暗中拍摄的,但看起来您并没有在点击页面后结束您的请求。循环继续。尝试使用 return 语句或 res.send 或类似的方式。
  • for 循环仅在服务器启动时迭代一次,之后 pgrest 组件已初始化。
  • 与您的问题无关,只是在您的代码上:当您包含pg-promise 时,也不要包含pg,因为pg-promise 公开了它通过属性pg 使用的pg初始化后,即require('pg-promise')(/*options*/).pg.
  • 这实际上是我自己的小图书馆,而不是this one。话虽如此,pg 是一个旧的require,我还没来得及删除。而pg-promise本身也不再在主js文件中使用。这一切都包含在pgrest(我的另一个小图书馆)中。

标签: javascript node.js express


【解决方案1】:

你应该替换

for (var pageKey in Page) {
  if (Page.hasOwnProperty(pageKey)) {
    // ...
  }
}

Object.keys(Page).forEach(function(pageKey) {
  // ...
});

否则,您在路由处理程序中对 page 的引用将不是您所期望的,从它的外观来看,这可能是导致您的问题的原因。

page 引用意外值的原因是因为forwhiledo 等不为其块内的代码创建闭包。所以发生的事情是page 被提升到最近的闭包(then() 回调),并且它的值被重新分配给循环的每次迭代。但是,当您的路由处理程序函数实际执行时,page (到那时)指向在循环执行期间(过去)设置的最后一个值。 page 的最后一个值将是管理路由,因为那是 Page 中的最后一个属性值。

使用forEach() 创建一个新的闭包,以便page 的值被“捕获”,因此将始终引用您期望在路由处理程序中的值。

【讨论】:

  • 这将遍历页面键,而不是页面对象。但我会尝试这种迭代风格,看看会发生什么。
  • 这种迭代方式有效,我希望您在我接受之前更正答案。应该是Object.keys(Page).forEach(function(pageKey) { var page = Page[pageKey]; /* ... */ });
  • 它解决了这个问题,但我仍然不知道为什么,因为我们仍在从同一个 Page 对象中按键查找对象。
  • @SteveTaylor 我已经在我的回答中解释了更多。我希望这样可以更清楚地了解发生了什么。
  • 感谢您的解释!似乎问题在于愚蠢的 JavaScript 变量范围。变量的作用域是函数而不是块,因此在for-in 循环中,只有一个page 变量,并且在调用任何请求处理程序时,它指向最后一个,即@ 987654340@。额外的关闭解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-04
  • 2019-07-20
  • 2020-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多