ExpressJS : res.redirect() not working as expected? - node.js

I've been struggling for 2 days on this one, googled and stackoverflowed all I could, but I can't work it out.
I'm building a simple node app (+Express + Mongoose) with a login page that redirects to the home page. Here's my server JS code :
app
.get('/', (req, res) => {
console.log("Here we are : root");
return res.sendfile(__dirname + '/index.html');
})
.get('/login', (req, res) => {
console.log("Here we are : '/login'");
return res.sendfile(__dirname + '/login.html');
})
.post('/credentials', (req, res) => {
console.log("Here we are : '/credentials'");
// Some Mongoose / DB validations
return res.redirect('/');
});
The login page makes a POST request to /credentials, where posted data is verified. This works. I can see "Here we are : '/credentials'" in the Node console.
Then comes the issue : the res.redirect doesn't work properly. I know that it does reach the '/' route, because :
I can see "Here we are : root" in the Node console
The index.html page is being sent back to the browser as a reponse, but not displayed in the window.
Chrome inspector shows the POST request response, I CAN see the HTML code being sent to the browser in the inspector, but the URL remains /login and the login page is still being displayed on screen.
(Edit) The redirection is in Mongoose's callback function, it's not synchronous (as NodeJS should be). I have just removed Mongoose validation stuff for clarity.
I have tried adding res.end(), doesn't work
I have tried
req.method = 'get';
res.redirect('/');
and
res.writeHead(302, {location: '/'});
res.end();
Doesn't work
What am I doing wrong? How can I actually leave the '/login' page, redirect the browser to '/' and display the HTML code that it received?
Thanks a million for your help in advance :)

The problem might not lie with the backend, but with the frontend. If you are using AJAX to send the POST request, it is specifically designed to not change your url.
Use window.location.href after AJAX's request has completed (in the .done()) to update the URL with the desired path, or use JQuery: $('body').replaceWith(data) when you receive the HTML back from the request.

If you are using an asynchronous request to backend and then redirecting in backend, it will redirect in backend (i.e. it will create a new get request to that URL), but won't change the URL in front end.
To make it work you need to:
use window.location.href = "/url"
change your async request (in front end) to simple anchor tag (<a></a>)

