Reputation: 543
I am using jwtwebtoken
, express
, mongo
, nodejs
, bcrypt
for my backend APIs. I notices that the router that i have for localhost:3000/account/update
path is getting executed but it returns 404 after successful operation. Db connection is fine. but the localhost:3000/account/update
route in postman gets 404 Why?
server.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const dbUtils = require('./services/dbconnect');
module.exports = app;
// get an instance of the router for account api routes.
const accountRoutes = require('./services/account');
const registerRoutes = require('./services/register');
const authenticateRoutes = require('./services/authentication');
// used to create, sign, and verify tokens
const jwt = require('jsonwebtoken');
// Secret key to generate new tockens.
app.set('superSecret', "11111"); // secret variable
// parse application/json
app.use(bodyParser.json())
app.use('/account', accountRoutes);
app.use('/authenticate', authenticateRoutes);
app.use('/register', registerRoutes);
app.get('/', (req, res) => res.send('Hello World!'));
dbUtils.connectToServer(() =>{
app.listen(3000, () => console.log('Server listening on port 3000!'));
});
each-request.js
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
const app = require('./../server');
function verifyLoginTokenForEachRequest(req, res, next) {
// check header parameters for token
const token = req.headers['x-access-token'];
try {
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), (err, decoded) => {
console.log(decoded);
if (err) {
throw err || new Error('not authorized')
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log("[authorized] user logged in")
next();
}
});
} else {
// if there is no token, return an error
return res.status(403).send({msg: "not authorized"});
}
} catch(e) {
console.log("[unauthorized]", e);
return res.status(401).json({ msg: 'Failed to authenticate token.' });
} finally{
next();
}
}
module.exports={verifyLoginTokenForEachRequest};
account.js
const express = require('express')
const router = express.Router();
const verifyLoginTokenForEachRequest = require('./each-request').verifyLoginTokenForEachRequest;
const dbUtils = require('./dbconnect');
router.use(verifyLoginTokenForEachRequest);
router.post('/update', (req, res) => {
const body = req.body;
const db = dbUtils.getDb();
console.log(body);
try {
db.collection('users')
.updateOne({emailid: body.emailid},{$set:{firstName: body.firstName, lastName: body.lastName, lastModified: body.lastModified}},function(err, doc){
if(err || !doc) {
console.log(err);
throw err || new Error('Failed to update')
} else {
console.log("[success] update success");
console.log(doc)
res.status(200).send();
}
});
} catch(e) {
console.error("[error] failed to update");
res.status(500).send({msg: 'failed to update'});
}
});
module.exports = router;
Upvotes: 0
Views: 535
Reputation: 708026
There are a bunch of issues I see wrong here. I'm not sure which ones exactly do or don't add up to exactly what you asked about, but you need to first clean up these issues and then see what problems are left (if any).
Your verifyLoginTokenForEachRequest()
function is always calling next()
, even when it's already sent a 401 or 403 response. If you send a response, don't call next()
. next()
tells Express to keep going to the next routes. Once you've sent a response, you're done. Stop further routing. Don't call next()
.
In verifyLoginTokenForEachRequest()
, you are trying to create an error condition by doing a throw xxx
inside an async callback and then expecting to catch that at a higher level. That will not work. The exception will just go back into the bowels of jwt.verify()
and will not get to your exception handler. Async exceptions (unless they are part of a promise .then()
handler) will go nowhere. You can't throw and catch them at a higher level. You have to deal with them where they occur. In this case, you should just send your error response right there.
In verifyLoginTokenForEachRequest()
, you're calling next()
in a finally clause when you very well may have already sent the response. Only call next()
when you want routing to continue to other route handlers and you have not yet sent a response.
In router.post('/update', ...)
, you're again throwing an exception inside an async callback which will not be caught by your exception handler. Can't do that. Send the error response there or use promises so you can more easily propagate the error to a higher level.
Here's a fixed up version of verifyLoginTokenForEachRequest()
:
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
const app = require('./../server');
function verifyLoginTokenForEachRequest(req, res, next) {
// check header parameters for token
const token = req.headers['x-access-token'];
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), (err, decoded) => {
console.log(decoded);
if (err) {
console.log("[verifyLoginTokenForEachRequest]", err);
return res.status(401).json({msg: 'Failed to authenticate token.'});
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log("[authorized] user logged in")
next();
}
});
} else {
// if there is no token, return an error
return res.status(403).send({msg: "not authorized"});
}
}
module.exports = {
verifyLoginTokenForEachRequest
};
Here's a fixed up version of account.js:
const express = require('express')
const router = express.Router();
const verifyLoginTokenForEachRequest = require('./each-request').verifyLoginTokenForEachRequest;
const dbUtils = require('./dbconnect');
router.use(verifyLoginTokenForEachRequest);
router.post('/update', (req, res) => {
const body = req.body;
const db = dbUtils.getDb();
console.log(body);
db.collection('users')
.updateOne({emailid: body.emailid},{$set:{firstName: body.firstName, lastName: body.lastName, lastModified: body.lastModified}},function(err, doc){
if(err || !doc) {
console.error("[error] failed to update", err);
res.status(500).send({msg: 'failed to update'});
} else {
console.log("[success] update success");
console.log(doc);
res.end();
}
});
});
module.exports = router;
Upvotes: 2