Express始终将HTTP路径与最后配置的路由匹配

时间:2015-08-09 20:05:50

标签: javascript node.js express

我遇到这个问题,其中配置了一堆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

这显然不正确。

知道这里发生了什么吗?

1 个答案:

答案 0 :(得分:3)

你应该替换

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

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

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

page引用意外值的原因是因为forwhiledo等不会为其块内的代码创建闭包。因此,page被提升到最近的闭包(then()回调),并且为循环的每次迭代重新赋值。但是,当您的路由处理函数实际执行时,page(到那时)指向循环执行期间的最后一个值(过去)。 page的最后一个值是admin路由,因为这是Page中的最后一个属性值。

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