It's almost certain that you are making an async call to check Mongoose but you haven't structured the code so that the redirect only happens after the async call returns a result.
In javascript, the POST would look like something this:
function validateCredentials(user, callback){
// takes whatever you need to validate the visitor as `user`
// uses the `callback` when the results return from Mongoose
}
app.post('/credentials', function(req, res){
console.log("Here was are: '/credentials'";
validateCredentials(userdata, function(err, data){
if (err) {
// handle error and redirect to credentials,
// display an error page, or whatever you want to do here...
}
// if no error, redirect
res.redirect('/');
};
};
You can also see questions like Async call in node.js vs. mongoose for parallel/related problems...

I've been working on implementing nodemailer into my NextJS app with Express. Was having this issue and came across this. I had event.preventDefault() in my function that was firing the form to submit and that was preventing the redirect as well, I took it off and it was redirecting accordingly.

Add the following in your get / route :
res.setHeader("Content-Type", "text/html")
Your browser will render file instead of downloading it

Related

NodeJs - res.redirect changes the content displayed but not the URL

i have a react native frontend and a nodejs backend. In one of my API calls i am getting a redirectHTML from a gateway to display to the users. The redirectHTML obtained is used in react native Webview to get displayed. my problem is the only way to know that the transaction is success or not is from the url. I have tried res.redirect and res.writeHead and both change the content of the screen but the URL still remains the same.
Server side
router.get(
"/pay/authenticate/result",
async(req, res) => {
console.log(req.query)
// res.redirect(302,"/");
res.writeHead(302,{'Location':'https://www.google.com/'});
res.end("");
});
client Side
<WebView
style={{flex:1}}
source={{html:`${authenticateWebViewUrl}`}} //this is the redirectHTML that came from the response
onNavigationStateChange={(navState) => {
console.log(navState)
}}
scalesPageToFit={false}
javaScriptEnabled={true}
/>
res.redirect with 302 will add the temporary redirection, if you want a permanent redirection, prefer HTTP 301. Reference
res.writeHead + res.end does not, on its own, cause the redirection to a new URL.
Edit: Typo req and res.

Access URL query Params in an Express POST route

I have a NodeJS/Express application.
From an url endpoint titled: localhost:3000/form?someProp=someValue&somethingElse=someOtherValue
This page submits a form to another Express endpoint.
I submit to an Express POST endpoint. I know in a GET endpoint I could access the query params via req.query, but I cannot seem to do that in a POST request.
Is there a way to access the query params from the request in a POST route?
(other than splicing the header.referrer... which I may just have to do)
Here are some code snippets:
If I submit to the first route it works, if I submit to the second... it does not.
router.get('/test',
(req, res) => {
console.log(req.query); // {someProp: someValue, somethingElse: someOtherValue }
}
);
router.post('/test2',
(req, res) => {
console.log(req.query); // returns {}
}
);
So I tried to send a simple request to test it and got everything working without doing anything special (The only thing extra I have is that I'm using body-parser middleware from npm):
app.use(bodyParser.json({limit: '50mb'}));
Then I tried this simple route and got the result query params as you can see in the picture attached.
http://localhost:8080/test?test=1&what=2
Any chance you're sending the other form request from the client in a different way? try looking at the network in chrome and see if you sending what you expecting. Obviously, there is something missing here as it worked for me without doing anything special.

How to send data with redirect back in Express JS

Hello I am new to NodeJs. Currently I am working in node with Express framework.
I installed the express-back package in my project and now I want to send send back data to view from where post request fired.
Below is my code that I write:
routes.js
router.post('/register/user',Rules.UserRules.registerUser,UserController.registerUser)
UserController.js
const {check, validationResult} = require('express-validator');
registerUser = function (req, res, next) {
// Validate request parameters, queries using express-validator
const errors = validationResult(req)
console.log("==== errors ===")
console.log(errors.array())
if (!errors.isEmpty()) {
console.log("==== erorror founded ====")
return res.redirect('/signup',{errors:errors})
}else{
console.log('--- form body ----')
console.log(req.body)
}
}
module.exports = {registerUser}
When I submit my form to this route then control moves to UserController and I validations fails then I want to send it back to view without defining the redirect path I just want to send it back to view from where request received with errors. But I did not find any useful solution yet.
Is there any idea that how I can achieve this target. Suggest any useful link for nodejs beginner.
use res.send(errors) it send errors to client at this route. but if you want to redirect it to another route and then send it to client you have to create /signup route and use res.send(errors) it send errors to client. by default ``` res```` will redirect to redirected route.
router.post('/signup', (req, res)=>{
//send error or do somethings.
res.json(req.body.errors);
})
Use
app.locals.errors=errors
to make your error variable available to your template upon redirection as a general guide. Read more at http://expressjs.com/en/5x/api.html#app.locals. Remember, this will share the error variable across the application, so do not forget to apply logic that allows only the logged in user to view the error information relevant to that user.

Logging out with Firebase and Node.JS

I wanted to provide a Node.JS Express route wrapper for users who needed to logout and didn't have JavaScript enabled on the front-end. DB is my reference to my Firebase:
router.get('/logout', function(req, res) {
DB.unauth();
res.redirect(302, '/');
});
When I try to logout this way having been logged in, I get the following error:
Error: Can't set headers after they are sent.
The documentation for Firebase.unauth() is pretty light, but calling it in a route (with or without the res.redirect) causes the error. Does anyone have a suggestion or workaround for this issue?
So i was running into the same problem. For me it was that i was using the asynchronous authDataCallback function.
Apparently onAuth sets your headers so when you try to go to /logout your headers are set and the redirect will fail. So what i did is i switch the authDataCallback
that i had checking for login states to:
var authData = ref.getAuth();
if (authData) {
console.log("User " + authData.uid + " is logged in with " + + authData.provider);
} else {
console.log("User is logged out");
}
then my route for log out work flawlessly.
app.get('/logout', function(req , res){
ref.unauth();
res.redirect('/');
});
Looks like that error is being thrown on the redirect - you can't headers once the body has been sent. Maybe try:
response.writeHead(302, { Location: '/' });
response.end();
If that doesn't work, I'd put money on that .unauth() is actually setting headers, and if it is, you'll probably need to use the ol' <noscript> tag.
All of this said though: you're building an app that uses JavaScript to integrate with Firebase on the back end. Why build a modern web-app that (presumably) relies heavily on JavaScript only to support logging out users when they don't support it? Could they even log in in the first place?

How do I update A Jade Template form an ajax post?

I have set up a basic node.js web-app using express with the default view engine jade.
When the User first loads the page the following occurs
app.get('/', function(req, res){
res.render('index', {
title: 'Test',
mode: "user"
});
});
What i cannot work out is how to then change the parameter I initially passed into the jade template from a ajax call.
app.post('/', function(req, res){
console.log(req.body.list);
res.redirect('back');
// I imagine the code needs to go here and look somewhat like the following
//
// res.?update-view({
// mode: "admin"
// });
});
If anyone has had experience with this working your input would be appreciated.
I'm not exactly sure what you're after, but if it's updating the page with the results of an AJAX call (which does not refresh or otherwise reload the page) then you'll have to use client-side JavaScript. jQuery's load() or post() should be able to handle that.
Alternatively, if you are not using AJAX but instead performing a normal form submit, you have a couple of options. One, you can keep your redirect in and use Express/Connect's Sessions system to determine what is used for the get request, or two you can replace the redirect with another res.render of the index.jade page and include the variable you want to change.
It should be understood that after either of these takes place, node.js relinquishes control of the web page to the browser, unless you specifically set up architecture for communication. There are currently no controls in node.js to force updates or page changes down to the client. Except via socket connections or unless otherwise polled by the client itself (such as in the first example involving jQuery).
Assuming you want to display the same page, with other "mode"
// set the page title for all requests
app.locals({ title: 'Test' });
// GET request
app.get('/', function(req, res){
res.render('index', {
// displays the default "user" mode
mode: 'user'
});
});
// when POST is submited
app.post('/', function(req, res){
// this is the param given by the user
console.log(req.body.list);
// now render the same page
res.render('index', {
// with mode set to the the given parameter
mode: req.body.list
});
});
You could use something like the following, or if you want to use an AJAX call use jQuery ajax as long as there is a response
Client
script(type="text/javascript")
$(document).ready(function(){
//...AJAX here....
var newValue = #{value} + 10;
});
Server
app.get('/ajax', function(req, res){
//This is where your code and response go
});

Resources