Node redirect after saving to db - node.js

Im doing a node app, which has a html form doing an action to /users
This url calls this method on post
exports.create = function(req, res, next) {
const user = new User(req.body);
user.save((err) => {
if (err) {
return next(err);
} else {
res.status(302).json(user).redirect('/chat');
}
});
};
However i'm unable to do a redirect after storing the data in my db, and get the error Can't set headers after they are sent.
I've tried placing the redirect different places in the form, but i keep getting the same error.

Try removing .json(user) from the redirect. You cannot both send a JSON and a redirect at the same time.
Or if you want to send the JSON, maybe as a response to an Ajax request, don't send .status(302) but do the redirect on client side JavaScript.

Related

Express JS - Redirecting If MongoDB id doesn't exist / Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Within my Posts routes, i'm making it so that the user can go to posts/:id to retrieve information about the post with that ID, however i'm trying to make sure that if the user goes to a post that doesn't exist, he gets redirected back to the posts index route. My issue is that the server is going through the entire router.get function instead of redirecting the user and stopping.
router.get("/:id", async (req, res) => {
if (!mongoose.isValidObjectId(req.params.id)) {
console.log("Test1")
res.redirect("/")
res.end()
}
console.log("Test2")
try {
console.log("Test3")
const post = await Post.findById(req.params.id)
if (post == null) res.redirect("/")
res.render("posts/show", { post: post })
}
catch {
console.log("Test4")
res.redirect("posts/index")
res.end()
}
})
If I go to the route posts/weiqeiqwie (which isn't valid) it prints all of the test console logs. What can I do for the code to return after the error?
I'm still a bit new to using express, so sorry for the dumb question.
you cant redirect AND render at the same time.
this is what this part of the code does when post is null :
if (post == null) res.redirect("/")
res.render("posts/show", { post: post })
adapt it like this, so that now when post is null it only does the redirect part.
(never forget the return)
if (post == null) return res.redirect("/");
res.render("posts/show", { post: post })
the same goes for the redirect line 3 :
it should be :
if (!mongoose.isValidObjectId(req.params.id)) {
console.log("Test1")
return res.redirect("/")
}
also res.end() is not required. you can remove it from everywhere.

Fetch/React not redirecting with Node/Express res.redirect()

On my React front-end, I make a fetch() GET call to my Node/Express server. The Node/Express server checks if response has a certain cookie, and if not should redirect it. My code is something like this:
React code
componentDidMount(){
fetch('/example',{redirect: "follow"})
.then(data => data.json())
.then(res => {
//do something with response
}
})
}
Node/Express:
router.use((req, res, next) => {
let { token } = req.cookies;
if(token){
next();
}
else{
res.redirect(303, '/login');
}
});
router.get('/example', (req, res) =>{
//sends a json response back
})
It might be important to note that I am using react-router-dom as a browser router, thought I'm not sure what this does to server redirects. I've searched and saw people who said to set redirect to "follow". However, even when setting status to a 3xx and setting redirect to "follow", the page still doesn't redirect properly. Instead, it receives a response with 200 OK status(which I don't understand how it could have changed, unless Node/Express didn't actually set it to '303'), redirected: true, and a response.url pointing to the redirect url, but still stays on the same page. I've made sure that the cookie isn't there, so that's not it. I know I can probably check response.redirected every time and manually set window.location = response.url, but I still don't understand why the redirect did not work. Do I have to set it manually everytime then?

Redirecting to route from Node.JS server doesn't render the next page

