Error : Cannot set headers after they are sent to the client - node.js

When I hit the signup route "req.body" doesn't pick any up any of the POST values, however whenever the same code is tested on Postman - with body raw method - the values display.
const router = require('express').Router();
const bodyParser = require('body-parser');
const Promise = require('bluebird');
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({
extended: true
}));
const registration = require('./services/registration');
router.get('/', (req, res, next) => {
res.send('Admin Welcome');
});
router.get('/signup', (req, res, next) => {
res.render('user/signup');
});
router.post('/signup', (req, res, next) => {
res.send(req.body);
registration.registration(req.body);
.then(ok=>{
res.redirect('signin')
})
.catch(err => {
res.render('error', {message: err})
})
})
router.get('/signin', (req, res, next) => {
res.render('user/signin');
});
original code

router.post("/signup", (req, res, next) => {
res.send(req.body);
registration
.registration(req.body)
.then(ok => {
res.redirect("signin");
})
.catch(err => {
res.render("error", { message: err });
});
});
The res object represents the HTTP response that an Express app sends when it gets an HTTP request. In the following link you can see all the methods that are exposed for res object:
https://expressjs.com/en/api.html#res
Method that you are using at the beginning of you route handler is:
res.send([body])
And as it can be read from the documentation it sends the HTTP response. Now you can send that response only once, otherwise you will get an error:
Error : Cannot set headers after they are sent to the client
And what you are trying to do in the handler is to redirect result to "signin" page afterwards already sending the response with res.send(req.body).
Take this fake route for an example:
router.post("/example", (req, res, next) => {
res.send('1');
res.send('2');
res.send('3');
});
Contrary to what you might believe, it wont return values (1,2,3), but actually return value 1 and raise and error that was previously described.
Finally to solve your issue you need to remove line containing res.send(req.body) and double check if registration.registration service is correctly handling provided data, in this case req.body.

Related

How can I pass param from the controller to pre-save middleware?

My function in the controller:
getWeather: (req, res) => {
const userId = req.params.userId;
weather.save().then(() => {
console.log('weather saved')
}).catch(error => { return res.status(500).json({ error }) })
}
The middleware in the model, here I want to get the userId as a param
weatherSchema.pre('save', function (req, res, next) {
console.log( req + ' pre!! '); //req
next();
})
I don't succeed, I tried to look for similar questions but their answers did not help me. What can I try next?
I guess you're confused between express middleware and mongoose middleware. The save mongoose middleware that you are using is a document middleware and it only gets a single parameter i.e. next and it is triggered before .save().
I guess an express middleware would solve your problem.
app.get("/someroute", (req, res, next) => {
// Here you have access to req.params.userId
next();
}, (req, res) => {
// Here you can save it to the db
})

Express trigger not found route for each route

I am using express with a pattern like this :
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
console.log("Error occure");
res.send("<h1>Error</h1>");
});
app.listen(3000);
When I request '/foo' I would like to have
> my middleware before
> My route
> my middleware after
<h1>Hello</h1>
And when I request anything else :
> page not found
> Error occure
<h1>Error</h1>
But the page not found route is executed in each case, even if route '/foo' is done.
How can I get it working ?
When I run your code, I do not get the output you show, so something about your real code is apparently different than what you show in your question.
I do get a slightly confusing output and that happens because the browser sends both the /foo request and a /favicon.ico request. When I run it, the /foo request generates the desired output. The /favicon.ico request generates some middleware output and then gets stuck in the router.
If you filter out the /favicon.ico route (so that it doesn't confuse things) by adding this as the first route:
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
});
Then, I get exactly this output in the server logs when I request /foo:
my middleware before
My route
my middleware after
Which is exactly what you asked for.
There is, however, a general problem with this:
router.use((req, res, next) => {
console.log("my middleware after");
});
Because it will catch and hang any legit requests that haven't yet had a response sent. You can't really code it that way unless you only don't call next() if a response has already been sent.
As a bit of a hack, you could do this:
router.use((req, res, next) => {
console.log("my middleware after");
// if response hasn't yet been sent, continue routing
if (!res.headersSent) {
next();
}
});
But, there is probably a better way to solve whatever problem you're actually trying to solve. If, in the future, you describe your real problem rather than a problem you have with your solution, then you allow people to offer a wider range of solutions to your real problem including things you haven't even thought of to try. As your question is written right now, we're stuck down the solution path you followed and don't know what the original problem was. That is, by the way, referred to as an XY Problem.
Do this
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
// use locals to record the fact we have a match
res.locals.hasMatch = true
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
// check locals to see if we have a match
if (!res.locals.hasMatch) {
console.log("Error occure");
res.send("<h1>Error</h1>");
}
});
app.listen(3000);
You can utilize middlewares and even nest them.
You can implement it like this:
Middlewares
const before = (req, res, next) => {
console.log("my middleware before");
next(); // Supply next() so that it will proceed to the next call,
// in our case, since this is supplied inside the router /foo, after this runs, it will proceed to the next middleware
};
const after = (req, res, next) => {
console.log("my middleware after");
};
Route
// Supply "before" middleware on 2nd argument to run it first when this route is called
router.get('/foo', before, (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>");
next(); // Call next() to proceed to the next middleware, or in "after" middleware
}, after); // Supply the "after" middleware
Once ran, it will proceed with this desired result sequence:
> my middleware before
> My route
> my middleware after
Unmatched Routes Handler
Instead of this
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
You can implement it like this instead, this is after your app.use("/", router); -- This will handle your unmatched routes:
Sources:
https://stackoverflow.com/a/44540743/6891406
https://stackoverflow.com/a/16637812/6891406
app.use((req, res, next) => {
console.log("page not found");
res.json({ error: 'Page not Found' })
});

