$http error handling: distinguishing user offline from other errors - ajax

I have a simple contact form, which Angular submits via AJAX to my app on Google App Engine. (A POST handler uses the send_mail function to email the website owner). This is the client code:
$http.post('', jQuery.param(user), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).success(function(data, status, headers, config) {
//...
}).error(function(data, status, headers, config) {
alert('Please check your Internet connection and try again.');
});
Obviously the alert() is handling all errors. Assuming no errors in my server-side code, I'm guessing the chances of App Engine returning anything other than an HTTP status code of 200 is low. However I would still like to distinguish between server errors and the user having lost their connection.
I was thinking of using XMLHttpRequest's textStatus as per this SO answer, but it doesn't appear to be available to the $http.error() callback. I also thought of using Offline.js but I don't need most of what it does so that would seem like wasted bytes in this case.
The $http.error() status I get when offline is 0, but I'm not sure how cross-browser reliable that's going to be. What should I be using?

Before giving you the solution I just wanted to highlight the problems with browser provided is-offline flag
Different browser's offline flag has different meanging
a. for some browsers it means internet is not there
b. for some browsers it means intranet is not there
c. some browsers do allow offline mode, so even if there is no internet, you are not actually offline.
not all browsers support offline in consistent way
Another problem I had with using browser based flag is development scenario, where having internet is not necessary, and that should not trigger the offline mode (for me I block all user interaction If my website goes offline). You can solve this problem by having another indicator telling you if you are in dev/prod, etc.
And most imp part, why do we care to find if browser is in offline mode, is because we do care only if our website is reachable or not, we don't actually care if the internet is there or not. So even if browser tell us it is offline, it is not exactly what we want. there is a tiny difference between what we want and what browser provides.
So considering all of above, I have solved the problem using an offline directive which I am using to block user interaction if user is offline
csapp.directive('csOffline', ["$http", '$interval', "$timeout", "$modal", function ($http, $interval, $timeout, $modal) {
var linkFn = function (scope) {
scope.interval = 10; //seconds
var checkConnection = function () {
$http({
method: 'HEAD',
url: document.location.pathname + "?rand=" + Math.floor((1 + Math.random()) * 0x10000)
})
.then(function (response) {
$scope.isOnline = true;
}).catch(function () {
$scope.isOnline = false;
});
};
scope.isOnline = true;
$interval(checkConnection, scope.interval * 1000);
scope.$watch('isOnline', function (newVal) {
console.log("isOnline: ", newVal);
//custom logic here
});
};
return {
scope: {},
restrict: 'E',
link: linkFn,
};
}]);
I was about to use offline.js, it was too much and most of which I didnt need, so this is the solution I came up with which is purely in angular.js.
please note that http interceptor is invoked during these calls, I wanted to avoid that, hence I had used $.ajax for the calls
$.ajax({
type: "HEAD",
url: document.location.pathname + "?rand=" + Math.floor((1 + Math.random()) * 0x10000),
contentType: "application/json",
error: function () {
scope.isOnline = false;
},
success: function (data, textStatus, xhr) {
var status = xhr.status;
scope.isOnline = status >= 200 && status < 300 || status === 304;
}
}
);
you can replace the logic inside isOnline true/false, with whatever custom logic you want.

I'd go with response.status === 0 check. I've tested it using the code below (needs to be put on a webserver that you can switch on/off at will) and I'm getting 0 in current versions of Google Chrome, Mozilla Firefox, and Internet Explorer. You may use it to test all browsers you want to support.
Code for testing connection status:
<!DOCTYPE html>
<html>
<head>
<title>Connection status test</title>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<script type="text/javascript">
var log = [],
pendingCount = 0,
pendingLimit = 5;
angular.module('app', [])
.run(function ($rootScope) {
$rootScope.log = log;
})
.run(function ($http, $interval, $rootScope) {
$interval(function () {
if (pendingCount >= pendingLimit) {
return;
}
var item = {
time: Date.now(),
text: 'Pending...'
};
++pendingCount;
$http.get('.', {})
.then(function () {
item.text = 'Done';
}, function (response) {
item.text = 'Done (status ' + response.status + ')';
})
.finally(function () {
--pendingCount;
});
log.unshift(item);
}, 1000);
});
</script>
<style rel="stylesheet" type="text/css">
ul {
list-style: none;
padding: 0;
}
</style>
</head>
<body ng-app="app">
<ul>
<li ng-repeat="item in log">{{item.time | date:'HH:mm:ss'}}: {{item.text}}</li>
</ul>
</body>
</html>

try this one
$http.post('', jQuery.param(user), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).complete(function(data, status, headers, config) {
//...
});

Related

get requests fail when cache manifest is applied

