2015-07-10 167 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