Why adding a get parameter forces middleware execution - javascript

I have got a middleware like this
// route middleware to verify a token
app.use(function(req, res, next) {
console.log(req.baseUrl);
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
This route http://localhost:8080/verifyAccount doesn't responds as No token provided
app.get('/verifyAccount', function (req, res) {
res.json({ message: 'Welcome to verify account!' });
});
But the following route http://localhost:8080/verifyAccount?id=123 does:
app.get('/verifyAccount/:id', function (req, res) {
res.json({ message: 'Welcome to verify account!' });
});
The middleware code is at the bottom in the code file and the get paths are upwards
What is that concept behind?
Why adding a get parameter forces middleware execution?
Just found that if I call it like this http://localhost:8080/verifyAccount/id=123, it properly returns Welcome to verify account!

Found that the issue was in the way by which the route was getting called. Thanks to Thomas Theiner for the help.
The request with query parameter ?id=123 does not match with /:id. It should be called as verifyAccount/123 instead.
Since, the route ?id=123 did not matched any of the path. Hence, was finally reaching the middleware for execution
The position determines the parameter, not the name. The name is only used inside node code to reference the parameter.
For multiple parameters, we'll have multiple slashes like verifyAccount/:id/:otherParameter which would be called using verifyAccount/123/234

Related

Koa & Passport Missing credentials

I have a Koa server that uses Passport to authenticate users against an Array,
and a React client. After successful login, the following requests are not authenticated as the cookie is undefined. The authenticate function's error parameter has:
{ message: 'Missing credentials' }
After browsing the site, I fixed the usual errors, calling the returned function of authenticate, adding {credentials: 'include'} to fetch etc, but still I have the same problem.
Middleware list:
router.use(cookie.default());
app.use :
koa-body, koa-session-store (also tried with koa-session), passport.initialize(), passport.session(), router.routes(), koa-static
local strategy
passport.use(new Strategy((username,password,callback)=>{
var u = users.find(u=>u.username == username);
return (u && password == 'password')? callback(null, u ):callback('user not found', false);
}));
/login authenticate
.post('/login', (ctx)=>{
console.log(ctx.request.body);
return passport.authenticate('local',(err,user,info,status)=>{
if(user) {
ctx.login(user);
ctx.body = {success: true}; // works correctly
ctx.redirect('/login-success');
} else {
ctx.redirect('/login-failure');
}
})(ctx);
});
/login-success
router.get('/login-success',async(ctx)=> {
return passport.authenticate('local',(err,user,info,status)=>{
console.log(err); // "Missing credentials"
})(ctx);
await ctx.response;
ctx.body = {success: true};
}).
Client call
let body = JSON.stringify({username: this.state.username, password: this.state.password});
let result = await fetch('http://localhost:4200/login',{method:'POST',credentials: 'include',body, headers:{'Content-Type':'application/json'}});
The fix is actually simple, but the reason is hard to find.
async middleware must either call await next() or return next() at the end.
Otherwise a 404 error is caused.
Adding await next() to the async /login-success callback, fixed the issue.
Documentation: https://github.com/koajs/koa/blob/master/docs/troubleshooting.md#my-middleware-is-not-called

Node.JS returns a 404 on interrogation of an endpoint that actually exists

I have a web application, started by a previous company, written in Angular.JS. The application exposes a request towards the back-end (written in Node.JS+Express) to gather some data required to fill a table. Specifically, this is the request that the application sends everytime the user enters in the page that holds the table (The config variable holds the access token).
return $http.get(API + '/api/myPath/for/Having/Data', config).then(handleSuccess, handleError);
handleSuccess and handleError are so defined
handleSuccess: function (res) {
debugger;
var deferred = $q.defer();
res.data.success ? deferred.resolve(res.data) : deferred.reject(res.data.message);
return deferred.promise;
},
handleError: function (error) {
return {
success: false,
message: error
};
}
In my back-end I've put an a listener to whatever gets called with the "/api" prefix, like this
app.use('/api', authorization.validateToken);
And another listener, that should work only if there is no match (written at the very end of the file that handles all the general inquiries of the app)
app.all('*', (req, res) => {
console.log('Hi, Stack Overflow!');
res.send({
success: false,
status: 404,
message: 'Invalid Uri Resource'
});
});
And, lastly, this is the endpoint that should get called in the back-end from Angular.js
app.get('/api/myPath/for/Having/Data', something.somethingToCall);
Here's the funny part: for a reason that I still have to understand, Angular.JS calls that endpoint twice, resulting in one failing procedure (404) and another one that goes smoothly (200).
The operation flow should be like this: Angular calls the back-end --> Node checks the validity of the token --> executes operation if everything goes okay.
The operation is called twice (seen thanks to the Visual Studio Code debugger and Chrome's Network Monitor) and, even though the token's validation process is correctly executed everytime, the first time the next() function will hold the app.all() listener.
Also,even before I start debugging the first request that is sent out, the JavaScript console on Google Chrome warns me that there has been an error such as like "Cannot read property 'data' of undefined", meaning that the request gets executed twice with the first time returning a 404.
exports.validateToken = (req, res, next) => {
console.log(`check user here`);
// next();
var token = //I take the token
console.log(token);
if (token) {
jwt.verify(token, require('../../secret'), (err, decoded) => {
if (err) {
res.send({
success: false,
status: 500,
tokenExpired: true,
message: "Effettua nuovamente l'accesso"
});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.send({
success: false,
status: 406, // Fprbidden
message: 'User not Authenticated'
});
}
};
Does anybody know how to help me somehow?
EDIT: this is an example of how Chrome's sees both requests. The screenshot, in particular, refers to the first one that gets called and produces the 404
The CORS is handled in the back-end like this
app.use(function (req, res, next) {
if (req.headers.origin && (req.headers.origin.match("http:\/\/somewebsite.com.*") || req.headers.origin.match("http:\/\/localhost:8010") )) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
}
next();
});
Also, I'm adding the endpoint that needs to be called. This also exploits MongoDB + Mongoose for querying the DataBase and return stuff to the front-end. The parameters that I'm passing are pageSize (how many elements per page) and the current page number
exports.getAds = (req, res) => {
var criteria = req.body || {};
var pageSize = criteria['pageSize'] ? Number(criteria['pageSize']) : undefined;
var pageNumber = criteria['pageNumber'] ? Number(criteria['pageNumber']) : undefined;
var sort = criteria.sort || { createdAt: 'desc' };
if (criteria.customerName) criteria.customerName = { $regex: `.*${criteria.customerName}.*`, $options: 'i' };
if (criteria.spentEuros) criteria.spentEuros.$gte = criteria.spentEuros;
if (criteria.referralMail) criteria.referralMail = { $regex: `.*${criteria.referralMail}.*`, $options: 'i' };
console.log(criteria);
var columns = "customerName duration spentEuros";
if (pageSize && pageNumber) {
Adv.paginate(criteria, {
page: pageNumber,
limit: pageSize,
select: columns,
sort: sort
}, function (err, result) {
if (!err) res.status(200).send({ success: true, data: result });
else res.status(500).send({ success: false, message: err });
});
} else {
Adv.find(criteria)
.select(columns)
.sort(sort)
.exec(function (err, result) {
if (!err) res.status(200).send({ success: true, data: result });
else res.status(500).send({ success: false, message: err });
});
}
};
EDIT2: Solution to the question: adding an app.options listener in the back-end (as pointed out by #slebetman), alongside with the already existing app.get one, solved the issue
Here's the funny part: for a reason that I still have to understand, Angular.JS calls that endpoint twice...
That sounds a lot like the browser sending a CORS preflight OPTIONS request, followed by a GET. Check the HTTP verb being used, and be sure you're handling OPTIONS (not just GET) if you need to support CORS on your endpoint. (If you're not expecting this to be a cross-origin request, check the origin of the page relative to the origin of the API call, something [protocol, port, domain] seems to be different — if it's an OPTIONS call.)

Sending a POST request with chai sends an empty body?

I have the following setup right now
test.js
var user = {
username: 'test_user',
email: 'test#test.me',
password: 'you shall not pass',
address: 'No where street'
};
chai.request(app)
.post('/api/v1/users')
.send(user);
I'm handling the post request in my routes/user.js
router.post('/', function(req, res, next) {
console.log('body: ' + req.body);
queries.insertUser(req.body)
.then(function(id) {
return queries.getSingleUser(id);
})
.then(function(user) {
res.status(200).json(user);
})
.catch(function(err) {
next(err);
});
});
req.body ends up being undefined. Any clue as to what might be going wrong?
The code is live at https://ide.c9.io/burtonium/node-from-scratch if anybody wants to have a look.
req.body being undefined is generally caused by either not using the body-parser middleware in Express, or declaring it incorrectly (for instance, after the route that wants to access req.body).
Assuming that Chai sends JSON, add this to your Express app:
app.use(require('body-parser').json());
(before you declare the routers)

How to throw a 404 error in express.js?

In app.js, I have
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
so if I request some not exist url like http://localhost/notfound, above code will execute.
In exist url like http://localhost/posts/:postId, I would like to throw 404 error when access some not exist postId or deleted postId.
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
// How to throw a 404 error, so code can jump to above 404 catch?
}
In Express, a 404 isn't classed as an 'error', so to speak - the reasoning behind this is that a 404 isn't usually a sign that's something's gone wrong, it's just that the server couldn't find anything. Your best bet is to explicitly send a 404 in your route handler:
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
res.status(404).send("Not found.");
}
Or alternatively, if this feels like too much repeated code, you could always pull that code out into a function:
function notFound(res) {
res.status(404).send("Not found.");
}
Posts.findOne({_id: req.params.id, deleted: false}).exec()
.then(function(post) {
if(!post) {
notFound(res);
}
I wouldn't recommend using a middleware in this situation solely because I feel like it makes the code less clear - the 404 is the direct result of the database code not finding anything, so it makes sense to have the response in the route handler.
I have the same app.js structure, and I solved this problem in this way in the route handler:
router.get('/something/:postId', function(req, res, next){
// ...
if (!post){
next();
return;
}
res.send('Post exists!'); // display post somehow
});
The next() function will call the next middleware which is the error404 handler if it is right after your routes in the app.js.
You can use this and the end of your routers.
app.use('/', my_router);
....
app.use('/', my_router);
app.use(function(req, res, next) {
res.status(404).render('error/404.html');
});
you're probably looking for something like https://github.com/expressjs/api-error-handler
or just https://github.com/jshttp/http-errors
Even though 404 pages are not considered an error in Express as written here, its really damn handy if you DO handle them like so. For instance when you are developing an API that wants consistent JSON output. The following code should help you with that:
Define a helper function abort to create status errors that can be easily used in your code to pass to the next function:
// Use the `statuses` package which is also a dependency of Express.
const status = require('statuses');
const abort = (code) => {
const err = new Error(status[code]);
const err.status = code;
return err;
};
Define the catch-all middleware for 404 pages which should be defined at the bottom of your stack (after all routes have been added). This forwards the 404 as an error:
app.use((req, res, next) => {
next(abort(404));
});
Lastly, the final error handler will now consistently send all errors in JSON format:
app.use((err, req, res, next) => {
if(!res.headersSent) {
// You can define production mode here so that the stack trace will not be sent.
const isProd = false;
res.status(err.status || 500).json({
error: err.toString(),
...(!isProd && {stack: err.stack.split('\n').map(i => i.trim())}),
});
}
next(err);
});

How to properly handle errors in Express?

I am beginning to work with Express JS and have run into an issue. I can't seem to figure out the proper way to handle errors.
For example, I have a web services API that serves an object called "event". I'd like to return a simple string of "cannot find event" when a user submits an event id that isn't found. Here is how I'm currently structuring my code:
app.get('/event/:id', function(req, res, next) {
if (req.params.id != 1) {
next(new Error('cannot find event ' + req.params.id));
}
req.send('event found!');
});
When I submit an id other than 1, Node crashes with the following output:
http.js:527
throw new Error("Can't set headers after they are sent.");
^
Error: Can't set headers after they are sent.
at ServerResponse.<anonymous> (http.js:527:11)
at ServerResponse.setHeader (/usr/local/kayak/node_modules/express/node_modules/connect/lib/patch.js:62:20)
at /usr/local/kayak/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:72:19
at [object Object].<anonymous> (fs.js:107:5)
at [object Object].emit (events.js:61:17)
at afterRead (fs.js:878:12)
at wrapper (fs.js:245:17)
From what I can tell by using the node.js debugger, execution of the block of code continues after next() is called, meaning that req.send('event found!') tries to run. I don't want this to happen.
The only workaround that I've found is to simply throw a new Error() instead of "next-ing" it, but this results in a default Express HTML error page being generated. I'd like a little more control than that.
I have taken the time to read over the error handling section of the Express documentation, but I couldn't make sense of it.
You'll want to check out Express Error Handling. From there:
app.param('userId', function(req, res, next, id) {
User.get(id, function(err, user) {
if (err) return next(err);
if (!user) return next(new Error('failed to find user'));
req.user = user;
next();
});
});
The sweetspot that you are missing is the return next(...)
That's because you're doing it wrong: you already threw an Error (which will be processed by Express and return a 500 - Error page for the user or something like that) but you are also trying to send your own response to the client: res.send('event found!');
You should really check out the Express guide about Error Handling here: http://expressjs.com/guide/error-handling.html
What I would do in your example is:
function NotFound(msg){
this.name = 'NotFound';
Error.call(this, msg);
Error.captureStackTrace(this, arguments.callee);
}
app.get('/event/:id', function(req, res, next){
if (req.params.id != 1) {
throw new NotFound('Cannot find event ' + req.params.id);
} else {
res.send('event found!');
}
});
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.ejs');
} else {
next(err);
}
});
You have a couple of problems in your code:
When responding to the client, you need to use the response object (res rather than req).
When sending an error to next, you should return, so the rest of the function doesn't run.
Here's your code after fixing those errors:
app.get('/event/:id', function(req, res, next) {
if (req.params.id != 1) {
return next(new Error('cannot find event ' + req.params.id));
}
res.send('event found!'); // use res.send (NOT req.send)
});

Categories