快速路由:路由被忽略

时间:2015-07-10 13:39:25

标签: javascript node.js express

我正在开发一个拥有多个路由和子路由的NodeJS应用程序,并使用Express来管理它们。我的应用程序的一个功能是显示类列表,每个版本的软件一个类列表。对于此功能,我在路线'classes'中有三个子路由:

var express = require('express'),
router = express.Router();

var fs = require('fs');
var path = require('path');

router.get('/', function(req, res){
    // default route, redirect to the list of classes of the last version of the software
    // classesGetLastVersion(cb) reads a JSON file and launch callback with last version number
    classesGetLastVersion(function(version) { 
    res.writeHead(301, {
        Location: (req.socket.encrypted ? 'https://' : 'http://') + req.headers.host + '/classes/' + version
    });
    res.end();
});

router.get('/:version', function(req, res){
    // checks the given version in argument, display the list of classes corresponding 
    // to the version (if it exists; else: 404 error page)

    // retrieve the version number specified
    var version = req.params.version;

    // NOTE: we only serve static HTML pages, so here I directly check if the 
    // corresponding file exists
    fs.exists('public/html/classes_' + version + '.html', function(exists){
    if(exists){
        var options = {
            root: path.join(__dirname, __publicRootPath)
        };

        // file exists, serve it
        res.status(200);
        res.set({'Content-type':'text/html'});
        res.sendFile('./html/classes_' + version + '.html', options);
    } else {
        // file doesn't exists, so we'll check if the req.param.version argument corresponds
        // to a class name, in every version of the software

        /** the file 'data/classes.json' has the following architecture:
         * {
         *      "first_version_number": ["className1", "className2", ..., "classNameN"],
         *      "second_version_number" : ["className1", "className2", ..., "classNameN"],
         *      ...
         *      "nth_version_number": ["className1", "className2", ..., "classNameN"]
         * }
         **/
        fs.readFile('data/classes.json', function(err, data){
            if (err) throw err;

            // for clarification purpose
            var className = version;

            var lastVersion,
                jsonData = JSON.parse(data);

            for(var versionName in jsonData){
                console.log('Searching class in version ' + versionName + '...');

                if(jsonData[versionName].lastIndexOf(className) != -1){
                    console.log('Found it! In v' + versionName);
                    lastVersion = versionName;
                } else {
                    console.log('Class is not here :-(');
                }
            }

            if(lastVersion){
                // redirect to the correct class page
                res.writeHead(301, {
                    Location: (req.socket.encrypted ? 'https://' : 'http://') + req.headers.host + '/classes/' + lastVersion + '/' + className
                });
                res.end();
            } else {
                // render 404 - Page not found
                logger.error('404 error - Page not found: public/html/classes_' + version + '.html');
                res.render('errorpages/404.jade', {});
            }
        });
    }
});

router.get('/:version/:name', function(req, res){
    // check the given version AND name of the class, and display the page corresponding
    // to the specified class, if it exists; else: 404 error page

    var version         = req.params.version;
    var className       = req.params.className;
    className = className
        .replace('<', '_').replace('>', '_')
        .replace('%3CT%3E', '_T_')
        .replace('&lt;T$gt;', '_T_');

    console.log('/:version/:className');

    var fileName = path.join('./public/html/class_' + version, className) + '.html';
    fs.exists(fileName, function(exists){
        if(!exists){
            // 404 class not found
            // render 404 - Class not found
            logger.error('404 error - File not found: '  + fileName);
            res.render('errorpages/404_class_not_found.jade', {classname:className});
        } else {
            fileName = path.join('./html/class_' + version, className) + '.html';

            var options = {
                root: path.join(__dirname, __publicRootPath)
            };

            res.status(200);
            res.set({'Content-type':'text/html'});
            res.sendFile(fileName, options);
        }
    });
});

module.exports = router;

所以,原则上,没有什么是棘手的,一切都很完美,直到我尝试实现一个新功能:如果用户试图输入类的名称而不指定其版本,我想要第二如果该类存在于其中一个版本的软件中,则检查JSON文件的路由,并在找到的最新版本中显示与该类对应的页面。

但是,由于一个未知的原因,当我尝试访问/ classes / nameOfAClass时,它不会评估第二条路径,除非我为类的名称输入完整的废话。如果我给出一个正确类的名称,它会立即进入第三个路由(即使我只给它 ONE 参数),并给出最后一个版本号:version参数的软件,并尝试解析/ classes / lastVersion / nameOfTheClass。

您是否知道为什么它只忽略只有一个参数的第二条路径并直接转到第三条路线,自动提供有效的版本号?

已编辑 - &gt;现在有更多代码

为了帮助您,这里有一些关于该应用的额外信息: 在我的项目的根目录中,我有文件server.js,它声明:

var app = require('./app');

在app.js文件中,我有:

var express  = require('express');
var app      = express();

app.use(compress());

// Serve static files (css, js, images)
app.use(express.static('public'));
app.set('view engine', 'jade');
app.set('views', './views');
app.set('view cache', true);

//require all routes, index.js is called by default
require('./scripts/router')(app);

module.exports = app;

在你问自己“他为什么这么做”之前:在我必须部署我的应用程序的平台上需要那个架构(需要app文件本身声明Express应用程序的服务器文件)。让我们继续在架构中继续。

你肯定注意到了行require('./scripts/router')(app);。在这个路由器文件夹中,我有一个名为“index.js”的文件和一个名为“routes”的文件夹;此文件夹包含我的所有子路由。 index.js文件如下:

module.exports = function (app) {
    // require several subroutes as follow
    app.use('/classes', require('./routes/classes'));
    [...other subroutes...]

    // ERRORS
    // Handle 404
    app.use(function (error, req) {
        req.status(404);
        req.render('errorpages/404.jade', {});
    });

    // Handle 500
    app.use(function (error, req, res, next) {
        res.status(500);
        res.render('errorpages/500.jade', {});
    });
};

所以,在简历中:

myProjectRooT
|_ server.js
|_ app.js
|_ scripts/
    |_ router/
        |_ index.js
        |_ routes/
            |_ classes.js
            |_ otherRoute.js
            |_ etc...

希望有助于理解这个问题: - )

新信息

HEY!你觉得这个问题很奇怪吗?嗯,它甚至更奇怪!正如用户kanzelm建议的那样,我安慰所有人(意思是:在每条路线的开头,我做console.log('nameOfTheRoute');),并且一些结果完全出乎意料:

  1. localhost:3000/classes:记录/:version并直接转到localhost:/classes/lastVersionNumber; 那真是出乎意料
  2. localhost:3000/classes/aValidVersionNumber:记录/:version,然后转到正确的版本页面;这是正常的
  3. localhost:3000/classes/aNotValidVersionNumber:logs /:version,查找名称为无效版本号的类失败,然后重定向到404 class not found页面;这是正常的
  4. localhost:3000/classes/aNotValidClassName:log /:version,查找具有此名称的类,失败并重定向到404 class not found页面;这是正常的
  5. localhost:3000/classes/aValidVersionNumber/aValidClassName:记录/:version/:className并转到正确的课程页面;这是正常的
  6. localhost:3000/classes/aValidVersionNumber/aNotValidClassName:记录/:version/:className并转到404 class not found页面;这是正常的
  7. 所以,在这里,我有两个我甚至无法理解的主要问题:首先,完全忽略根路由并且永远不会被捕获,即使我尝试转到locahost:3000/classes ;似乎网址自动完成,带有最后一个有效版本号。从理论上讲,这就是我想要的(查看第一条路径的代码),但是路线中没有来自classesGetLastVersion方法的console.log。其次,路由/:version仅在给出有效版本号(完全没问题)或者给出NON-VALID版本号/类名时才捕获(完全不行)让我发疯了。

    有什么想法吗?

2 个答案:

答案 0 :(得分:1)

Sooo,问题是...... CACHE。

我作为同事建议我创建虚拟文件以模拟新版本,并在应用程序上搜索一组新类。然后我试着去localhost:3000/classes,令我惊讶的是,我仍然使用与以前相同的版本号,意思是“2.1”,即使我创建了一个虚假的“2.2”版本。但是,使用Ctrl + F5清空缓存并没有做任何事情。所以我在浏览器的开发者工具页面中检查了“当开发者工具框打开时禁用缓存”选项,然后一切都很顺利。正如预期的那样,每条路线都得到了正确的处理并正在各自的工作中。

用户Dan Pantry已经向我提供了绕过缓存的解决方案:将Cache-Control: 0放入不同路由的明确响应的标题中。

答案 1 :(得分:0)

我的预感是这个代码块正在执行,它正在回归“lastVerison”:

        if(lastVersion){
            // redirect to the correct class page
            res.writeHead(301, {
                Location: (req.socket.encrypted ? 'https://' : 'http://') + req.headers.host + '/classes/' + lastVersion + '/' + className
            });
            res.end();
        }

尝试记录“存在”并查看是真还是假。