2015-05-04 55 views
0

我寫了一個快速應用程序來創建一個REST API。Express.js:錯誤處理程序後,執行不會停止。爲什麼?

據我瞭解這一點,如果我發送一個報頭具有無效令牌,jwt.verify套犯錯(JsonWebTokenError),這是傳遞給我的錯誤處理程序,並把它發送401:

res.status(401).send({ 
     success: false, 
     message: err.name 
}); 

和執行應該結束。但相反,我得到這個:

Start token verification 
Error before: JsonWebTokenError 
JsonWebTokenError: invalid signature 
at Object.module.exports.verify (/srv/lonja/node_modules/jsonwebtoken/index.js:129:17) 
at /srv/lonja/app/routes/api.js:76:17 
at Layer.handle [as handle_request] (/srv/lonja/node_modules/express/lib/router/layer.js:82:5) 
at trim_prefix (/srv/lonja/node_modules/express/lib/router/index.js:302:13) 
at /srv/lonja/node_modules/express/lib/router/index.js:270:7 
at Function.proto.process_params (/srv/lonja/node_modules/express/lib/router/index.js:321:12) 
at next (/srv/lonja/node_modules/express/lib/router/index.js:261:10) 
at Function.proto.handle (/srv/lonja/node_modules/express/lib/router/index.js:166:3) 
at router (/srv/lonja/node_modules/express/lib/router/index.js:35:12) 
at Layer.handle [as handle_request] (/srv/lonja/node_modules/express/lib/router/layer.js:82:5) 
This is the last message related to[object Object] 
GET /api/home_gallery/ 401 20.834 ms - 47 
_http_outgoing.js:335 
throw new Error('Can\'t set headers after they are sent.'); 
    ^
Error: Can't set headers after they are sent. 
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11) 
at ServerResponse.header (/srv/lonja/node_modules/express/lib/response.js:700:10) 
at ServerResponse.send (/srv/lonja/node_modules/express/lib/response.js:154:12) 
at ServerResponse.json (/srv/lonja/node_modules/express/lib/response.js:240:15) 
at /srv/lonja/app/routes/api.js:214:17 
at /srv/lonja/node_modules/mongoose/node_modules/kareem/index.js:103:16 
at process._tickCallback (node.js:355:11) 

所以,在錯誤處理後,執行路由處理程序,我不明白爲什麼。

我的代碼(只是相關位):

var User = require('../models/user'); 
var myImage = require('../models/image'); 
var jwt = require('jsonwebtoken'); 
var config = require('../../config'); 

// super secret for creating tokens 
var superSecret = config.secret; 

module.exports = function(app, express) { 

    var apiRouter = express.Router(); 

我用這個中間件來驗證jsonwebtoken:

// route middleware to verify a token 
apiRouter.use(function(req, res, next) { 
    // do logging 
    console.log('Start token verification'); 

    // check header or url parameters or post parameters for token 
    var token = req.body.token || req.params.token || req.headers['x-access-token']; 

    // decode token 
    // verifies secret and checks exp 
    if (token) { 
     jwt.verify(token, superSecret, function(err, decoded) { 
      if (err) { 
       return next(err); 
      } 
      // if everything is good, save to request for use in other routes 
      else req.decoded = decoded; 
     }); 
    } 
    // if there is no token 
    // return an HTTP response of 401 (access unauthorized) and an error message 
    else { 
     var noToken = new Error('Error_NoTokenProvided'); 
     return next(noToken); 
    } 
    next(); // make sure we go to the next routes and don't stop here 
}); 

稍後在代碼中,我有這個路由處理:

apiRouter.route('/home_gallery') 
// get all the images 
.get(function(req, res) { 
    myImage.find(function(err, images) { 
     if (err) res.send(err); 
     // return the users 
     res.json(images); 
    }); 
}); 

最後,這最後一塊中間件做錯誤處理:

apiRouter.use(function(err, req, res, next) { 
    console.log('Error before: ' + err.name); 
    if (err.name == 'Error_NoTokenProvided' || 
     err.name == 'JsonWebTokenError') { 
     res.status(401).send({ 
      success: false, 
      message: err.name 
     }); 
    } else if (err.name == 'TokenExpiredError') { 
     res.redirect('/login'); 
    } else { 
     res.status(500).send({ 
      success: false, 
      message: err.name 
     }); 
    } 
    console.log(err.stack); 
    console.log('This is the last message related to' + req); 
}); 

回答

3

這個問題似乎是這樣的:

if (token) { 
    jwt.verify(token, superSecret, function(err, decoded) { 
     if (err) { 
      return next(err); 
     } 
     // if everything is good, save to request for use in other routes 
     else req.decoded = decoded; 
    }); 
} else { ... } 
next(); 

由於jwt.verify()是一個臺異步方法,處理該函數調用,這意味着該底部next()被調用後繼續進行。如果令牌驗證失敗,請致電next(err);換句話說,你可以撥打next()兩次。

一個可能的解決辦法是:

if (token) { 
    // We use `return` only to stop further processing of the middleware, 
    // we don't actually care about the return value from `jwt.verify()`. 
    return jwt.verify(token, superSecret, function(err, decoded) { 
     if (err) { 
      return next(err); 
     } 
     // if everything is good, save to request for use in other routes 
     else { 
      req.decoded = decoded; 
      next(); 
     } 
    }); 
} 

類似的事情是發生在你的路由處理:

if (err) res.send(err); 
// return the users 
res.json(images); 

如果err設置,你可以調用兩個res.send()res.json(),因爲調用res.send()沒有按」奇蹟般地停止了對代碼其餘部分的進一步處理。

類似的解決方案爲可以使用上面:

if (err) return res.send(err); 
res.json(images); 

或者,如果你喜歡:

if (err) { 
    res.send(err); 
} else { 
    res.json(images); 
} 
+0

我一直在想這件事,我有一個疑問,是不是轉換代碼同步執行所有這些「if else」語句。執行必須等待_asyncronous_調用才能繼續執行,否則我錯過了一些東西? – mvillar

+0

@mvillar這裏的異步函數是'jwt.verify()',我們等待在調用next()之前完成它(在完成回調函數中調用'jwt.verify()') 。 'if/else'只是爲了區分哪個調用要'next()'。 – robertklep

相關問題