2015-07-10 169 views
4

我正在研究一个拥有多个路由和子路由的NodeJS应用,并使用Express来管理它们。我的应用程序的一个特点是显示类的列表,每个软件版本的一个类列表。针对这一特点,我对路线“类”三子路径:高速路由:路由被忽略

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时,它不会评估第二条路线,除非我为该类的名称输入完整的废话。如果我给出一个正确的类的名称,它立即在第三个路由(即使我只给它一个参数),并给出:version参数的软件的最新版本号,并尝试resolve/classes/lastVersion/nameOfTheClass。

你有什么想法,为什么它只是忽略只有一个参数的第二条路线,直接进入第三条路线,自动给出一个有效的版本号?

EDITED - >更多的代码,现在

为了帮助你帮我,这里是关于应用中的一些附加的相关信息: 在我谟的根,我有一个文件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; 

你会问自己“为什么之前到底他做那个“:该平台需要架构(需要自己声明快速应用的应用文件的服务器文件),我将不得不部署我的应用。让我们继续进一步的体系结构。

您当然有注意行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... 

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

最新资讯

嘿!你认为这个问题很奇怪吗?它变得更加陌生!正如用户kanzelm建议的那样,我使用控制台。记录一切(意思是:在每条路线的开始,我做console.log('nameOfTheRoute');),以及一些研究结果完全出乎意料:

  1. localhost:3000/classes:原木/:version,并直接进入localhost:/classes/lastVersionNumber; 这真的出乎意料
  2. localhost:3000/classes/aValidVersionNumber:日志/:version,并转到正确的版本页面;这很正常
  3. localhost:3000/classes/aNotValidVersionNumber:日志/:version,并查找一个名称为无效版本号的类,失败,然后重定向到404类未找到的页面;这是正常的
  4. localhost:3000/classes/aNotValidClassName:log /:version,查找与此名称的类,失败,并重定向到404类未找到页面;这是正常的
  5. localhost:3000/classes/aValidVersionNumber/aValidClassName:日志/:version/:className并转到正确的类页面;这是正常的
  6. localhost:3000/classes/aValidVersionNumber/aNotValidClassName:日志/:version/:className并转到404类未找到的页面;这是正常的

所以,在这里我有两个主要问题我甚至无法理解:第一,根路径是完全忽略,总是擦肩而过,甚至当我尝试去locahost:3000/classes;它似乎是自动完成与最后一个有效的版本号。理论上,这就是我想要的(查看第一条路线的代码),但没有显示路线中的console.log,也没有显示方法classesGetLastVersion。其次,仅当给出有效版本号(完全正常)或给出无效版本号/类名称(根本不完全没有问题)时才会捕获路由/:version这一事实,这使得我疯狂。

有什么想法?

+0

恐怕我们在这里缺乏一些信息。只用这个代码,我们无法猜测为什么一个有效的魔法版本号从野外出现。也许你使用一些URL重写工具? (阿帕奇也许?) – Magus

+0

我会编辑第一篇文章,向您展示路由中的一些代码;-)因此,我不知道你在说什么(url重写工具),所以我想我没有使用它,除非意外: -/ – TheFrenchieCake

+0

你错过了'});'关闭'classesGetLastVersion('在你的第一条路线 – Plato

回答

1

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

作为一名同事,我创建了一个模拟新版本的虚拟文件,以及在应用程序中搜索的一组新类。然后我试着去localhost:3000/classes,在我非常吃惊的时候,即使我创建了一个错误的“2.2”版本,我仍然会进入与之前相同的版本号,即“2.1”。虽然用Ctrl + F5清空缓存并没有做任何事情。因此,我在浏览器的开发人员工具页面中选中了“当开发人员工具箱打开时禁用缓存”选项,然后所有事情都很顺利。正如预期的那样,每条路线都被正确地抓住并正在完成各自的工作。

用户Dan Pantry给我提供了绕过缓存的解决方案:将Cache-Control: 0放入不同路由的快速响应头部。

+1

要禁用/旁路缓存,您可以向要绕过缓存的文件附加版本查询字符串,ala'?v = 123',其中'123'是请求的时间戳。如果你不能这样做,那么将'Cache-control'设置为'0'。使用构建工具(如gulp)预处理所有静态资产,并将内容哈希附加到其名称中,以避免它们被缓存。请注意查询字符串路由,因为它们可能会导致服务器(和客户端,由于额外的http请求,他们可能不需要)额外的负载) –

+0

'缓存控制:0'解决方案对我来说很好:-)谢谢你真是太棒了! – TheFrenchieCake

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(); 
     } 

尝试登录“存在”,看如果这是真的还是假的。

+0

嗯,这让事情变得更加奇怪。正如你所建议的那样,我在每条路线的开始都插入了一个'console.log(nameOfTheRoute)'来查看我尝试访问每条路线时的位置。结果不是我所期望的: 'localhost:3000/classes // logs“/:version”'; 'localhost:3000/classes/aVersionNumber // logs“/:version”'; 'localhost:3000/classes/aVersionNumber/aValidClassName // logs“/:version /:name”'; 'localhost:3000/classes/aValidClassName // logs“/:version /:name”'; 'localhost:3000/classes/anINVALIDClassName // logs“/:version”'。 – TheFrenchieCake

+0

在星期一,我会尝试通过重构代码来解决问题,并在单个路由中使用queryString,而不是使用optionnal参数的多个参数化路由。 – TheFrenchieCake