This is solved at last with "timeout" attribute of jQuery AJAX (and JSONP). See my own answer !
Please see the updated part, I have tried with applet too. And will not hesitate to accept your answer if you can give a solution with applet implementation.
I am working with a Java based web application. My requirement is to check whether a particular port (say 1935) is open or blocked at client's end. I have implemented a "jsonp" (why 'jsonp' ? i found that 'http' request through AJAX cannot work for corssdomain for browsers 'same origin policy') AJAX call to one of my server containing particular port. And if the server returns xhr.status == 200 the port is open. Here is a drawback that I can't make the execution-flow wait (synchronous) until the call completes. Here is the JavaScript function I am using.
Any alternative solution (must be a client-sided thing must be parallel with my application, please dont suggest python/php/other languages) is also welcome. Thanks for your time.
function checkURL() {
var url = "http://10.0.5.255:1935/contextname" ;
var isAccessible = false;
$.ajax({
url: url,
type: "get",
cache: false,
dataType: 'jsonp',
crossDomain : true,
asynchronous : false,
jsonpCallback: 'deadCode',
complete : function(xhr, responseText, thrownError) {
if(xhr.status == "200") {
isAccessible = true;
alert("Request complete, isAccessible==> " + isAccessible); // this alert does not come when port is blocked
}
}
});
alert("returning isAccessible=> "+ isAccessible); //this alert comes 2 times before and after the AJAX call when port is open
return isAccessible;
}
function deadCode() {
alert("Inside Deadcode"); // this does not execute in any cases
}
---------------------------------------------------------UPDATE----------------------------------------------------------------
I have tried with Java Applet (thanks to Y Martin's suggestion). This is working fine in appletviewer. But when I add the applet in HTML page, it is giving vulnerable results. Vulnerable in the sense, when I change the tab or resize the browser, the value of portAvailable is being altered in the printed message.
Applet Code :
import java.applet.Applet;
import java.awt.Graphics;
import java.net.InetSocketAddress;
import java.net.Socket;
public class ConnectionTestApplet extends Applet {
private static boolean portAvailable;
public void start() {
int delay = 1000; // 1 s
try {
Socket socket = new Socket();
/*****This is my tomcat5.5 which running on port 1935*************/
/***I can view it with url--> http://101.220.25.76:1935/**********/
socket.connect(new InetSocketAddress("101.220.25.76", 1935), delay);
portAvailable = socket.isConnected();
socket.close();
System.out.println("init() giving---> " + portAvailable);
}
catch (Exception e) {
portAvailable = false;
System.out.println("init() giving---> " + portAvailable);
System.out.println("Threw error---> " + e.getMessage());
}
}
public void paint(Graphics g) {
System.out.println("Connection possible---> " + portAvailable);
String msg = "Connection possible---> " + portAvailable;
g.drawString(msg, 10, 30);
}
}
And this is my HTML page (I am hosting it on same computer with a different Tomcat 6 which runs on port 9090. I can view this page with url ---> http://101.220.25.76:9090/test/):
<html>
<body>
<applet code="ConnectionTestApplet" width=300 height=50>
</applet>
</body>
</html>
And how I am doing the port 1935 blocking and openning ?
I have created firewall rule for both inbound and outbound for port 1935.
I check the port 1935 open/blocked scenario by disabling/enabling both rules.
This is my S.S.C.C.E. Now please help me :)
Gotcha !!! I have solved my problem with JSONP and jQuery AJAX call. I discovered the timeout attribute of jQuery AJAX and my code executed fluently when the port was blocked or opened. Here is the solution for future visitors. Thanks to all answerers for contribution.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="jquery-1.7.2-min.js"></script>
</head>
<body>
<script type"text/javascript">
var isAccessible = null;
function checkConnection() {
var url = "http://101.212.33.60:1935/test/hello.html" ;
$.ajax({
url: url,
type: "get",
cache: false,
dataType: 'jsonp', // it is for supporting crossdomain
crossDomain : true,
asynchronous : false,
jsonpCallback: 'deadCode',
timeout : 1500, // set a timeout in milliseconds
complete : function(xhr, responseText, thrownError) {
if(xhr.status == "200") {
isAccessible = true;
success(); // yes response came, esecute success()
}
else {
isAccessible = false;
failure(); // this will be executed after the request gets timed out due to blockage of ports/connections/IPs
}
}
});
}
$(document).ready( function() {
checkConnection(); // here I invoke the checking function
});
</script>
</body>
</html>
I don't think you understand the use cases for JSONP and it's not possible to test open ports with it. http://en.wikipedia.org/wiki/JSONP
If you want a client side solution it could be possible with websockets, but this is only available on new browsers like chrome or ff. Otherwise request a server side script which does the ping. For example - with a curl script: curl and ping - how to check whether a website is either up or down?
Here is a Java code as an Applet to test server/port connectivity:
import java.io.*;
import java.net.*;
public class ConnectionTestApplet extends Applet {
public void start() {
boolean portAvailable = false;
int delay = 1000; // 1 s
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("server.domain.com", 1935), delay);
portAvailable = socket.isConnected();
socket.close();
} catch (UnknownHostException uhe) {
uhe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("Connection possible: " + portAvailable);
}
}
You still have to get the information out of the applet to do something else with that result. The easiest way is to redirect the browser thanks to getAppletContext().showDocument(url)
Instead of an applet a flash component may be used. Using the Socket class available in ActionCcript one can open a tcp connection from flash to a port on a server to check if its open. But based on the flash player version a policy file needs to be placed on the server to which the socket is opened.
Check this out:
http://blog.andlabs.org/2010/12/port-scanning-with-html5-and-js-recon.html
With JS-Recon, you can do port scanning with javascript. You can simply point it to your local IP address. I believe it works by making a web sockets/cors connection to an arbitrary desintation ip/socket and measuring the timeouts. It is not a perfect approach, but this may be the limit of javascript ultimately.
If you can do it in a java applet/flash application, that may be better ultimately as they have lower-level access.
You cannot do this in JavaScript because it doesn't have true socket support, with JavaScript you can only test for the presence of HTTP socket. You could use Java (JavaScript is not Java) and write a proper Java Applet to do it.
You should also read this Q&A How to Ping in java
Try using isReachable
In JavaScript, you have to work-around the asynchronous issue. Here is a proposal:
The HTML page displays an animated image as a progress bar
You invoke the checkURL
After either receiving the callback or a defined timeout, you change display for an error message or do on with the job to do
Based on the following document with the use of XMLHttpRequest, here is a code example for checkURL:
var myrequest = new ajaxRequest();
var isAccessible = false;
myrequest._timeout = setTimeout(function() {
myrequest.abort();
displayErrorMessage();
},
1000
) //end setTimeout
myrequest.onreadystatechange = function() {
if (myrequest.readyState == 4) { //if request has completed
if (myrequest.status == 200) {
isAccessible = false;
goOnWithTheJob();
} else {
displayErrorMessage();
}
}
myrequest.open("GET", url, true);
myrequest.send(null); //send GET request
// do nothing - wait for either timeout or readystate callback
This code lets 1 second to get the 200 response from a HTTP GET on a basic resource.
In your local test, you get an immediate answer because the system answers connection reset if the port is closed but a firewall just does not answer.
Even if the open method may be used synchronously, I recommend the use of a timer because the code is likely to wait for TCP timeouts and retries (3 x 1 minute ?) as a firewall usually just drops packets on closed ports and may reject ICMP packets, preventing you to test availability thanks to ping. And I imagine such a long wait is not expected for such a check.
I am occasional frontend/javascript/jQuery guy, so this may not be 100% professional, but it is good enough and it solved my similar problem:
ping_desktop_app = $.get({
url: "http://127.0.0.1:#{desktop_app_port}",
dataType: 'jsonp',
})
$(#).parent().find(".click-me-to-use-desktop-app").click ->
if ping_desktop_app.status == 200
$.get({
url: "http://127.0.0.1:#{desktop_app_port}/some_command/123123",
dataType: 'jsonp',
})
else
alert("Please run your desktop app and refresh browser")
I could not check whether port is open (desktop app is running) on server side because views are cached, so I needed to check the localhost/port right before user click in browser
Edits translating to JS are welcome
Related
In fact, I have a JavaScript application (jQuery) that is sending a request to a Java function which is supposed to send back data (success case) to my ajax function.
In the success case, I go on with my application; in error case, I stop my procedures and display the error message to the user.
But when the Java code takes more than 5 minutes to send a response to my Ajax call, this one (ajax code) doesn't (work / respond / go on /) get any response anymore ...
I tried setting up a timeout limit to 600000 ms (so 10 minutes) but I still get the same problem (and ain't got any error messages or output, which could have been helping me).
So if anyone's got an idea on the source of this problem, I'd be grateful.
Here is some piece of code if it may make it clearer :
JSP
$(document).ready(function() {
/* performing some layout changes */
//
preValidationDisplay();
}
function preValidationDisplay() {
/* performing some layout changes */
setTimeout("getValidationResult()", 500);
}
function checkReadLogFile() {
$.ajax({
url: logFileUrl,
cache: false,
success: function(result)
{
$("#progressBarToResize").width(
'' + parseFloat(result.trim()) + '%' );
}
});
}
function getValidationResult()
{
var timer = setInterval("checkReadLogFile()", 1000);
$.ajax({
url: '<s:url value="doValidation"/>',
type: "GET",
cache: false,
dataType: "json",
async: true,
//timeout : 600000,
success: function(result, status)
{
// Do something
},
error: function(jqXHR, status, errorStr){
// Do something
}
});
}
java
#RequestMapping(value = "/doValidation", method = RequestMethod.GET)
public String processValidationResults(HttpServletRequest request, HttpServletResponse response,
#RequestHeader("Connection") String conn) {
ValidationResult validationResult = null;
JSONObject resultJson = new JSONObject();
HttpSession session = request.getSession();
JSONObject progressMap = (JSONObject) session.getAttribute("progressMap");
String uploadedFilePath = progressMap.getString("filePath");
String realMimeType = progressMap.getString("realMimeType");
long fileSize = progressMap.getLong("fileSize");
String ctxPath = request.getContextPath();
// INVOKE the VALIDATION ENGINE
try {
validationResult = validationEngineService.getValidationResult(uploadedFilePath, ctxPath);
resultJson = JSONObject.fromObject(validationResult);
} catch (Exception e) {
validationResult = null;
resultJson.put("errorMsg", e.toString());
}
try {
response.getWriter().print(resultJson);
response.flushBuffer();
} catch (Exception e) {
}
return null;
}
When you're working with HTTP, never expect a connection to take longer than half a minute.
Either use WebSockets or Comet (long polling).
See, there are many parties that might decide to stop your connection:
web browser, firewall/router/isp, web server (eg: Apache), server scripting language (eg: PHP)
The only time I disable this on the PHP side is when I want PHP to keep running even after the user stopped the connection (which, by the way, I have enable yet another setting).
Without some code, i'm not sure if i could help much but have you tried checking which event is triggering the ajax call? I made a silly mistake once by sending an ajax request on keydown, i later found out that instead of keydown , i needed to use keyup since thats the event triggered once the value of the textbox is set to the text + key recently entered.
So you could try a keyup or change the event in jQuery. If this is on localhost, its not a good sign....first determine whats causing the problem, the frontend or backend.
Things to do:
1) Change event type in jQuery
$("#elem").live("keyup",function() {//send req});
2) Check apache log. If your on ubuntu or something similar, you can go to /var/log/apache2/error.log
Is it possible to detect in JavaScript (in the browser) if a port is disabled by the firewall or router?
No, with pure javascript this is not possible (aside of making http requests to the specific ports, but those results mean little), what you can do however is check from the outside (in other words your server) whether the specific port is open. Another option would be to use a java applet or browser plugin which could do this for you if you really need it, in which case there are various open source tools which you could probably port if you have the necessary experience with those. Do note however that this isn't exactly user friendly. (Either way, it would be useful if you would describe the exact scenario where you need this, as there might be an altogether different solution.)
You can only see if the expected response is there or not.
One has to stay in the boundaries of HTTP when using javascript.
Of course you can send an Ajax request on whatever port of server and see if you get an error. If you want to check port for current machine then probably sending a request on "localhost:843" could help.
But the error could be of some other reasons and not necessarily firewall issue.
We need more information to help you out.
If you are flexible enough to use jQuery, then see this Answer by me. This will not only check the availability of port, but also whether a success response code 200 is coming from the remote (or any , I meant it supports cross-domain also) server. Also giving the solution here. I will be checking here for port 843.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="jquery-1.7.2-min.js"></script>
</head>
<body>
<script type"text/javascript">
var isAccessible = null;
function checkConnection() {
/*make sure you host a helloWorld HTML page in the following URL, so that requests are succeeded with 200 status code*/
var url = "http://yourserverIP:843/test/hello.html" ;
$.ajax({
url: url,
type: "get",
cache: false,
dataType: 'jsonp', // it is for supporting crossdomain
crossDomain : true,
asynchronous : false,
jsonpCallback: 'deadCode',
timeout : 1500, // set a timeout in milliseconds
complete : function(xhr, responseText, thrownError) {
if(xhr.status == "200") {
isAccessible = true;
success(); // yes response came, execute success()
}
else {
isAccessible = false;
failure(); // this will be executed after the request gets timed out due to blockage of ports/connections/IPs
}
}
});
}
$(document).ready( function() {
checkConnection(); // here I invoke the checking function
});
</script>
</body>
</html>
I am using jQuery Mobile to create a webapp to look at and update a CRM type system.
The mobile app sends update using jQuery.get and jQuery.post and they work fine when network connection is available.
How should I code or what can I use to queue the jQuery.get and jQuery.post calls when the network connection is not available so they are sent when it becomes available again.
Edit: ah poo, i just noticed you said 'jQuery Mobile', I initially read that as jquery for mobile lol. Ummm, this'll probably only work as long as jQM supports ajax the same as normal jquery
I had an idea with a secondary ajax request, but you shouldn't need that. Just set up your AJAX like this, and give it a timeout. If it takes > 4 (should be enough for a broadband connection, but some phones may need ~10-15) seconds for the server to respond, it'll just try the ajax request again up to retryLimit, which can be set, then changed later as well after the 50 times is up (i.e. should it send when the program is idle and has no data perhaps?). When it connects, it'll go to the success function, which will then send the data to the server.
So it'd be like:
$.ajax({
type: 'GET',
timeout: 4000,
tryCount : 0,
retryLimit: 50,
success:function(data) {
sendSavedData();
}
error: function(xhr, textStatus, errorThrown) {
if(textStatus == 'timeout') {
this.tryCount++;
if(this.tryCount <= this.retryLimit) {
$.ajax(this);
return;
}
var check = confirm('We have tried ' + this.retryLimit + ' times to do this and the server has not responded. Do you want to try again?');
if(check) {
this.timeout = 200000;
$.ajax(this);
return;
} else {
return;
}
}
}
});
How to detect the Internet connection is offline in JavaScript?
Almost all major browsers now support the window.navigator.onLine property, and the corresponding online and offline window events. Run the following code snippet to test it:
console.log('Initially ' + (window.navigator.onLine ? 'on' : 'off') + 'line');
window.addEventListener('online', () => console.log('Became online'));
window.addEventListener('offline', () => console.log('Became offline'));
document.getElementById('statusCheck').addEventListener('click', () => console.log('window.navigator.onLine is ' + window.navigator.onLine));
<button id="statusCheck">Click to check the <tt>window.navigator.onLine</tt> property</button><br /><br />
Check the console below for results:
Try setting your system or browser in offline/online mode and check the log or the window.navigator.onLine property for the value changes.
Note however this quote from Mozilla Documentation:
In Chrome and Safari, if the browser is not able to connect to a local area network (LAN) or a router, it is offline; all other conditions return true. So while you can assume that the browser is offline when it returns a false value, you cannot assume that a true value necessarily means that the browser can access the internet. You could be getting false positives, such as in cases where the computer is running a virtualization software that has virtual ethernet adapters that are always "connected." Therefore, if you really want to determine the online status of the browser, you should develop additional means for checking.
In Firefox and Internet Explorer, switching the browser to offline mode sends a false value. Until Firefox 41, all other conditions return a true value; since Firefox 41, on OS X and Windows, the value will follow the actual network connectivity.
(emphasis is my own)
This means that if window.navigator.onLine is false (or you get an offline event), you are guaranteed to have no Internet connection.
If it is true however (or you get an online event), it only means the system is connected to some network, at best. It does not mean that you have Internet access for example. To check that, you will still need to use one of the solutions described in the other answers.
I initially intended to post this as an update to Grant Wagner's answer, but it seemed too much of an edit, especially considering that the 2014 update was already not from him.
You can determine that the connection is lost by making failed XHR requests.
The standard approach is to retry the request a few times. If it doesn't go through, alert the user to check the connection, and fail gracefully.
Sidenote: To put the entire application in an "offline" state may lead to a lot of error-prone work of handling state.. wireless connections may come and go, etc. So your best bet may be to just fail gracefully, preserve the data, and alert the user.. allowing them to eventually fix the connection problem if there is one, and to continue using your app with a fair amount of forgiveness.
Sidenote: You could check a reliable site like google for connectivity, but this may not be entirely useful as just trying to make your own request, because while Google may be available, your own application may not be, and you're still going to have to handle your own connection problem. Trying to send a ping to google would be a good way to confirm that the internet connection itself is down, so if that information is useful to you, then it might be worth the trouble.
Sidenote: Sending a Ping could be achieved in the same way that you would make any kind of two-way ajax request, but sending a ping to google, in this case, would pose some challenges. First, we'd have the same cross-domain issues that are typically encountered in making Ajax communications. One option is to set up a server-side proxy, wherein we actually ping google (or whatever site), and return the results of the ping to the app. This is a catch-22 because if the internet connection is actually the problem, we won't be able to get to the server, and if the connection problem is only on our own domain, we won't be able to tell the difference. Other cross-domain techniques could be tried, for example, embedding an iframe in your page which points to google.com, and then polling the iframe for success/failure (examine the contents, etc). Embedding an image may not really tell us anything, because we need a useful response from the communication mechanism in order to draw a good conclusion about what's going on. So again, determining the state of the internet connection as a whole may be more trouble than it's worth. You'll have to weight these options out for your specific app.
IE 8 will support the window.navigator.onLine property.
But of course that doesn't help with other browsers or operating systems. I predict other browser vendors will decide to provide that property as well given the importance of knowing online/offline status in Ajax applications.
Until that happens, either XHR or an Image() or <img> request can provide something close to the functionality you want.
Update (2014/11/16)
Major browsers now support this property, but your results will vary.
Quote from Mozilla Documentation:
In Chrome and Safari, if the browser is not able to connect to a local area network (LAN) or a router, it is offline; all other conditions return true. So while you can assume that the browser is offline when it returns a false value, you cannot assume that a true value necessarily means that the browser can access the internet. You could be getting false positives, such as in cases where the computer is running a virtualization software that has virtual ethernet adapters that are always "connected." Therefore, if you really want to determine the online status of the browser, you should develop additional means for checking.
In Firefox and Internet Explorer, switching the browser to offline mode sends a false value. All other conditions return a true value.
if(navigator.onLine){
alert('online');
} else {
alert('offline');
}
There are a number of ways to do this:
AJAX request to your own website. If that request fails, there's a good chance it's the connection at fault. The JQuery documentation has a section on handling failed AJAX requests. Beware of the Same Origin Policy when doing this, which may stop you from accessing sites outside your domain.
You could put an onerror in an img, like <img src="http://www.example.com/singlepixel.gif" onerror="alert('Connection dead');" />.
This method could also fail if the source image is moved / renamed, and would generally be an inferior choice to the ajax option.
So there are several different ways to try and detect this, none perfect, but in the absence of the ability to jump out of the browser sandbox and access the user's net connection status directly, they seem to be the best options.
As olliej said, using the navigator.onLine browser property is preferable than sending network requests and, accordingly with developer.mozilla.org/En/Online_and_offline_events, it is even supported by old versions of Firefox and IE.
Recently, the WHATWG has specified the addition of the online and offline events, in case you need to react on navigator.onLine changes.
Please also pay attention to the link posted by Daniel Silveira which points out that relying on those signal/property for syncing with the server is not always a good idea.
You can use $.ajax()'s error callback, which fires if the request fails. If textStatus equals the string "timeout" it probably means connection is broken:
function (XMLHttpRequest, textStatus, errorThrown) {
// typically only one of textStatus or errorThrown
// will have info
this; // the options for this ajax request
}
From the doc:
Error: A function to be called if the request
fails. The function is passed three
arguments: The XMLHttpRequest object,
a string describing the type of error
that occurred and an optional
exception object, if one occurred.
Possible values for the second
argument (besides null) are "timeout",
"error", "notmodified" and
"parsererror". This is an Ajax Event
So for example:
$.ajax({
type: "GET",
url: "keepalive.php",
success: function(msg){
alert("Connection active!")
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
if(textStatus == 'timeout') {
alert('Connection seems dead!');
}
}
});
window.navigator.onLine
is what you looking for, but few things here to add, first, if it's something on your app which you want to keep checking (like to see if the user suddenly go offline, which correct in this case most of the time, then you need to listen to change also), for that you add event listener to window to detect any change, for checking if the user goes offline, you can do:
window.addEventListener("offline",
()=> console.log("No Internet")
);
and for checking if online:
window.addEventListener("online",
()=> console.log("Connected Internet")
);
The HTML5 Application Cache API specifies navigator.onLine, which is currently available in the IE8 betas, WebKit (eg. Safari) nightlies, and is already supported in Firefox 3
I had to make a web app (ajax based) for a customer who works a lot with schools, these schools have often a bad internet connection I use this simple function to detect if there is a connection, works very well!
I use CodeIgniter and Jquery:
function checkOnline() {
setTimeout("doOnlineCheck()", 20000);
}
function doOnlineCheck() {
//if the server can be reached it returns 1, other wise it times out
var submitURL = $("#base_path").val() + "index.php/menu/online";
$.ajax({
url : submitURL,
type : "post",
dataType : "msg",
timeout : 5000,
success : function(msg) {
if(msg==1) {
$("#online").addClass("online");
$("#online").removeClass("offline");
} else {
$("#online").addClass("offline");
$("#online").removeClass("online");
}
checkOnline();
},
error : function() {
$("#online").addClass("offline");
$("#online").removeClass("online");
checkOnline();
}
});
}
an ajax call to your domain is the easiest way to detect if you are offline
$.ajax({
type: "HEAD",
url: document.location.pathname + "?param=" + new Date(),
error: function() { return false; },
success: function() { return true; }
});
this is just to give you the concept, it should be improved.
E.g. error=404 should still mean that you online
I know this question has already been answered but i will like to add my 10 cents explaining what's better and what's not.
Window.navigator.onLine
I noticed some answers spoke about this option but they never mentioned anything concerning the caveat.
This option involves the use of "window.navigator.onLine" which is a property under Browser Navigator Interface available on most modern browsers. It is really not a viable option for checking internet availability because firstly it is browser centric and secondly most browsers implement this property differently.
In Firefox: The property returns a boolean value, with true meaning online and false meaning offline but the caveat here is that
"the value is only updated when the user follows links or when a script requests a remote page." Hence if the user goes offline and
you query the property from a js function or script, the property will
always return true until the user follows a link.
In Chrome and Safari: If the browser is not able to connect to a local area network (LAN) or a router, it is offline; all other
conditions return true. So while you can assume that the browser is
offline when it returns a false value, you cannot assume that a true
value necessarily means that the browser can access the internet. You
could be getting false positives, such as in cases where the computer
is running a virtualization software that has virtual ethernet
adapters that are always "connected".
The statements above is simply trying to let you know that browsers alone cannot tell. So basically this option is unreliable.
Sending Request to Own Server Resource
This involves making HTTP request to your own server resource and if reachable assume internet availability else the user is offline. There are some few caveats to this option.
No server availability is 100% reliant, hence if for some reason your server is not reachable it would be falsely assumed that the user is offline whereas they're connected to the internet.
Multiple request to same resource can return cached response making the http response result unreliable.
If you agree your server is always online then you can go with this option.
Here is a simple snippet to fetch own resource:
// This fetches your website's favicon, so replace path with favicon url
// Notice the appended date param which helps prevent browser caching.
fetch('/favicon.ico?d='+Date.now())
.then(response => {
if (!response.ok)
throw new Error('Network response was not ok');
// At this point we can safely assume the user has connection to the internet
console.log("Internet connection available");
})
.catch(error => {
// The resource could not be reached
console.log("No Internet connection", error);
});
Sending Request to Third-Party Server Resource
We all know CORS is a thing.
This option involves making HTTP request to an external server resource and if reachable assume internet availability else the user is offline. The major caveat to this is the Cross-origin resource sharing which act as a limitation. Most reputable websites blocks CORS requests but for some you can have your way.
Below a simple snippet to fetch external resource, same as above but with external resource url:
// Firstly you trigger a resource available from a reputable site
// For demo purpose you can use the favicon from MSN website
// Also notice the appended date param which helps skip browser caching.
fetch('https://static-global-s-msn-com.akamaized.net/hp-neu/sc/2b/a5ea21.ico?d='+Date.now())
.then(response => {
// Check if the response is successful
if (!response.ok)
throw new Error('Network response was not ok');
// At this point we can safely say the user has connection to the internet
console.log("Internet available");
})
.catch(error => {
// The resource could not be reached
console.log("No Internet connection", error);
});
So, Finally for my personal project i went with the 2nd option which involves requesting own server resource because basically there are many factors to tell if there is "Internet Connection" on a user's device, not just from your website container alone nor from a limited browser api.
Remember your users can also be in an environment where some websites or resources are blocked, prohibited and not accessible which in turn affects the logic of connectivity check. The best bet will be:
Try to access a resource on your own server because this is your users environment (Typically i use website's favicon because the response is very light and it is not frequently updated).
If there is no connection to the resource, simply say "Error in connection" or "Connection lost" when you need to notify the user rather than assume a broad "No internet connection" which depends on many factors.
I think it is a very simple way.
var x = confirm("Are you sure you want to submit?");
if (x) {
if (navigator.onLine == true) {
return true;
}
alert('Internet connection is lost');
return false;
}
return false;
The problem of some methods like navigator.onLine is that they are not compatible with some browsers and mobile versions, an option that helped me a lot was to use the classic XMLHttpRequest method and also foresee the possible case that the file was stored in cache with response XMLHttpRequest.status is greater than 200 and less than 304.
Here is my code:
var xhr = new XMLHttpRequest();
//index.php is in my web
xhr.open('HEAD', 'index.php', true);
xhr.send();
xhr.addEventListener("readystatechange", processRequest, false);
function processRequest(e) {
if (xhr.readyState == 4) {
//If you use a cache storage manager (service worker), it is likely that the
//index.php file will be available even without internet, so do the following validation
if (xhr.status >= 200 && xhr.status < 304) {
console.log('On line!');
} else {
console.log('Offline :(');
}
}
}
I was looking for a client-side solution to detect if the internet was down or my server was down. The other solutions I found always seemed to be dependent on a 3rd party script file or image, which to me didn't seem like it would stand the test of time. An external hosted script or image could change in the future and cause the detection code to fail.
I've found a way to detect it by looking for an xhrStatus with a 404 code. In addition, I use JSONP to bypass the CORS restriction. A status code other than 404 shows the internet connection isn't working.
$.ajax({
url: 'https://www.bing.com/aJyfYidjSlA' + new Date().getTime() + '.html',
dataType: 'jsonp',
timeout: 5000,
error: function(xhr) {
if (xhr.status == 404) {
//internet connection working
}
else {
//internet is down (xhr.status == 0)
}
}
});
How about sending an opaque http request to google.com with no-cors?
fetch('https://google.com', {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'no-cors',
}).then((result) => {
console.log(result)
}).catch(e => {
console.error(e)
})
The reason for setting no-cors is that I was receiving cors errors even when disbaling the network connection on my pc. So I was getting cors blocked with or without an internet connection. Adding the no-cors makes the request opaque which apperantly seems to bypass cors and allows me to just simply check if I can connect to Google.
FYI: Im using fetch here for making the http request.
https://www.npmjs.com/package/fetch
My way.
<!-- the file named "tt.jpg" should exist in the same directory -->
<script>
function testConnection(callBack)
{
document.getElementsByTagName('body')[0].innerHTML +=
'<img id="testImage" style="display: none;" ' +
'src="tt.jpg?' + Math.random() + '" ' +
'onerror="testConnectionCallback(false);" ' +
'onload="testConnectionCallback(true);">';
testConnectionCallback = function(result){
callBack(result);
var element = document.getElementById('testImage');
element.parentNode.removeChild(element);
}
}
</script>
<!-- usage example -->
<script>
function myCallBack(result)
{
alert(result);
}
</script>
<a href=# onclick=testConnection(myCallBack);>Am I online?</a>
Just use navigator.onLine if this is true then you're online else offline
request head in request error
$.ajax({
url: /your_url,
type: "POST or GET",
data: your_data,
success: function(result){
//do stuff
},
error: function(xhr, status, error) {
//detect if user is online and avoid the use of async
$.ajax({
type: "HEAD",
url: document.location.pathname,
error: function() {
//user is offline, do stuff
console.log("you are offline");
}
});
}
});
You can try this will return true if network connected
function isInternetConnected(){return navigator.onLine;}
Here is a snippet of a helper utility I have. This is namespaced javascript:
network: function() {
var state = navigator.onLine ? "online" : "offline";
return state;
}
You should use this with method detection else fire off an 'alternative' way of doing this. The time is fast approaching when this will be all that is needed. The other methods are hacks.
There are 2 answers forthis for two different senarios:-
If you are using JavaScript on a website(i.e; or any front-end part)
The simplest way to do it is:
<h2>The Navigator Object</h2>
<p>The onLine property returns true if the browser is online:</p>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = "navigator.onLine is " + navigator.onLine;
</script>
But if you're using js on server side(i.e; node etc.), You can determine that the connection is lost by making failed XHR requests.
The standard approach is to retry the request a few times. If it doesn't go through, alert the user to check the connection, and fail gracefully.
I'm using $.post() to call a servlet using Ajax and then using the resulting HTML fragment to replace a div element in the user's current page. However, if the session times out, the server sends a redirect directive to send the user to the login page. In this case, jQuery is replacing the div element with the contents of the login page, forcing the user's eyes to witness a rare scene indeed.
How can I manage a redirect directive from an Ajax call with jQuery 1.2.6?
I read this question and implemented the approach that has been stated regarding setting the response HTTP status code to 278 in order to avoid the browser transparently handling the redirects. Even though this worked, I was a little dissatisfied as it is a bit of a hack.
After more digging around, I ditched this approach and used JSON. In this case, all responses to AJAX requests have the status code 200 and the body of the response contains a JSON object that is constructed on the server. The JavaScript on the client can then use the JSON object to decide what it needs to do.
I had a similar problem to yours. I perform an AJAX request that has 2 possible responses: one that redirects the browser to a new page and one that replaces an existing HTML form on the current page with a new one. The jQuery code to do this looks something like:
$.ajax({
type: "POST",
url: reqUrl,
data: reqBody,
dataType: "json",
success: function(data, textStatus) {
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
} else {
// data.form contains the HTML for the replacement form
$("#myform").replaceWith(data.form);
}
}
});
The JSON object "data" is constructed on the server to have 2 members: data.redirect and data.form. I found this approach to be much better.
I solved this issue by:
Adding a custom header to the response:
public ActionResult Index(){
if (!HttpContext.User.Identity.IsAuthenticated)
{
HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
}
return View();
}
Binding a JavaScript function to the ajaxSuccess event and checking to see if the header exists:
$(document).ajaxSuccess(function(event, request, settings) {
if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
window.location = '/';
}
});
No browsers handle 301 and 302 responses correctly. And in fact the standard even says they should handle them "transparently" which is a MASSIVE headache for Ajax Library vendors. In Ra-Ajax we were forced into using HTTP response status code 278 (just some "unused" success code) to handle transparently redirects from the server...
This really annoys me, and if someone here have some "pull" in W3C I would appreciate that you could let W3C know that we really need to handle 301 and 302 codes ourselves...! ;)
The solution that was eventually implemented was to use a wrapper for the callback function of the Ajax call and in this wrapper check for the existence of a specific element on the returned HTML chunk. If the element was found then the wrapper executed a redirection. If not, the wrapper forwarded the call to the actual callback function.
For example, our wrapper function was something like:
function cbWrapper(data, funct){
if($("#myForm", data).length > 0)
top.location.href="login.htm";//redirection
else
funct(data);
}
Then, when making the Ajax call we used something like:
$.post("myAjaxHandler",
{
param1: foo,
param2: bar
},
function(data){
cbWrapper(data, myActualCB);
},
"html"
);
This worked for us because all Ajax calls always returned HTML inside a DIV element that we use to replace a piece of the page. Also, we only needed to redirect to the login page.
I like Timmerz's method with a slight twist of lemon. If you ever get returned contentType of text/html when you're expecting JSON, you are most likely being redirected. In my case, I just simply reload the page, and it gets redirected to the login page. Oh, and check that the jqXHR status is 200, which seems silly, because you are in the error function, right? Otherwise, legitimate error cases will force an iterative reload (oops)
$.ajax(
error: function (jqXHR, timeout, message) {
var contentType = jqXHR.getResponseHeader("Content-Type");
if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
// assume that our login has expired - reload our current page
window.location.reload();
}
});
Use the low-level $.ajax() call:
$.ajax({
url: "/yourservlet",
data: { },
complete: function(xmlHttp) {
// xmlHttp is a XMLHttpRquest object
alert(xmlHttp.status);
}
});
Try this for a redirect:
if (xmlHttp.code != 200) {
top.location.href = '/some/other/page';
}
I just wanted to share my approach as this might it might help someone:
I basically included a JavaScript module which handles the authentication stuff like displaying the username and also this case handling the redirect to the login page.
My scenario: We basically have an ISA server in between which listens to all requests and responds with a 302 and a location header to our login page.
In my JavaScript module my initial approach was something like
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
The problem (as many here already mentioned) is that the browser handles the redirect by itself wherefore my ajaxComplete callback got never called, but instead I got the response of the already redirected Login page which obviously was a status 200. The problem: how do you detect whether the successful 200 response is your actual login page or just some other arbitrary page??
The solution
Since I was not able to capture 302 redirect responses, I added a LoginPage header on my login page which contained the url of the login page itself. In the module I now listen for the header and do a redirect:
if(xhr.status === 200){
var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
window.location.replace(loginPageRedirectHeader);
}
}
...and that works like charm :). You might wonder why I include the url in the LoginPage header...well basically because I found no way of determining the url of GET resulting from the automatic location redirect from the xhr object...
I know this topic is old, but I'll give yet another approach I've found and previously described here. Basically I'm using ASP.MVC with WIF (but this is not really important for the context of this topic - answer is adequate no matter which frameworks are used. The clue stays unchanged - dealing with issues related to authentication failures while performing ajax requests).
The approach shown below can be applied to all ajax requests out of the box (if they do not redefine beforeSend event obviously).
$.ajaxSetup({
beforeSend: checkPulse,
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.open();
document.write(XMLHttpRequest.responseText);
document.close();
}
});
Before any ajax request is performed CheckPulse method is invoked (the controller method which can be anything simplest):
[Authorize]
public virtual void CheckPulse() {}
If user is not authenticated (token has expired) such method cannot be accessed (protected by Authorize attribute). Because the framework handles authentication, while token expires, it puts http status 302 to the response. If you don't want your browser to handle 302 response transparently, catch it in Global.asax and change response status - for example to 200 OK. Additionally, add header, which instructs you to process such response in special way (later at the client side):
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302
&& (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
{
Context.Response.StatusCode = 200;
Context.Response.AddHeader("REQUIRES_AUTH", "1");
}
}
Finally at the client side check for such custom header. If present - full redirection to logon page should be done (in my case window.location is replaced by url from request which is handled automatically by my framework).
function checkPulse(XMLHttpRequest) {
var location = window.location.href;
$.ajax({
url: "/Controller/CheckPulse",
type: 'GET',
async: false,
beforeSend: null,
success:
function (result, textStatus, xhr) {
if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
XMLHttpRequest.abort(); // terminate further ajax execution
window.location = location;
}
}
});
}
I think a better way to handle this is to leverage the existing HTTP protocol response codes, specifically 401 Unauthorized.
Here is how I solved it:
Server side: If session expires, and request is ajax. send a 401 response code header
Client side: Bind to the ajax events
$('body').bind('ajaxSuccess',function(event,request,settings){
if (401 == request.status){
window.location = '/users/login';
}
}).bind('ajaxError',function(event,request,settings){
if (401 == request.status){
window.location = '/users/login';
}
});
IMO this is more generic and you are not writing some new custom spec/header. You also should not have to modify any of your existing ajax calls.
Edit: Per #Rob's comment below, 401 (the HTTP status code for authentication errors) should be the indicator. See 403 Forbidden vs 401 Unauthorized HTTP responses for more detail. With that being said some web frameworks use 403 for both authentication AND authorization errors - so adapt accordingly. Thanks Rob.
I resolved this issue like this:
Add a middleware to process response, if it is a redirect for an ajax request, change the response to a normal response with the redirect url.
class AjaxRedirect(object):
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
r = HttpResponse(json.dumps({'redirect': response['Location']}))
return r
return response
Then in ajaxComplete, if the response contains redirect, it must be a redirect, so change the browser's location.
$('body').ajaxComplete(function (e, xhr, settings) {
if (xhr.status == 200) {
var redirect = null;
try {
redirect = $.parseJSON(xhr.responseText).redirect;
if (redirect) {
window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
}
} catch (e) {
return;
}
}
}
Another solution I found (especially useful if you want to set a global behaviour) is to use the $.ajaxsetup() method together with the statusCode property. Like others pointed out, don't use a redirect statuscode (3xx), instead use a 4xx statuscode and handle the redirect client-side.
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
Replace 400 with the statuscode you want to handle. Like already mentioned 401 Unauthorized could be a good idea. I use the 400 since it's very unspecific and I can use the 401 for more specific cases (like wrong login credentials). So instead of redirecting directly your backend should return a 4xx error-code when the session timed out and you you handle the redirect client-side. Works perfect for me even with frameworks like backbone.js
I have a simple solution that works for me, no server code change needed...just add a tsp of nutmeg...
$(document).ready(function ()
{
$(document).ajaxSend(
function(event,request,settings)
{
var intercepted_success = settings.success;
settings.success = function( a, b, c )
{
if( request.responseText.indexOf( "<html>" ) > -1 )
window.location = window.location;
else
intercepted_success( a, b, c );
};
});
});
I check the presence of html tag, but you can change the indexOf to search for whatever unique string exists in your login page...
Most of the given solutions use a workaround, using an extra header or an inappropiate HTTP code. Those solutions will most probably work but feel a bit 'hacky'. I've come up with another solution.
We're using WIF which is configured to redirect (passiveRedirectEnabled="true") on a 401 response. The redirect is usefull when handling normal requests but won't work for AJAX requests (since browsers won't execute the 302/redirect).
Using the following code in your global.asax you can disable the redirect for AJAX requests:
void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
{
string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];
if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
{
e.RedirectToIdentityProvider = false;
}
}
This allows you to return 401 responses for AJAX requests, which your javascript can then handle by reloading the page. Reloading the page will throw a 401 which will be handled by WIF (and WIF will redirect the user to the login page).
An example javascript to handle 401 errors:
$(document).ajaxError(function (event, jqxhr, settings, exception) {
if (jqxhr.status == 401) { //Forbidden, go to login
//Use a reload, WIF will redirect to Login
location.reload(true);
}
});
This problem may appear then using ASP.NET MVC RedirectToAction method. To prevent form displaying the response in div you can simply do some kind of ajax response filter for incomming responses with $.ajaxSetup. If the response contains MVC redirection you can evaluate this expression on JS side. Example code for JS below:
$.ajaxSetup({
dataFilter: function (data, type) {
if (data && typeof data == "string") {
if (data.indexOf('window.location') > -1) {
eval(data);
}
}
return data;
}
});
If data is: "window.location = '/Acount/Login'" above filter will catch that and evaluate to make the redirection instead of letting the data to be displayed.
Putting together what Vladimir Prudnikov and Thomas Hansen said:
Change your server-side code to detect if it's an XHR. If it is, set the response code of the redirect to 278.
In django:
if request.is_ajax():
response.status_code = 278
This makes the browser treat the response as a success, and hand it to your Javascript.
In your JS, make sure the form submission is via Ajax, check the response code and redirect if needed:
$('#my-form').submit(function(event){
event.preventDefault();
var options = {
url: $(this).attr('action'),
type: 'POST',
complete: function(response, textStatus) {
if (response.status == 278) {
window.location = response.getResponseHeader('Location')
}
else { ... your code here ... }
},
data: $(this).serialize(),
};
$.ajax(options);
});
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if(responseStatus=="success"){
window.location= "adminIndex.html";
}
});
}
</script>
Let me just quote again the problem as described by #Steg
I had a similar problem to yours. I perform an ajax request that has 2
possible responses: one that redirects the browser to a new page and
one that replaces an existing HTML form on the current page with a new
one.
IMHO this is a real challenge and will have to be officially extended to the current HTTP standards.
I believe the new Http Standard will be to use a new status-code.
meaning: currently 301/302 tells the browser to go and fetch the content of this request to a new location.
In the extended standard, it will say that if the response status: 308 (just an example), then the browser should redirect the main page to the location provided.
That being said; I'm inclined to already mimic this future behavior, and therefore when a document.redirect is needed, I have the server respond as:
status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html
When JS gets the "status: 204", it checks for the existence of the x-status: 308 header, and does a document.redirect to the page provided in the location header.
Does this make any sense to you?
Try
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
Put it on the login page. If it was loaded in a div on the main page, it will redirect til the login page. "#site" is a id of a div which is located on all pages except login page.
While the answers seem to work for people if you're using Spring Security I have found extending LoginUrlAuthenticationEntryPoint and adding specific code to handle AJAX more robust. Most of the examples intercept all redirects not just authentication failures. This was undesirable for the project I work on. You may find the need to also extend ExceptionTranslationFilter and override the "sendStartAuthentication" method to remove the caching step if you don't want the failed AJAX request cached.
Example AjaxAwareAuthenticationEntryPoint:
public class AjaxAwareAuthenticationEntryPoint extends
LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
super(loginUrl);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if (isAjax(request)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
} else {
super.commence(request, response, authException);
}
}
public static boolean isAjax(HttpServletRequest request) {
return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
Sources:
1, 2
I solved this by putting the following in my login.php page.
<script type="text/javascript">
if (top.location.href.indexOf('login.php') == -1) {
top.location.href = '/login.php';
}
</script>
Some might find the below useful:
I wanted clients to be redirected to the login page for any rest-action that is sent without an authorization token. Since all of my rest-actions are Ajax based, I needed a good generic way to redirect to the login page instead of handling the Ajax success function.
This is what I've done:
On any Ajax request my server will return a Json 200 response "NEED TO AUTHENTICATE" (if the client needs to authenticate).
Simple example in Java (server side):
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);
public static final String COOKIE_NAME = "token_cookie";
#Override
public void filter(ContainerRequestContext context) throws IOException {
// Check if it has a cookie.
try {
Map<String, Cookie> cookies = context.getCookies();
if (!cookies.containsKey(COOKIE_NAME)) {
m_logger.debug("No cookie set - redirect to login page");
throw new AuthenticationException();
}
}
catch (AuthenticationException e) {
context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
}
}
}
In my Javascript I've added the following code:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var originalSuccess = options.success;
options.success = function(data) {
if (data == "NEED TO AUTHENTICATE") {
window.location.replace("/login.html");
}
else {
originalSuccess(data);
}
};
});
And that's about it.
in the servlet you should put
response.setStatus(response.SC_MOVED_PERMANENTLY);
to send the '301' xmlHttp status you need for a redirection...
and in the $.ajax function you should not use the .toString() function..., just
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
the problem is it is not very flexible, you can't decide where you want to redirect..
redirecting through the servlets should be the best way. but i still can not find the right way to do it.
I just wanted to latch on to any ajax requests for the entire page. #SuperG got me started. Here is what I ended up with:
// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
switch(request.status) {
case 301: case 404: case 403:
window.location.replace("http://mysite.tld/login");
break;
}
});
I wanted to specifically check for certain http status codes to base my decision on. However, you can just bind to ajaxError to get anything other than success (200 only perhaps?) I could have just written:
$('body').bind('ajaxError', function(event,request,settings){
window.location.replace("http://mysite.tld/login");
}
If you also want to pass the values then you can also set the session variables and access
Eg:
In your jsp you can write
<% HttpSession ses = request.getSession(true);
String temp=request.getAttribute("what_you_defined"); %>
And then you can store this temp value in your javascript variable and play around
I didn't have any success with the header solution - they were never picked up in my ajaxSuccess / ajaxComplete method. I used Steg's answer with the custom response, but I modified the JS side some. I setup a method that I call in each function so I can use standard $.get and $.post methods.
function handleAjaxResponse(data, callback) {
//Try to convert and parse object
try {
if (jQuery.type(data) === "string") {
data = jQuery.parseJSON(data);
}
if (data.error) {
if (data.error == 'login') {
window.location.reload();
return;
}
else if (data.error.length > 0) {
alert(data.error);
return;
}
}
}
catch(ex) { }
if (callback) {
callback(data);
}
}
Example of it in use...
function submitAjaxForm(form, url, action) {
//Lock form
form.find('.ajax-submit').hide();
form.find('.loader').show();
$.post(url, form.serialize(), function (d) {
//Unlock form
form.find('.ajax-submit').show();
form.find('.loader').hide();
handleAjaxResponse(d, function (data) {
// ... more code for if auth passes ...
});
});
return false;
}
Finally, I solve the problem by adding a custom HTTP Header. Just before response for every request in server side, i add the current requested url to response's header.
My application type on server is Asp.Net MVC, and it has a good place to do it. in Global.asax i implemented the Application_EndRequest event so:
public class MvcApplication : System.Web.HttpApplication
{
// ...
// ...
protected void Application_EndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
}
}
It works perfect for me! Now in every response of the JQuery $.post i have the requested url and also other response headers which comes as result of POST method by status 302, 303 ,... .
and other important thing is that there is no need to modify code on server side nor client side.
and the next is the ability to get access to the other information of post action such errors, messages, and ..., In this way.
I posted this, maybe help someone :)
I was having this problem on a django app I'm tinkering with (disclaimer: I'm tinkering to learn, and am in no way an expert). What I wanted to do was use jQuery ajax to send a DELETE request to a resource, delete it on the server side, then send a redirect back to (basically) the homepage. When I sent HttpResponseRedirect('/the-redirect/') from the python script, jQuery's ajax method was receiving 200 instead of 302. So, what I did was to send a response of 300 with:
response = HttpResponse(status='300')
response['Location'] = '/the-redirect/'
return response
Then I sent/handled the request on the client with jQuery.ajax like so:
<button onclick="*the-jquery*">Delete</button>
where *the-jquery* =
$.ajax({
type: 'DELETE',
url: '/resource-url/',
complete: function(jqxhr){
window.location = jqxhr.getResponseHeader('Location');
}
});
Maybe using 300 isn't "right", but at least it worked just like I wanted it to.
PS :this was a huge pain to edit on the mobile version of SO. Stupid ISP put my service cancellation request through right when I was done with my answer!
You can also hook XMLHttpRequest send prototype. This will work for all sends (jQuery/dojo/etc) with one handler.
I wrote this code to handle a 500 page expired error, but it should work just as well to trap a 200 redirect. Ready the wikipedia entry on XMLHttpRequest onreadystatechange about the meaning of readyState.
// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
//console.dir( this );
this.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
try {
document.documentElement.innerHTML = this.responseText;
} catch(error) {
// IE makes document.documentElement read only
document.body.innerHTML = this.responseText;
}
}
};
oldXMLHttpRequestSend.apply(this, arguments);
}
I got a working solulion using the answers from #John and #Arpad link and #RobWinch link
I use Spring Security 3.2.9 and jQuery 1.10.2.
Extend Spring's class to cause 4XX response only from AJAX requests:
public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
super(loginFormUrl);
}
// For AJAX requests for user that isn't logged in, need to return 403 status.
// For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
#Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException)
throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
applicationContext-security.xml
<security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
<security:form-login login-page='/login.jsp' default-target-url='/index.jsp'
authentication-failure-url="/login.jsp?error=true"
/>
<security:access-denied-handler error-page="/errorPage.jsp"/>
<security:logout logout-success-url="/login.jsp?logout" />
...
<bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
<constructor-arg value="/login.jsp" />
</bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<property name="requestMatcher">
<bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
<constructor-arg>
<bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
</constructor-arg>
<constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
<property name="useEquals" value="true"/>
</bean>
</constructor-arg>
</bean>
</property>
</bean>
In my JSPs, add a global AJAX error handler as shown here
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
Also, remove existing error handlers from AJAX calls in JSP pages:
var str = $("#viewForm").serialize();
$.ajax({
url: "get_mongoDB_doc_versions.do",
type: "post",
data: str,
cache: false,
async: false,
dataType: "json",
success: function(data) { ... },
// error: function (jqXHR, textStatus, errorStr) {
// if(textStatus != null)
// alert(textStatus);
// else if(errorStr != null)
// alert(errorStr);
// else
// alert("error");
// }
});
I hope it helps others.
Update1
I found that I needed to add the option (always-use-default-target="true") to the form-login config.
This was needed since after an AJAX request gets redirected to the login page (due to expired session), Spring remembers the previous AJAX request and auto redirects to it after login. This causes the returned JSON to be displayed on the browser page. Of course, not what I want.
Update2
Instead of using always-use-default-target="true", use #RobWinch example of blocking AJAX requests from the requstCache. This allows normal links to be redirected to their original target after login, but AJAX go to the home page after login.
As alternative to ajax, there is a new Fetch API being developed, which allows manual redirect handling. You need to check if the current browser support is enough for your needs.