When I send an xhr post request to my server. It replies with a 302 redirect, but I can only log the entire redirect html, and cannot get the browser to redirect to the new url.
server.js
const Hapi = require('hapi');
const Inert = require('inert');
const server = new Hapi.Server();
const port = 8888;
server.connection({ port });
server.register([ Inert ], () => {
server.route([
{
method: 'get',
path: '/',
handler: (request, reply) => {
reply.file('index.html');
}
},
{
method: 'get',
path: '/login',
handler: (request, reply) => {
reply.file('login.html');
}
},
{
method: 'post',
path: '/login',
handler: (request, reply) => {
reply.redirect('/');
}
}
]);
server.start(() => {
console.log('Server running on ', server.info.uri);
});
});
index.html
<!doctype html>
<html>
<body>
<h1> Home </h1>
go to login
</body>
</html>
login.html
<!doctype html>
<html>
<body>
<button id="home">home</button>
<script>
function goHome () {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState === 4 && xhr.status === 200) {
console.log('Response: ', xhr.response);
}
}
xhr.open('post', '/login');
xhr.send();
}
document.getElementById('home').addEventListener('click', goHome);
</script>
</body>
</html>
Is there a way to redirect to '/' without doing it client side?
Thanks in advance for the help
Is there a way to redirect to '/' without doing it client side?
No.
A redirect means "What you asked for can be found here" not "Navigate the browser to this URL". The browser will only navigate to the redirected URL if it was going to navigate to the URL it originally asked for.
A request initiated by XMLHttpRequest will get a response that will be processed by XMLHttpRequest. If that response is a redirect, it will be followed and the response to the new request will be handled by XMLHttpRequest.
Ajax is the act of making an HTTP request from JS without leaving the page.
If you want to leave the page, don't use Ajax.
Was looking for something similar, ended up setting header and then on client side used that for redirect:
$(document).on 'ajax:success', selector, (e, data, status, xhr) =>
redirectToPath = xhr.getResponseHeader('Location')
window.location = redirectToPath if redirectToPath
Can't give Node.js example, but basic Ruby on Rails controller method example with setting just header and head response:
def create
response.headers['Location'] = polymorphic_path(#document, action: :edit)
head(:ok)
end
Related
I am trying to implement a server which send large amount of data (objects, not files) every 500 ms.
After some reading, Server-sent events over http2 seems like the fastest option (due to http2 being a binary protocol and SSE reduce the traffic overhead)
After playing a bit with SSE on http/1.1
I've been trying to do the same on http2. I've tried to do so with stream and pushStream, but without success. However, using the same manner I used for http/1.1 seems to work.
My question is - why server 1 (see below) which using stream is not working, while server 2 seems to work fine? am I missing something when working node streams?
I'm using node v10.9.0 and chrome 68.0.3440.106
I've read the following questions and posts, but still couldn't solve this issue:
Can I stream a response back to the browser using http2?
Node.js HTTP2 server Error: socket hang up
HTTP2 / SPDY Push-Stream Verification: How to Test?
HTTP/2 Server Push with Node.js
Using Server Sent Events with express
Server 1 - http2 with stream (Not working - client don't get events. chrome describe the request as unfinished request):
const fs = require('fs');
const http2 = require('http2');
const HTTPSoptions = {
key: fs.readFileSync('./cert/selfsigned.key'),
cert: fs.readFileSync('./cert/selfsigned.crt'),
};
const template = `
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
const source = new EventSource('/sse/');
source.onmessage = function(e) {
document.body.innerHTML += e.data + '<br>';
};
</script>
</body>
</html>
`;
const server = http2.createSecureServer(HTTPSoptions);
server.on('stream', (stream, headers, flags) => {
if (stream.url === 'sse/') {
console.log(stream.respond);
stream.respond({
':status': 200,
'content-type': 'text/event-stream'
});
setInterval(() => stream ? res.write(`data: ${Math.random()}\n\n`) : '', 200);
}
});
server.on('request', (req, res) => {
if(req.url === '/') {
res.end(template);
}
});
server.listen(3001);
Server 2 - http2 with stream (working just fine):
const fs = require('fs');
const http2 = require('http2');
const HTTPSoptions = {
key: fs.readFileSync('./cert/selfsigned.key'),
cert: fs.readFileSync('./cert/selfsigned.crt'),
};
const template = `
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
const source = new EventSource('/sse/');
source.onmessage = function(e) {
document.body.innerHTML += e.data + '<br>';
};
</script>
</body>
</html>
`;
const server = http2.createSecureServer(HTTPSoptions);
server.on('request', (req, res) => {
req.socket.setKeepAlive(true);
if(req.url === '/sse/') {
res.writeHead(200, {
'Content-Type': 'text/event-stream', // <- Important headers
'Cache-Control': 'no-cache'
});
res.write('\n');
setInterval(() => res.write(`data: ${Math.random()}\n\n`), 200);
} else {
res.end(template);
}
});
server.listen(3001);
To get the request path for a http2 stream, you have to look at the :path header (docs):
if (headers[':path'] === '/sse/') {
You also tried to use res.write, while res should be stream.
This is a working handler function based on your "Server 1" implementation:
server.on('stream', (stream, headers, flags) => {
if (headers[':path'] === '/sse/') {
stream.respond({
':status': 200,
'content-type': 'text/event-stream'
});
setInterval(() => stream.write(`data: ${Math.random()}\n\n`), 200);
}
});
I have a problem with my Google Sign in.
I follow the steps on Google Sign-In for server-side apps
After all this step, i have this html file that run on a server :
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/Article">
<head>
<link rel="stylesheet" href="theme.css">
<!-- BEGIN Pre-requisites -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
<!-- END Pre-requisites -->
<script>
function start() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: 'CLIENT_ID'
});
});
}
</script>
</head>
<body>
<button id="googleBtn">Connect</button>
<script>
$('#googleBtn').click(function() {
auth2.grantOfflineAccess().then(googleCallback);
});
</script>
<script>
function googleCallback(authResult) {
if (authResult['code']) {
console.log(authResult);
} else {
console.log(authResult);
}
}
</script>
</body>
</html>
This first part work perfectly (I think), when I click on button, a window popup, I select an account and I get a code. So now, I want to get email and profile, so I make this request in nodejs to exchange the code with an access token.
var form = {
code: 'XXXXXXXXXXXX',
client_id: 'CLIENT_ID,
client_secret: 'CLIENT',
grant_type:'authorization_code'
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: 'https://www.googleapis.com/oauth2/v4/token',
body: formData,
method: 'POST'
}, function (err, rep, body) {
console.log(err);
console.log(rep.statusCode);
console.log(body);
});
But in response, I have this
{
"error": "redirect_uri_mismatch",
"error_description": "Bad Request"
}
And I don't understand why, because I don't use any redirect_uri in primary request in html file. I have try some cases like add redirect_uri on Console API and in request, but I have the same result...
Can you help me ?
From a client side auth2.grantOfflineAccess process, to get accessToken on server, you have to pass redirect_uri=postmessage in request params.
From your example :
var form = {
code: 'XXXXXXXXXXXX',
client_id: 'CLIENT_ID,
client_secret: 'CLIENT',
grant_type:'authorization_code',
redirect_uri: 'postmessage' // the line to add
};
You need to define your callback url to Google via console.developer.google.com
So you need to follow this steps for define the callback URL:
Go to console.developer.google.com
Select your project.
Click 'Credentials' on the left menu.
Click your client on the 'OAuth 2.0 client IDs' list.
Add your callback url to 'Authorized redirect URIs' list.
And done.
I am trying to get the data from the url: autotrader_url
Unfortunately I am not able to handle the redirect.
Here is my code so far.
var request = require('request');
var cheerio = require('cheerio');
request({followAllRedirects: true,url:"http://www.autotrader.com/cars-for-sale/showcase.xhtml?zip=94536&endYear=2017&Log=0&modelCode1=LEGACY&sortBy=derivedpriceDESC&startYear=1981&makeCode1=SUB&numRecords=25&searchRadius=25&mmt=%5BSUB%5BLEGACY%5B%5D%5D%5B%5D%5D&makeCodes=SUB"},
function (error, response, html) {
console.log(response)
if (!error && response.statusCode == 200) {
console.log("yo");
var $ = cheerio.load(html);
console.log($("title").text());
$('div.listing-title h2').each(function(i, element){
var a = $(this);
console.log(a.innerHTML);
});
}
});
What am i missing?
The followAllRedirects: true option will follow http redirects sent back from the server. It appears that they are not using an http redirect because when you visit that page in the browser it loads a page that says "We're searching for the car that you want" and that page does a client side redirect in the browser using javascript. To follow that kind of redirect you probably have to use something like phatomjs.
Alternatively using cheerio(maybe) or some combination of regex you could get the redirect url directly from the source and then make a second request to that url on your own once you have the right url.
<script type="text/javascript">
$(function() {
atc.create(atc.showcaseRedirect, {
destinationUrl: '/cars-for-sale/Subaru/Legacy/Fremont+CA-94536?endYear=2017&firstRecord=0&makeCode1=SUB&mmt=%5BSUB%5BLEGACY%5B%5D%5D%5B%5D%5D&modelCode1=LEGACY&searchRadius=25&showcaseOwnerId=68619541&startYear=1981&Log=0',
queryString: '',
....
}).init();
});
</script>
You just have to grab that destinationUrl. Now with all that said, this assumes you aren't breaking any of their terms of use so you should definitely look into that before you move forward.
I'm not sure if its a bug on their end or if they are trying to prevent people from scraping but you need to set a User-Agent header to get them to respond so add this to your request.
Here is a full working example:
var request = require('request');
var cheerio = require('cheerio');
var firstUrl = "http://www.autotrader.com/cars-for-sale/showcase.xhtml?zip=94536&endYear=2017&Log=0&modelCode1=LEGACY&sortBy=derivedpriceDESC&startYear=1981&makeCode1=SUB&numRecords=25&searchRadius=25&mmt=%5BSUB%5BLEGACY%5B%5D%5D%5B%5D%5D&makeCodes=SUB";
makeRequest(firstUrl, function(err, html) {
if(err) {
return console.log('There was a problem');
}
// get "redirect" url from page source
var re = new RegExp("destinationUrl\:[^\,\}]*");
var redirectUrl = 'http://www.autotrader.com' + html.match(re)[0].replace('destinationUrl: ', '').replace('\'', '');
console.log('redirectUrl', redirectUrl);
// make the second request and process the markup with cheerio
makeRequest(redirectUrl, processFinalMarkup);
});
function processFinalMarkup(err, html) {
var $ = cheerio.load(html);
console.log($("title").text());
$('div.listing-title h2').each(function(i, element){
var a = $(this);
console.log(a.innerHTML);
});
}
function makeRequest(url, callback) {
request({
// Their page requires a User-Agent to be set.
headers: {
'User-Agent': 'express'
},
followAllRedirects: true,
url: url
},
function (error, response, html) {
console.log(response.headers, response.statusCode);
if (!error && response.statusCode == 200) {
console.log("yo");
callback(null, html);
}
});
}
I have a token named userToken, it's in localstorage. I need to attach this token to http requests (get/post) since I check whether a user is logged in or not using this token.
I implemented it for jQuery Ajax post requests, now I can work with them, but I have no idea how to attach it to all http requests (for instance, a simple get request), with or without ajax.
Here's what I've done so far:
Jade
#click Click here
script.js (Client Side)
var _token = localStorage.getItem('userToken');
$.ajaxSetup({
headers: { 'token' : _token }
});
$(document).ready(function () {
$('#click').click(function () {
$.post("/testurl2", {}, function (data) {
console.log(data);
});
});
});
NodeJS
app.get('/testurl1', function(req,res) {
var myPreciousToken = req.headers.token;
console.log(myPreciousToken); // it doesn't work.
});
app.post('/testurl2', function(req,res) {
var myPreciousToken = req.headers.token;
console.log(myPreciousToken); // it works
});
Try to change your ajaxSetup to
$.ajaxSetup({
//headers: { 'token' : _token },
beforeSend: function(xhr){
xhr.setRequestHeader('token', _token );
}
});
I am trying to do the following:
from client
var req = jQuery.post(
"http://www.example.com:3000"+"/dologin",
{"username" : username, "password" : password}).error(function(){
alert("an error occurred");
});
in express root
app.post('/dologin',function(req, res) {
res.redirect('http://bbc.co.uk');
});
result passed back
<p>Moved Temporarily. Redirecting to http://bbc.co.uk</p>
Seems that if I do post from jquery the redirect will not work. Does anyone know a way to force it to redirect?
Browser does not redirect the window on redirect on ajax response. Redirect the browser with javascript.
In server send the new site as content, for example.
res.contentType('application/json');
var data = JSON.stringify('http://site.example.com/')
res.header('Content-Length', data.length);
res.end(data);
In client
var req = jQuery.post(
"http://www.mysite.com:3000"+"/dologin",
{"username" : username, "password" : password}, 'json').error(function(){
alert("an error occurred");
}).success(function(data) {
window.location = data;
});
I've actually encountered the same thing when developing an app. It seems Express doesn't redirect if the method is post.
Try:
app.post('/dologin',function(req, res) {
req.method = 'get';
res.redirect('http://bbc.co.uk');
});
I'm doing something like this when using OAuth2. I have an link to one of my pages and this in turn redirects to google.
To redirect to another location the following code does the actual redirect
app.get('/GoogleRequestAuthorization.html',function(req,res) {
.
.
.
.
res.writeHead(302, {location: url});
res.end();
});
url being the address you want to redirect to.
The full function is...
I have come to a similar problem and solved it by checking the type of the request. In my case I use JSON, but it works for other POST requests as well:
var ajax = req.xhr;
if(ajax) {
res.status(401).json({'msg':'redirect','location':'/login'});
}
else {
req.method = 'get';
res.status(401).redirect('/login');
//Or if you prefer plain text
//res.status(333).send("Redirect");
}
This handles both Ajax POST and AJAX and standard GET requests.
On the client, in the Ajax respose callback:
$.ajax({
type: 'POST',
data: Msg,
url: '/some/post',
dataType: 'JSON'
}).success(function(data, textStatus, req ) {
if(data.msg==="redirect") window.location = data.location;
else {...}
}).error(function(data, textStatus, req) {
if(req=="Unauthorized") {
alert("Unauthorized!");
window.location = "/login";
} else if (data.responseJSON.msg==="redirect") window.location = data.responseJSON.location;
else {
//...
}
});
You can actually handle more statuses here, except for 302, which is automatically followed by JQuery and you get as response 200 from the page you wanted to redirect to. So I avoid sending the 302, sending 401 in my case, or any other status, for example 333, which will be handled as error.