I'm comfortable working with $.ajax and also in using cache.manifest. Recently I decided to start using "get" instead of "post" to help see the parameters easier.
In this proof-of-concept, if I delete the cache.manifest from the server, everything works. But when I put the cache.manifest on the server, the page stops working with an undefined jqXHR.responseText.
Furthermore, if I change the get to a post, it works with the cache.manifest.
Q: Does an https require a post, making "get" invalid if you are using a cache manifest? It seems to be working if the cache manifest is missing and it works with the cache manifest if I use post.
var local = {}
local.type = 'get'
local.dataType = 'text'
local.data = {}
local.data.CtrlName = 'testing123'
var promise = $.ajax('where_ctrlName.cfm',local)
promise.done(done)
promise.fail(fail)
function done(response) {
console.log(response)
}
function fail(jqXHR, textStatus, errorThrown) {
debugger
}
window.applicationCache.addEventListener('updateready', updateReady, false)
function updateReady() {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
$('body').html('<h1>Updating</h1>')
setTimeout(reloadCache,1000)
}
}
function reloadCache() {
window.location.reload()
}
<html manifest="cache.manifest">
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</body>
</html>
Here's my cache.manifest:
CACHE MANIFEST
https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
For What It's Worth, this is an https call.

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/

socketio client: How to handle socketio server down

