2012-10-09 118 views
2

我試圖將身份驗證添加到基於Express的服務器。我注意到路由的一些奇怪的行爲。在通過特定路由處理程序提供響應之後調用通用路由處理程序

我蒸的問題本明確代碼:

app.get('/', function (req, res) { 
    console.log('this is reached first'); 
    res.send('Hello'); 
}); 

app.get('/', function (req, res) { 
    console.log('this is not reached'); 
}); 

app.get('*', function (req, res) { 
    console.log('this is reached'); 
}); 

請求後「/」第一次處理程序被調用。它提供了一個響應,不會調用next()。因此,我很驚訝地發現第三個處理程序('*')也被稱爲! 另一個驚喜是傳遞給第三個處理程序的響應('res')與傳遞給第一個處理程序的響應不同。 (如果我要從第一個處理程序調用next(),那麼第二個處理程序將被調用,並且具有相同的響應對象。)

現在對於我的真實場景:我想處理請求並驗證全局身份驗證方式。但是,有些路由應該保持可用於未經過身份驗證的用戶。我基於Zikes' answer的解決方案。 我首先路由了「自由」路徑。然後我包含了一個全部路由處理程序('*')。如果用戶通過驗證,它會調用next(),否則調用next(err)。以下是所有「限制」路線。最後,我使用app.use自己的錯誤處理程序。它看起來像這樣:

app.use(app.router); 
app.use(function(err, req, res, next) { 
    console.log('An error occurred: ' + err); 
    res.send(401, 'Unauthorized'); 
}); 

app.get('/', function (req, res) { 
    res.send('Hello all'); 
}); 

app.all('*', function (req, res, next) { 
    if (req.user) { 
     next(); // authorized 
    } else { 
     next(new Error('401')); // unauthorized 
    } 
}); 

app.get('/members', function (req, res) { 
    res.send('Hello member'); 
}); 

這工作得很好,阻止訪問'/成員'。但是,它存在一些錯誤:即使在訪問非限制路徑('/')時也會發生身份驗證檢查和錯誤處理。發送預期響應之後,錯誤處理程序將嘗試發送401錯誤響應。後者不會被髮送,但代碼不應該運行。

另外,此機制的副作用是未認證的用戶對於不存在的頁面會收到401錯誤。在某些情況下,我可能想要返回404。但現在我只是推...

我還挺有2個問題:

  1. 這種行爲是錯誤?一般處理程序是否應該在未被調用的情況下調用?
  2. 什麼將是一個很好的解決方案,以鉤住許多但不是所有的路線,而不必單獨標記它們?

回答

2

對於第一個問題,我的猜測是瀏覽器正在發送多個請求。

例如,當您瀏覽到http://localhost:3000/ Chrome也會要求http://localhost:3000/favicon.ico

可以打印正趕上了這樣的要求:

app.get('*', function (req, res) { 
    console.log('this is reached'); 
    console.log('url ' + req.url); 
}); 
+1

你也可以使用'app.use(express.logger('dev'))'這是更好打印出來的信息 –

1
app.get('/', function(req, res, next) { 
    console.log('this is reached first.'); 
    next(); 
}, function (req, res) { 
    console.log('this is reached last.'); 
    res.send('Hello all'); 
}); 

我通常會構建它是這樣的:

var auth = require('../modules/authenticate'); 

app.get('/', auth.requireLogin, routes.index.get); 
app.delete('/items/:id', auth.requireLogin, auth.permission.item.delete, routes.item.delete); 
+0

關於第一部分,你的代碼確實更優雅,但這不是問題。我的代碼示例可能是不正確的,但它只是用來表示一個觀點。無論如何,強調的是'get *'聲明本應該不被調用(在我看來)。 關於第二部分,我不太確定'routes.index.get'是什麼意思。 – oferei

+0

routes.index是處理所有請求的模塊。 – chovy

1

首先,您可能在點擊主頁(favicon.ico)時有兩個請求。使用app.use(express.logger('dev'))來記錄請求。此外,您可以嘗試阻止該路徑:

app.use('/favicon.ico', function(req, res) { 
    console.log('FAVICON') 
    res.send(404) 
} 

其次,您要處理您的錯誤。發送錯誤的好方法:

var err = new Error('this is my error message.') 
err.status = 404 // You want to send a "Not Found" page 
next(err) 

app.use(function(err, req, res, next) { 
    var status = err.status 
    if (status === 404) res.send(404, 'Page not found.'); 
    else res.send(500, 'Something went wrong.'); 
}) 

所有處理的錯誤都應該具有狀態。

相關問題