Hi I have a react component that renders a form like this:
<form onSubmit={this.onSubmit}>
<!-- a bunch of inputs here -->
</form>
where the function onSubmit() makes a post request to /results using axios:
handleSubmit(e) {
var self = this;
e.preventDefault();
const {value1, value 2, ....} = this.state;
axios.post('/results', {
key1 : value1,
key2 : value2,
etc.
}).then(function(response) {
if (errors) {
self.setState({errors: response.data.errorMessage});
}
}).catch(function(error){
console.log(error);
});
}
I have a route handler for the post request in my server.js which inserts the data from the form into a database. If there are errors then it'll send that data back to the client, otherwise it should redirect to the results page. The handler looks like this:
app.post('/results', function(req, res, next) {
const reportExists = Report.findOne({
attributes: ['caseId'],
where: {caseId : req.body.caseId},
}).then(report => {
if (report) {
console.log("report already exists");
res.status(200).send({errorMessage : "Report has been submitted for this case id"});
} else {
const report = Report.create(
{
// data from form
}
).then(() => {
console.log('Record inserted successfully');
var caseId = req.body.caseId;
res.redirect("/results/" + caseId);
next();
})
.catch(err => {
console.log('failed to insert record');
res.status(200).send({errorMessage: "Failed to insert record"});
});
}
});
});
I have another app.get('/results/:caseId') handler which should render the appropriate route for the results page. But when the record is inserted successfully it doesn't redirect to that page, it stays on the same page as the form. My question is, should I be redirecting to that page from the client or the server?
Submitting an Ajax call via client-side Javascript just gets the response back from the server, whatever it is. A redirect is not automatically processed by the browser, it's just the ajax response to your javascript. It is up to your client-side Javascript to decide what to do with the redirect response.
You have a couple choices. You could detect the redirect response in your client-side Javascript and then set window.location with the new location and manually tell the browser to go to the new page. Or, you could let the browser submit the form rather than your client-side Javascript and then the browser will follow the redirect response automatically.
Also, you should not be calling next() after you call res.redirect(). Once you've sent the response, you should not enable other route handlers to execute.

Nodejs ejs always asking for view when trying to redirect a url and receive its json data

I am trying to make a slack app and to complete Oauth2, I have to send the URI below and get a JSON response back in the body.
The problem is, every time I am trying to use the function request() in my app.get() function, ejs is always trying to go and get my views. Now I tried rendering my specific view for app.get() but then when I use request() again, ejs is again trying to get a view.
How can I redirect to another url from my app.get and receive the JSON. I can use req.redirect() but I don't know how to get the response back.
Please please help! Thanks
app.get('/', (req, res) =>{
var options = {
uri: 'https://slack.com/api/oauth.access code='+req.query.code+'&client_id='+client_id+'&client_secret='+client_secret,
method: 'GET'
}
request(options, (error, response, body) => {
var JSONresponse = JSON.parse(body)
if (!JSONresponse.ok){
console.log(JSONresponse)
res.send("Error encountered: \n"+JSON.stringify(JSONresponse)).status(200).end()
}else{
console.log(JSONresponse)
res.send("Success!")
}
})
})

Express redirecting doesn't change req.url

I have a route that redirects upon successful login
app.post('/login', function(req, res){
if(req.body.password === Server.cfg.auth.userPass) {
req.session.user = {nick: req.body.username, pass: req.body.password}
res.redirect('/chat')
} else {
res.render('user/login', { locals: { error: 'Invalid password' } })
}
})
The redirect seems to work as the page is refreshed with the correctly rendered jade file. However, the url still says /login and my pageTitle variable (being set through template vars) does not change either. If I refresh the page after the redirect, everything changes to the way it should be. It is only after the redirect that it does not change.
This has got to be a pretty common mix up for folks trying to deal with ajax redirects coming from a server controlled development background. My example shows what happens if authorization fails, slightly different; but you can use the same concept of intercepting the response and checking status, etc., and let the client JavaScript do the redirect.
My client code is actually a backbone model but in turn is calling jquery's ajax like:
model.save({ error:function...
Server
function requiresLogin(req, res, next) {
if(req.session.user) {
next();
} else {
//res.redirect('/sessions/new?redir=' + req.url); // won't work
res.send("Unauthorized", 403); // send 403 http status
}
}
Client
// Assumes you can get http status from response
error: function(resp, status, xhr)...
if(resp.status === 403) {
window.location = '/sessions/new'; // optionally put a redirLastPage=somewhere
}
This works as desired for me. I'd also suggest googling ajax post redirects to see why this
Looks like this is a jQuery problem. At least it was for me. You can override it with rel=external. More info at http://jquerymobile.com/demos/1.1.0/docs/pages/page-navmodel.html.

Resources