AJAX Request gets cancelled with AngularJS and Spring Security - ajax

We're running an external Grails server-application with the Spring Security plugin.
The front-end is running locally on AngularJS.
Whenever I try to login, the request is immediately canceled.. Remarkably AngularJS sends a GET request first with the OPTIONS method; this returns a 200 OK response just fine.
The actual POST request does never reach the server though... what could possibly cancel my request?
The following code:
$scope.login = function() {
$http.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
$scope.loggingIn = true;
// Setup Config
var data = {
j_username: $scope.user.email,
j_password: $scope.user.password
}
var config = {method: 'POST', url: serverUri+'/j_spring_security_check/', data: data};
// Dispatch HTTP Request
$http(config)
.success(function(data, status, headers, config) {
if (data.status) {
// successful login
User.isLogged = true;
User.username = data.username;
}
else {
User.isLogged = false;
User.username = '';
}
$scope.loggingIn = false;
console.log("NOICE!");
})
.error(function(data, status, headers, config) {
$scope.loggingIn = false;
User.isLogged = false;
User.username = '';
if (status == 0) {
// Request got cancelled
console.log("Request got cancelled.");
return;
}
});
}
This is what the canceled request looks like: http://i.stack.imgur.com/kiWnb.png
This is what the OPTIONS request looks like: http://i.stack.imgur.com/FAj96.png

Apparently Chrome does not handle 302 Moved temporarily status codes efficiently when queried by AngularJS in my situation. Firefox properly shows there is a response where Chrome just shows the request as canceled with no response information whatsoever.
This question is solved, but there is still a mystery as to WHY AngularJS does not work. See my question here:
AngularJS $http ajax does not follow Location header

Related

How to get each http body updates on angular Http request?