Cannot set headers after they are sent to the client using a middleware in nodejs

I'm getting this error
Cannot set headers after they are sent to the client
when I checkif an user is authenticated with a middleware
var isAuthenticated = function (req, res, next) {
if(!req.isAuthenticated()) res.redirect(301,'/login');
next();
};
module.exports = isAuthenticated;
and this is the route function
router.get('/', isAuthenticated,(req, res) => {
res.render('main/index.twig');
})
Try updating your if condition to return. Otherwise it could be trying to call redirect and then next.
if(!req.isAuthenticated()) return res.redirect(301,'/login');

How to catch error of specific router request before it ends up in global error handler

So my routes are ordered like shown bellow
verifyToken middleware is being used in a lot of routes.
Generally if an error occurs i want the global error handler of index.js to handle it.
But if the error occurred while verifyToken middleware is being used by the login.html route with method = get i would like
to handle it inside routers/user.js which i thought i could do by using router.get(/\/login(\.html)?$/, (error, req, res, next) => {} but the error bypasses it and moves to global error handler.
index.js
const userRouter = require('./routers/user')
app.get('', (req, res) => {
res.render('index')
})
app.use(userRouter)
app.use((req, res, next) => {
res.status(404).redirect('/404.html');
})
//Global error handling
app.use( (error, req, res, next) => {
switch(error.name) {
case "UnauthorizedError":
console.log("UnauthorizedError = ", error.message)
res.status(401).redirect('/401.html');
break
case "InternalServerError":
console.log("InternalServerError = ", error.message)
res.status(500).send('whatever')
break
default:
console.log("Another error = ", error)
}
})
/routers/user.js
const verifyToken = require('../middleware/authentication/verifyToken')
router.get(/\/login(\.html)?$/, verifyToken, (req, res) => {
// If he is already logged in redirect him to dashboard
// This route works as expected
res.redirect('/admin/dashboard.html')
});
router.get(/\/login(\.html)?$/, (error, req, res, next) => {
// If error = Unauthorized
// which means that he is not logged in proceed
if(error.name === 'UnauthorizedError') res.render('login')
// else pass error to global error handler (at index.js)
else next(error)
});
module.exports = router
/middleware/authentication/verifyToken.js
const jwt = require('jsonwebtoken')
var createError = require('http-errors')
const verifyToken = async (req, res, next) => {
try {
// Do some stuff
if (token_doesnt_exist) return next(createError(401, 'TOKEN NOT FOUND', {expose: false}))
// Do some stuff
next()
} catch {
next(createError(e.status, e.message, {expose: false}))
}
})
module.exports = verifyToken
UPDATE
I ended up transforming
router.get(/\/login(\.html)?$/, (error, req, res, next) => {}
to
router.use((error, req, res, next) => {}
which i guess works since it only catches errors from the above route.
I'm not sure if this is the best way i'd really like to see an alternative.
const verifyToken = require('../middleware/authentication/verifyToken')
router.get(/\/login(\.html)?$/, verifyToken, (req, res) => {
// If he is already logged in redirect him to dashboard
// This route works as expected
res.redirect('/admin/dashboard.html')
});
router.use((error, req, res, next) => {
// If error = Unauthorized
// which means that he is not logged in proceed
if(error.name === 'UnauthorizedError') res.render('login')
// else pass error to global error handler (at index.js)
else next(error)
});
module.exports = router
Since you want to catch errors ONLY in that route you can catch the error in the middleware itself:
const verifyToken = async (req, res, next) => {
try {
//middleware logic
} catch(err) {
//error handling logic
}
Maybe not SUPER elegant but it works..

How to set Content-Type header in node.js

I am executing the following code in node.js. The code runs fine, but the tutorial tells us that :
Now go back and add the Content-Type header with a value of application/json and run the request again. You will get the “You sent
JSON” message back from the server.
1) I am not able to understand how can I set headers for this program!
2) Also If I am running the program without setting headers, then the message 'Server requires application/json' should be displayed. I am not seeing it being displayed anywhere. Where should it be displayed?
const express = require('express');
const app = express();
const requireJsonContent = () => {
return (req, res, next) => {
if (req.headers['content-type'] !== 'application/json') {
res.status(400).send('Server requires application/json')
} else {
next()
}
}
}
app.get('/', (req, res, next) => {
res.send('Welcome Home');
});
app.post('/', requireJsonContent(), (req, res, next) => {
res.send('You sent JSON');
})
app.listen(3000);
What I see In Your code is that, the function requireJsonContent that you defined Does not have parameters. So, you should add (req, res, next) as params to your function. Besides, inside it, you return a function without execution. However, I think You don't need that function, and your code should be like this:
app.post('/', (req, res) => {
if (req.headers['content-type'] !== 'application/json') {
res.status(400).send('Server requires application/json')
} else {
res.send('You sent JSON');
}
})
With express 4.x, you can either use res.set() or res.append(). Read differences between both methods here.

Resources