I ran into issue where if i don't explicitly send the response back, my Express application is sending 'Cannot call /POST' message back to the browser. I am using MongoDB as backend and express as routing framework. Please refer the last (commented) in section 'Router code'.
User model code:
UserSchema.pre("save", function(done){
var user = this;
console.log("Processing before saving the user.")
if(!user.isModified("password")){
//if password isn't changed, then don't do any processing on pwd to re-hash it.
done();
}
//progress function - optional parameter for bcrypt's hash func.
var progressFunction = function(){} ;
bcrypt.genSalt(SALT_FACTOR, function(err, salt){
if(err){return done(err);}
//console.log("Computing hash for pwd.");
bcrypt.hash(user.password, salt, progressFunction, function(err, hashedPassword){
if(err){return done(err);}
user.password = hashedPassword;
//console.log("user-built logic done. invoking next middleware.");
done();
//console.log("next middleware exec done");
});
});
});
Router code:
var User = require("../models/user");
var router = express.Router();
router.post("/signup", function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
User.findOne({username : username}, function(err, existingUser){
if(err){return next(err);}
if(existingUser){
req.flash("error", "User with this username exist already. Try different username.");
return res.redirect("/signup");
}
//create and save a newly signedUp user.
var newUser = new User({
username : username,
password : password
});
newUser.save(next); //call model's processing save
//res.send(200); //if i dont send this response back, i get the error mentioned above.
});
});
My router is the last middleware on the stack before starting the app server.
//mount routes
app.use(routes);
app.listen(app.get("port"), function(){
console.log("Server started on port : " + app.get("port"));
});
It would be great if someone could explain the reasoning behind this behaviour.
To be a properly functioning server, you need to somewhere send a response to any of the incoming HTTP requests. This is because the client sends a request and then waits for a response. Getting a response back is the only way that it knows that the request was successfully received and understood.
Express does not know anything about what your middleware has or has not done so it simply doesn't know what the appropriate response would be to send as a default response. You may have handled it or you may have not. And, in fact, many express servers are manually configured to default to a 404 if no other handler takes the response. And, that is done by simply making the last handler in the chain send a 404. If the request got all the way through to the last handler, then no other handler must have handled the request so it is to be considered unhandled.
Though that is probably not what you want in this case (because you have handled the request), you can add a default 404 handler by adding this middleware as the last request handler in your app:
app.use(function (req, res, next) {
res.status(404).send('Sorry cant find that!')
});
You could change that to default to a 200 status, but then you'd be returning 200 for routes that don't exist and that is not a desirable way to configure your server.
As to why it's done this way, that's simply because there is no way for Express or the HTTP server itself to know what the appropriate response should be. Your code that knows what the appropriate response is has to send it.