I'm using an express api (my back-end) and an angular app (my front-end).
One express js end point (let's call it '/foo') is processing a lot of files,
i send data using res.write() after each treatment so the http response body is update.
I would like to get this update on my angular app.
I was using ajax in a previous version and it worked fine with ajax call :
xhrFields: {
// Getting on progress streaming response
onprogress: function(e)
{
var progressResponse;
var response = e.currentTarget.response;
if(lastResponseLength === false)
{
progressResponse = response;
lastResponseLength = response.length;
}
else
{
progressResponse = response.substring(lastResponseLength);
lastResponseLength = response.length;
}
actualResponse += progressResponse
}
Unfortunatly i found nothing to get partial http body. I tried to use 'reportProgress' Parameter but it's not working.
For some more context my front-end angular code:
service.ts :
setHolidaysDirectory(holidaysKey: string, path: string): Observable<Object>{
const setHolidayDirectoryStreamHttpRequest =
new HttpRequest('POST', 'http://localhost:8089/holidays/pictures/edit', { 'key': holidaysKey,
'path': path
}, {headers: this._httpHeaders, reportProgress: true, responseType: 'text'});
// pipe stream answer
return this._http.request(setHolidayDirectoryStreamHttpRequest);
}
and my component just call the service and subscribe :
this._holidaysService
.setHolidaysDirectory(key, finalHolidaysForm.path)
.subscribe((stream) => {
console.log('new answer');
console.log(stream);
}, error => console.log(error));
But unfortunatly i got empty answer and all the http body is recovered after res.end() (server side)
Can anyone help pls !
Thank a lot !

Ajax request with CORS redirect fails in IE11

I'm trying to make an ajax request to a resource on the same domain. Under certain circumstances the request gets redirected(303) to an external resource. The external resource supports CORS.
In browsers like Chrome, Firefox or Safari the request succeeds.
In IE11 the request fails with error:
SCRIPT 7002: XMLHttpRequest: Network Error 0x4c7, The operation was canceled by the user
The ajax request is made with jQuery:
$.ajax({
url: "/data",
type: "POST",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify({name: 'John Doe'})
}).done(function () {
console.log('succeeded');
}).fail(function () {
console.log('failed');
});
I've build a little example which demonstrates the problem. You could see the code here.
w/o redirect
w/ redirect
Is there a way to solve this problem? What am I missing?
In the initial definition of the CORS-standard, redirects after a successful CORS-preflight request were not allowed.
IE11 implements this (now outdated) standard.
Since August 2016, this has changed, and all major browsers now support it (Here's the actual pull request).
I'm afraid to support <=IE11 you'll have to modify your server-side code as well to not issue a redirect (at least for <=IE11).
Part 1) Server-side (I'm using node.js express here):
function _isIE (request) {
let userAgent = request.headers['user-agent']
return userAgent.indexOf("MSIE ") > 0 || userAgent.indexOf("Trident/") > 0
}
router.post('data', function (request, response) {
if (_isIE(request)) {
// perform action
res.set('Content-Type', 'text/plain')
return res.status(200).send(`${redirectionTarget}`)
} else {
// perform action
response.redirect(redirectionTarget)
}
})
Part 2 Client-side
Note: This is pure Javascript, but you can easily adapt it to your jQuery/ajax implementation.
var isInternetExplorer = (function () {
var ua = window.navigator.userAgent
return ua.indexOf("MSIE ") > 0 || ua.indexOf("Trident/") > 0
})()
function requestResource (link, successFn, forcedRedirect) {
var http
if (window.XMLHttpRequest) {
http = new XMLHttpRequest()
} else if (window.XDomainRequest) {
http = new XDomainRequest()
} else {
http = new ActiveXObject("Microsoft.XMLHTTP")
}
http.onreadystatechange = function () {
var OK = 200
if (http.readyState === XMLHttpRequest.DONE) {
if (http.status === OK && successFn) {
if (isInternetExplorer && !forcedRedirect) {
return requestResource(http.responseText, successFn, true)
} else {
successFn(http.responseText)
}
}
}
}
http.onerror = http.ontimeout = function () {
console.error('An error occured requesting '+link+' (code: '+http.status+'): '+http.responseText)
}
http.open('GET', link)
http.send(null)
}
its already answered - have a look - https://blogs.msdn.microsoft.com/webdev/2013/10/28/sending-a-cors-request-in-ie/

Express.js res.render not redirecting, just displayed in console

This time I want to use res.render to display html as success of DB update. I did it several times, but this time it doesn't work. It's not render html file, just displayed on chrome's console.
I think it caused because of async problem or duplicated response. I tried to many ways but I couldn't solve it, so pointers appreciated.
The code is related when the user paid service, increase user's level.
Get Access Token => Validate => res.render
app.post('/payment/validate', function(req, res, next){
// Get access token
request.post({
url : 'https://payment-company/get/token'
}, function(err, response, body) {
if(!err & response.statusCode == 200) {
var result = JSON.parse(body);
var accessToken = result.response.access_token;
// Validate payment (compare paid and would be paid)
request.get({
headers : { 'Authorization' : accessToken }
url : 'https://payment-company/find/paymentid'
}, function (err, response, body) {
if (!err && response.statusCode == 200){
var result = JSON.parse(body);
if (result.response.amount == req.body.price){
Members.findOne({id : req.user.id}, function(err, member){
// If no problem, update user level
member.level = 2;
member.save(function(err, result){
if (err) return next();
res.render('payment.view.result.ejs',
{
title : 'Success !',
description : 'level up.'
});
});
});
}
} else {
...
}
});
}
})
});
sorry to verbose code I tried to shorten code, No problem until res.render, res.render will work but it's not display page instead it just send html code to chrome's console.
Looks like there's a bit of a misunderstanding of how these requests work. What I think you intend:
Browser makes a GET request, server responds with an HTML document, the browser renders it
User takes an action
Browser makes a POST request, server responds with an HTML document, the browser renders it
What you've started coded on the frontend is an alternate method:
You make a POST request via AJAX, server responds with some JSON, you modify the current document with JavaScript to let the user know

AJAX call in expressJS

I can't seem to get the AJAX call correct. There have been other QA that deal with the $.ajax() function but I'm trying to solve this with $.post().
When the form button is clicked the javascript at the head is executed, which includes a $.post(). The url /login is routed through and passed to loginPost function. There a response is determined and sent back to the javascript (right?). Instead, webpage renders the response (pass || fail).
Why isn't the response from the AJAX call being sent back to get processed?
This is a simple example that I am working with to get me better acquainted to how AJAX in expressJS and jQuery work. Any Help is greatly appreciated!
--views/login.jade
script(src='/_js/jquery-1.8.2.min.js')
script
$(document).ready(function(req, res) {
$('#login').submit(function() {
var formData = $(this).serialize();
console.log(formData);
$.post('/login', formdata, processData).error('ouch');
function processData(data, status) {
console.log(status);
console.log(data);
if (data == 'pass') {
$('#content').html('<p>You have successfully loggin in!</p>');
} else {
if (! $('#fail').length) {
$('#formFrame').prepend('<p id="fail">Incorrect login information. Please try again)</p>');
}
}
} //end processData
}); //end submit
}); //end ready
div.main
h1= title
div#formFrame
form(id='login', action='/login', method='POST')
p
label(for='username') Username:
input(id='username', type='text', name='username')
p
label(for='password') Password:
input(id='password', type='password', name='password')
p
input(id='button', type='submit', name='button', value='Submit')
--routes/index.js
app.post('/login', loginPost);
--routes/loginPost
module.exports.loginPost = function(req, res) {
var password = 'admin'
, username = 'user'
, data = req.body;
if (data.username == username && data.password == password) {
res.send('pass');
} else {
res.send('fail');
}
};
You still have to stop the <form> from submitting via its default action, which can be done with event.preventDefault():
$('#login').submit(function(evt) {
evt.preventDefault();
// ...
});
Otherwise, the <form> will redirect the page to its action (or back to the current address if no action was given), interrupting the $.post request.

How can I send an AJAX request to a node.js server via HTTPS?

I have the following node.js server set up listening to port 9001
var https = require('https');
var fs = require('fs');
var qs = require('querystring');
var options = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
console.log('Request Received!');
console.log(req.method);
if (true || req.method == 'POST') {
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
console.log(body);
var POST = qs.parse(body);
console.log(POST);
});
}
res.end("hello, world\n");
}).listen(9001);
and I am trying to get this server to respond to an AJAX call
function form_save()
{
console.log("submitted!");
var data_obj = {
data1: "item1",
data2: "item2"
}
$.ajax({
url: 'https://adam.testserver.com:9001/',
type: "POST",
dataType: "json",
data: data_obj,
success: function() {
console.log("success!");
},
complete: function() {
console.log("complete!");
}
});
}
There are two problems occurring with my arrangement. The first is that if I start the server and then click the button that triggers my form_save() the node server does not respond and I get the following error:
submitted!
OPTIONS https://adam.testserver.com:9001/ Resource failed to load
jQuery.extend.ajaxjquery.js:3633
$.ajaxjquery.validate.js:1087
form_savew_worksheet.php:64
confirm_deletew_worksheet.php:95
jQuery.event.handlejquery.js:2693
jQuery.event.add.handlejquery.js:2468
w_worksheet.php:73
complete!
At this point if I access that url directy (https://adam.testserver.com:9001/) I get the expected "hello, world" output as well as the console message "Request Received!
GET". From this point on if I click the button to trigger my AJAX call I get a new error.
submitted!
XMLHttpRequest cannot load https://adam.testserver.com:9001/. Origin
https://adam.testserver.com is not allowed by Access-Control-Allow-Origin.
w_worksheet.php:73
complete!
I don't understand why I get this message as both my form and node server reside on the same server. Thanks for taking the time to read, I appreciate any help I can get on this. I've been stuck for a while now!
You've run into the Cross-Origin Resource Sharing (CORS) specification.
Note the OPTIONS in your output. The OPTIONS HTTP Verb is used by the browser to query the web server about the URL, not to GET its contents or POST data to it.
Your server doesn't respond with the correct header data on a CORS request, so your browser assumes it has no rights to access the data, and refuses to GET or POST to the URL.
If you truly want to let any website in the world run that AJAX request, you can do something similar to the following:
function handleOptions(request, response) {
response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Method": "POST, GET, OPTIONS",
"Access-Control-Allow-Headers": request.headers["access-control-request-headers"]
});
response.end();
}
function server(request, response) {
if(request.method == "POST") {
handlePost(request, response);
} else if(request.method == "OPTIONS") {
handleOptions(request, response);
} else {
handleOther(response);
}
}
https.createServer(sslObj, server).listen(9001);
You can fill in the details and whether you should handle GET separately, and so on (handleOther should return an appropriate error code for each request method you don't support).

Resources