I've got a socketio server/client working well together, however I want to start writing events for when the server is offline on page load or during normal run.
I'm including the remote socket.io code in my header:
<script src="<?=NODE_HOST?>/socket.io/socket.io.js"></script>
<script>
var nodeHost = '<?=NODE_HOST?>';
</script>
And in my client controller I have
if(typeof io != 'undefined')
this.socket = io.connect(this.settings.server);
else
this.handleDisconnect();
The function I have to attempt to re-connect over and over if a) A socket disconnect occurs during normal operation, or b) the server is down on page load
botController.prototype.handleDisconnect = function() {
$.getScript(nodeHost+"/socket.io/socket.io.js").done(function(script, textStatus) {
bot.control.socket = io.connect(bot.control.settings.server);
}).fail(function(jqxhr, settings, exception) {
setTimeout(function() {
bot.control.handleDisconnect();
}, 5000);
});
}
Am I going about this the correct way?
The main issue I have right now (which made me create this question) is my code errors on page load when the server is down because I have functions like:
socket.on(...
When socket doesn't yet exist. I could wrap those in a function and call it when I detect the global socket object exists on successful reconnection? Would it matter if that function that contains socket.on... is called multiple times (if the server goes down more than once during operation)?
OK I managed to come up with this solution that seems to work well using yepnope which I already had using Modernizr (it handles the cross domain issue for me too).
<script src="<?=NODE_HOST?>/socket.io/socket.io.js"></script>
<script>
var nodeHost = '<?=NODE_HOST?>';
</script>
// Attempt to connect to nodejs server
botController.prototype.start = function() {
// Is our nodejs server up yet?
if(typeof io != 'undefined') {
this.socket = io.connect(this.settings.server);
this.startSocketEvents();
} else {
this.handleDisconnect();
}
}
// Our connection to the server has been lost, we need to keep
// trying to get it back until we have it!
botController.prototype.handleDisconnect = function(destroySocketObject) {
if(destroySocketObject === undefined)
destroySocketObject = true;
// Destroy any cached io object before requesting the script again
if(destroySocketObject)
io = undefined;
yepnope.injectJs(nodeHost+"/socket.io/socket.io.js",
function(result) {
// Did it actually download the script OK?
if(typeof io != 'undefined') {
bot.control.socket = io.connect(bot.control.settings.server);
bot.control.startSocketEvents();
} else {
setTimeout(function() {
bot.control.handleDisconnect(false);
}, 5000);
}
}
);
Where startSocketEvents() function contains all of my socket.on events

Clear IE cache when using AJAX without a cache busting querystring, but using http response header

I'm having the classic IE-caches-everything-in-Ajax issue. I have a bit of data that refreshes every minute.
Having researched the forums the solutions boil down to these options (http://stackoverflow.com/questions/5997857/grails-best-way-to-send-cache-headers-with-every-ajax-call):
add a cache-busting token to the query string (like ?time=[timestamp])
send a HTTP response header that specifically forbids IE to cache the request
use an ajax POST instead of a GET
Unfortunately the obvious querysting or "cache: false" setting will not work for me as the updated data file is hosted on Akamai Netstorage and cannot accept querystrings. I don't want to use POST either.
What I want to do is try send an HTTP response header that specifically forbids IE to cache the request or if anyone else knows another cache busting solution??
Does anyone know how this might be done? Any help would be much appreciated.
Here is my code:
(function ($) {
var timer = 0;
var Browser = {
Version: function () {
var version = 999;
if (navigator.appVersion.indexOf("MSIE") != -1) version = parseFloat(navigator.appVersion.split("MSIE")[1]);
return version;
}
}
$.fn.serviceboard = function (options) {
var settings = { "refresh": 60};
return this.each(function () {
if (options) $.extend(settings, options);
var obj = $(this);
GetLatesData(obj, settings.refresh);
if (settings.refresh > 9 && Browser.Version() > 6) {
timer = setInterval(function () { GetLatestData(obj, settings.refresh) }, settings.refresh * 1000);
}
});
};
function GetLatestData(obj, refresh) {
var _url = "/path/updated-data.htm";
$.ajax({
url: _url,
dataType: "html",
complete: function () {},
success: function (data) {
obj.empty().append(data);
}
}
});
}
})(jQuery);
Add a random number to the GET request so that IE will not identify it as "the same" in its cache. This number could be a timestamp:
new Date().getTime()
EDIT perhaps make the requested url:
var _url = "/path/updated-data.htm?" + new Date().getTime()
This shouldn't cause any errors I believe.
EDIT2 Sorry I just read your post a bit better and saw that this is not an option for you.
You say "is hosted on Akamai and cannot accept querystrings" but why not?
I've never heard of a page that won't accept an additional: "?blabla", even when it's html.
This was driving me crazy. I tried many cache busting techniques and setting cache headers. So many of these either did not work or were wild goose chases. The only solution I found which tested to work correctly was setting:
Header Pragma: no-cache
I hope it saves others with IE headaches.

Looking for an extremely simple AJAX script

I need a very basic, simple and lightweight AJAX script.
Does anyone have a shell of such a script they can share?
Here's what I have:
I have a PHP script on the server that echo's the current date and time on the server
I just need the javascript that calls the php script and loads the echoed text string into a js var so I can use it in my app
(the reason I need the server's clock is that all visitors to the site have to work off the same clock. The app does not work for visitors outside the server's timezone.)
Thanks for helping out.
JQuery is perhaps the right answer for AJAX but you can also do this in plain old Javascript as follows:
<html>
<head>
<script type="text/javascript">
function loadXMLDoc(){
var xmlhttp;
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//the callback function to be callled when AJAX request comes back
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
  }
}
xmlhttp.open("POST","<<url of web address>>",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Henry&lname=Ford");
}
</script>
</head>
<body>
<h2>AJAX</h2>
<button type="button" onclick="loadXMLDoc()">Request data</button>
<div id="myDiv"></div>
</body>
</html>
You can find a simple example here:
AjaxCall = function(Data, WebServiceURL, Callback) {
var request;
var url = WebServiceURL;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
Callback(request);
} else {
alert("Sorry, an error occurred. " + request.responseText);
}
}
};
request.open("POST", url, true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send(Data);
} else if (window.ActiveXObject) {
url += "?" + Data;
request = new ActiveXObject("Microsoft.XMLHTTP");
if (request) {
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
Callback(request);
} else {
alert("Sorry, an error occurred. " + request.responseText);
}
}
};
request.open("GET", url, true);
request.send();
}
}
};
The the ajax functionality in jQuery is great but does mean a greater page download for one simple Javascript function.
You can find a downloadable fully worked example on my blog here:
http://www.willporter.co.uk/blog/simple-ajax-script.aspx
It uses ASP.NET on the server side but you should get the idea.
jQuery has made very simple ajax methods for you to use. You can find more information about them here.
Sample:
$.ajax({
type: 'GET',
url: '/SomeUrl/On/The/Server',
data: { SomeValue: 10 },
success: function(data, status)
{
// On Success
},
error: function(data, status)
{
// On Error
}
});
Look into this maybe : http://www.scriptiny.com/2011/01/simple-ajax-function-example/
jQuery is a more reliable library overall, but the lightest-weight AJAX methods I have found are the extremely simple Feather AJAX, coming in at 1.6 KB (with room for compression), or a one-liner snippet that I can't guarantee.
The risk of extremely lightweight libraries is that if they break, you're relying on the owner to fix it instead of a team of developers.
An alternative approach to solving your problem is to based your times on UTC instead of server-local time. You can even show the client local times based on that utc time, with a little work.
May I suggest AJAX Generator?
I am developer, and it is commercial tool but it has demo as well.
What you could do with that tool is:
put annotation on your PHP function
run AJAX Generator on PHP source file
include generated JavaScript file in HTML page and use PHP service as if you were calling function
To make it more clear, here is example code:
//example.php
<?php
//#WebService
function getServerDate(){
return date('m/d/Y h:i:s a', time());
}
?>
Now run generator on: example.php
Output would be two files: example_service.php and example_caller.js
Now you need to:
add example_service.php in same directory where is example.php and
include example_caller.js in index.html
Sorry for posting image instead of HTML code, but it wasn't showing properly.

Resources