I am trying to shorten my Express/Connect middleware pipeline by only calling certain middleware functions based on the requested path.
However, the following will fail:
_cookieParser(req, res, function(err) {
if(err) return next(err);
_session(req, res, function(err) {
if(err) return next(err);
_csrf(req, res, function(err) {
if(err) return next(err);
loadUserFromSession(req, res, function(err) {
if(err) return next(err);
if(req.method == "POST") {
_bodyParser(req, res, next);
} else {
next();
}
});
});
});
});
But this will work fine:
_cookieParser(req, res, function(err) {
if(err) return next(err);
_session(req, res, function(err) {
if(err) return next(err);
_csrf(req, res, function(err) {
if(err) return next(err);
_bodyParser(req, res, function(err) {
if(err) return next(err);
loadUserFromSession(req, res, next);
});
});
});
});
Where loadUserFromSession is:
function loadUserFromSession(req, res, next) {
if(req.session && req.session.userId) {
userFunctions.getUserById(req.session.userId, function(err, user) {
if(err) return next(err);
if(user) {
req.user = user;
return next();
} else {
req.session.destroy();
return next(new Error('Unauthenticated'));
}
});
} else {
return next(new Error('Unauthenticated'));
}
};
Why can I not call bodyParser() after loadUserFromSession()?
EDIT
Sorry for the lack of detail on the failure/unexpected outcome.
If I put bodyParser() or just json() (since the POST content is json) after loadUserFromSession(), the calls never return inside of json(). If I put breakpoints in node inspector on either res.on('data') or res.on('end') neither get tripped.
The source of the json middleware is below:
exports = module.exports = function(options){
var options = options || {}
, strict = options.strict !== false;
var limit = options.limit
? _limit(options.limit)
: noop;
return function json(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!utils.hasBody(req)) return next();
// check Content-Type
if ('application/json' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
limit(req, res, function(err){
if (err) return next(err);
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){
buf += chunk <==BREAKPOINT NEVER GETS CALLED
});
req.on('end', function(){
var first = buf.trim()[0]; <==BREAKPOINT NEVER GETS CALLED
if (0 == buf.length) {
return next(400, 'invalid json, empty body');
}
if (strict && '{' != first && '[' != first) return next(400, 'invalid json');
try {
req.body = JSON.parse(buf, options.reviver);
next();
} catch (err){
err.body = buf;
err.status = 400;
next(err);
}
});
});
}
};
OK, consider this advice/code review as opposed to a specific answer, but hopefully this helps.
First, my guess is understanding the following will solve your problem and unconfuse you, although I can't say exactly why your second example above behaves differently from the first example given your isolated snippets, but do some experiments base on this information: For a given request, the series of events (data, end, etc) will fire once and only once. If a listener is not attached when they fire, that listener never gets called. If a listener gets attached after they fire, it will never get called. The bodyParser has code to avoid attempting to re-parse the same request body more than once (because the necessary events would never fire and the code would hang without responding to the request).
SO, I suspect that in your app you have app.use(express.bodyParser()) and that middleware is getting called and running prior to your custom stuff above. Thus, my suggestion, which also addresses your file upload security question is:
Don't install the bodyParser globally like you see in the examples: app.use(express.bodyParser()). This code is neat for day-1 example apps, but it's totally inappropriate for a production site. You should instead do something like:
app.post('/upload/some/file', express.bodyParser(), myUploadHandler);
And just use the body parser exactly where it's needed and nowhere else. Don't go further down your stack of bizarre nesty middleware. There are clean ways to get the functionality, security, and efficiency you need that work in HARMONY with the middleware stack design built into connect. In general, understand that you can use different middleware stacks on different paths and express/connect can be configured to nicely match what you want to happen on a path-by-path basis.
Related
Login routes:
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({
mobileNo: req.body.mobileNo,
});
if (!user) {
res.status(401).json("You are not registerd");
}
const password = res.body.password;
if (password === user.password) {
return res.status(200).json("You are logged in");
} else {
return res.status(501).json("Naah! wrong pass");
}
} catch {
(err) => {
res.status(500).json(err);
};
}
});
module.exports = router;
index.js:
app.use("/api/auth", authRoute);
import:
const authRoute = require("./routes/auth");
My postman image, I am not getting any result.
Your try-catch syntax is wrong, correct would be
try {
...
} catch(err) {
res.status(500).json(err);
}
With your syntax, when the catch block is reached, the res.status(500).json(err) statement is not executed, therefore the request never comes back.
In your try block, there are 3 responses available. If the first condition in the if block is also executed with another one of the responses in the below if-else condition this problem may occur. Because at a time, sending 2 responses is impossible. Therefore, you should return that response and terminate.
if (!user) {
return res.status(401).json("You are not registered");
}
Sending a logout request to my server but I'm never getting a reply. The logout function is being called and the userID key is being deleted from my redis cache but I never get a response. Here's my code.
export const logout = async (req, res) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
return res.status(500);
} else {
return res.status(200);
}
});
};
Because of callback, you should use promise
export const logout = async (req, res) => {
return new Promise((resolve,reject) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
reject(res.status(500));
} else {
resolve(res.status(200));
}
});
});
}
res.status() does not send a response from the server. All it does is set the status as a property on the response object that will go with some future call that actually sends the response.
It is meant to be used in something like this:
res.status(500).send("Database error");
If you look at the Express doc for res.status(), you will see these examples:
res.status(403).end()
res.status(400).send('Bad Request')
res.status(404).sendFile('/absolute/path/to/404.png')
And, see that they all are followed by some other method that actually causes the response to be sent.
And, if you still had any doubt, you can look in the Express code repository and see this:
res.status = function status(code) {
this.statusCode = code;
return this;
};
Which shows that it's just setting a property on the response object and not actually sending the response yet.
You can use res.sendStatus() instead which will BOTH set the status and send the response:
export const logout = (req, res) => {
console.log("logout called");
const { userID } = req.user;
client.del(userID.toString, (err, reply) => {
console.log("inside client.del");
if (err) {
res.sendStatus(500);
} else {
res.sendStatus(200);
}
});
};
Note, I removed the two return keywords since they don't accomplish anything useful in this particular context.
I also removed the async keyword from the function definition since it was not doing anything useful in this context.
I have two User models (A and B) and two accesstoken models, the original and a custom (base: "AccessToken"),
I managed to make that the tokens of each User model be saved in each table (mysql).
But the problem in this moment is that loopback validates the accesstoken only in a model (table), and I want it validates in the two models (tables).
I am trying to do the first validation by myself before loopback validates in the default model (table), but I dont know how to skip access token validation automatic if I find that the accesstoken is correct in my first validation.
Any idea about it?
server/server.js
app.use(function(req, res, next) {
const CustomAccessToken = app.models.CustomAccessToken;
CustomAccessToken.resolve(req.headers.authorization, function(err, token){
if(err){
console.log(err);
}else{
console.log(token, "Correct!");
// Skip default accesstoken valitation
}
return next();
});
});
I already solved it.
app.use(function(req, res, next) {
const adminaccesstoken = app.models.adminaccesstoken;
var currentToken = req.headers.authorization;
if (typeof currentToken != 'undefined') {
adminaccesstoken.resolve(currentToken, function(err, cToken){
if(err){ return next(err); }
if (typeof cToken != 'undefined') {
req.accessToken = cToken;
}
return next();
});
} else { return next(); }
});
async1.each(arr, function(arrayMember) {
orders.where('name', arrayMember).fetch({withRelated: ['allOrders']}).
then(function(dd2, callback) {
dd2 = dd2.toJSON();
var sendMemberOrder = {};
sendMemberOrder.name = dd2.name;
sendMemberOrder.lastOrder = dd2.allOrders.length;
res.send(sendMemberOrder);
});
}, function(err) {
if (err) {
console.log("err");
}
});
I'm trying to use Express's res.send() feature but given that I'm using async.each, I'm getting
headers already sent
error.
How can I pass the result of each iteration as an array when a request is being made?
Since you already use promises here, I would like to doscourage you from using async.js here. Your code is broken anyway as it does not call callback at all, and the callback parameter is declared on the wrong function. Instead you could try this:
app.get(your_route, function(req, res, next) {
// obtain arr
Promise.all(arr.map(function(arrayMember) {
return orders.where('name', arrayMember)
.fetch({withRelated: ['allOrders']})
.then(function(dd2) {
dd2 = dd2.toJSON();
return {
name: dd2.name,
lastOrder: dd2.allOrders.length
};
});
})).then(function(resultData) {
res.send(resultData);
}).catch(function(err) {
console.log(err);
next(err);
});
});
I have been fiddling with this for days, and I cannot figure out why the Mongoose middleware is not being invoked.
So I have an API in node.js and I have a website using Angular.js. The Mongoose middleware is this:
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
So this hook is called perfectly fine when invoked from the Angular front end. However, when I run a test with supertest, chai, and mocha the hook is not invoked. Here is my code for the testing:
it('/doctors - POST - (create doctor)', function(done){
request(app)
.post('/doctors')
.send(doctor)
.end(function (err, res){
if (res.body['error']) {
expect(S(res.body['error']).startsWith('doctor already exists')).to.be.true;
}
else
expect(res.body['email']).to.equal(doctor['email']);
done();
});
});
....
it('/doctors/remove - DELETE', function(done){
request(app)
.del('/doctors/remove')
.auth(new_doctor_creds["email"], new_doctor_creds["pass"])
.end(function (err, res){
expect(Object.keys(res.body).length).to.not.equal(0);
done();
});
});
And here is my route for the express app:
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email:email}).remove(function (err, removed) {
if (err) return next(err);
return res.status(200).send(removed);
});
});
Again, this Mongoose middleware works perfectly fine when invoked from an API call from the Angular app. However, it does not work when tested with supertest. Any ideas on what to do here?
EDIT: I tried to recreate this example with a simplified version that way you can see all of the code. So here is a two file version that is STILL not working. Here is the app.js:
var mongoose = require('mongoose');
var app = require('express')();
var http = require('http');
var fs = require('fs');
var Doctors = require('./schema');
mongoose.connect('mongodb://localhost/m4', function(err) {
if (err) throw err;
console.log('connected');
app.get('/post', function (req, res, next) {
console.log('create');
Doctors.create({email:"hello"}, function (err, inserted) {
if (err) console.log(err);
res.end();
});
});
app.get('/delete', function (req, res, next) {
console.log('removed');
Doctors.remove({email:"hello"}, function (err, removed) {
if (err) console.log(err);
res.end();
});
});
http.createServer(app).listen('6000', function () {
console.log('now listen on localhost:6000');
});
});
and the schema:
var mongoose = require('mongoose');
var schema = mongoose.Schema({
email: { type: String }
});
schema.pre('save', function (next) {
console.log('doctors - post - save');
next();
});
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
module.exports = mongoose.model('Doctors', schema);
Here's what I suggest. Let's perform the #remove on the doc found by #findOne. If I remember correctly, remove post hooks only works on Doc#remove and not on Model#remove.
schema.post('remove', function (doc) {
console.log('doctors - post - remove'); // <-- now runs
});
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email: email}, function(err, doc) {
if (err) {
return next(err);
}
doc.remove().then(function(removed) {
return res.status(200).send(removed);
}, function(err) {
next(err);
});
});
});
Mongoose post hooks run AFTER the operation is completed, concurrently with operation callbacks. See the comments below:
Doctors.findOne({email:email}).remove(function (err, removed) {
// All this code and the post hook are executed at the same time
if (err) return next(err);
// Here you send the response so supertest#end() will be triggered
// It's not guaranteed that post remove was executed completely at this point
return res.status(200).send(removed);
});
Post hooks were made to run processes independent of the server response. When you run tests, the server shuts down right after the tests are completed, and maybe it had no time enough to finish the post hooks. In the other hand, when you call the API from a client, normally you keep the server running, so the post jobs can be completed.
Now, there comes a problem: how can we test post hooks consistently? I got up this question because I was looking for a solution to that. If you already have an answer, please post here.