115

I use the jQuery ajax functions to access a web service, but the server, instead of returning a response with a status code describing a problem, the request is redirected to a page with a 200 header, describing the problem. I can't make any changes to this, so I need to solve it on the client somehow.

Example: A request goes to some URL which is not found, so I receive a 302 Redirect to another location. A new request is sent, and I receive a 200 OK, thus preventing the error callback to fire.

Is there some way I can prevent the ajax request to follow redirects and instead invoke a callback, preferably the error method. Alternatively, is it possible to detect if a redirect has happened in the client?

Jørgen
  • 8,820
  • 9
  • 47
  • 67
  • This could help: http://stackoverflow.com/questions/1804928/handle-jquery-ajax-redirect – PiTheNumber Nov 23 '11 at 07:53
  • 9
    It is odd that your backend is reporting 302 for a not found, instead of a 404. – Jake Feasel Nov 23 '11 at 07:59
  • 1
    Possible duplicate of [Prevent redirection of Xmlhttprequest](http://stackoverflow.com/questions/228225/prevent-redirection-of-xmlhttprequest) – user Feb 15 '16 at 17:10

9 Answers9

105

I find your question interesting, but the problem in whole seems me more a misunderstanding. At least I'll try to explain my understanding of the problem.

The silent (transparent) redirection is the part of XMLHttpRequest specification (see here especially the words "... transparently follow the redirect ..."). The standard mention only that the user agent (the web browser) can prevent or notify of certain kinds of automatic redirections, but it's not a part of XMLHttpRequest. It's the part of HTTP client configuration (OS configuration) or the web browser configuration. So jQuery.ajax can't have any option where you can prevent redirection.

You can see that HTTP redirection is the part of HTTP protocol and not a part of XMLHttpRequest. So it's on the another level of abstraction or the network stack. For example the data from the XMLHttpRequest can be retrieved from the HTTP proxy or from the local browser cache, and it's the part of HTTP protocol. Mostly the server which provide the data and not the client can influence on caching.

You can compare the requirement from your question with the requirement to prevent changing of IP address of the web server or the changing of the IP route during the communication. All the things can be interesting in some scenarios, but there are parts of another level of the communication stack and can't be managed by jQuery.ajax or XMLHttpRequest.

The XMLHttpRequest standard say that the client configuration can have options which prevent redirection. In case of "Microsoft world", which I better know, you can look at WinHttpSetOption function which can be used to set WINHTTP_OPTION_DISABLE_FEATURE option with the WINHTTP_DISABLE_REDIRECTS value. Another way are the usage of WINHTTP_OPTION_REDIRECT_POLICY option with the WINHTTP_OPTION_REDIRECT_POLICY_NEVER value. One more feature which one can use in Windows is the WinHttpSetStatusCallback function which can set callback function received some notifications like WINHTTP_CALLBACK_FLAG_REDIRECT.

So it's do possible to implement your requirements in general, but the solution will be probably not independent from the operation system or the web browser and be not on the level of jQuery.ajax or XMLHttpRequest.

Oleg
  • 220,925
  • 34
  • 403
  • 798
  • 3
    Excellent answer with great insight! I have some experience with curl, for which you can set similar flags as you mention. I was hoping that such instructions could be passed to the browser, but from your answer, I understand that the expected behavior would be to follow the redirects as if the initial request was sent to the location assigned by the server. – Jørgen Nov 23 '11 at 12:38
  • @Jørgen: You are welcome! In the most scenarios the redirection is not a problem at all. You don't describe the context in which you use jQuery.ajax and whether you have full control over the web server to which you send the request. So it's difficult to give you other advises which can really solve your problem. – Oleg Nov 23 '11 at 12:55
  • I don't control the server side, I'm afraid. I guess my best option is to analyze or validate the response content. – Jørgen Nov 23 '11 at 12:59
  • @Jørgen: Why is the redirection a problem in your case? If the server moved some page on another location temporary or permanently it can redirect your original request to the new location. It will be absolutely OK. An administrator can configure web server to do redirection for example during restore operation on the server or any other support work. If you ask the server by URL having DNS the administrator can change IP mapping to another server. He can do HTTP redirect in the same way as DNS reconfiguration. What is your problem? – Oleg Nov 23 '11 at 13:14
  • My problem is that the redirect goes to a generic error page. I know the server should be set up differently, but for now I will need to find a solution for this situation as it is. – Jørgen Nov 23 '11 at 13:21
  • @Jørgen: HTTP redirection can be for example very important for authentication scenarios. The server can redirect you to your authorization server. The server can be Domain controller or Windows Federation Services of *your organization*. You will get your Active Directory password and authenticate yourself. Then you will receive the access token in the cookie generated for the server from which you tried to get the data originally. Then the server will know your permission and your roles and will get you the data which you requested. All this is the part of WS-Trust and OAuth standards. – Oleg Nov 23 '11 at 13:28
  • Certain browser implementations (safari), however, have been known to modify, or add headers to a redirected request which can cause errors if the added headers aren't compatible with the server's policy. – Tyler Biscoe Jul 21 '14 at 22:00
  • I feel that this post is misleading, in a way it is trying to say that server automatically "makes" a second request to itself while responding to original request and appends response from the second autorequest to the redirect response. It's like server returns two responses at once, but HTTP 1.1 standard explicitly states that client makes the second request by itself and may choose in some cases not to make it. It is very different from server IP changing and is simply a limitation of XMLHttpRequest standard. – snovity Apr 04 '16 at 09:40
  • @snovity: Sorry, but the question was: how prevet redirection by usage some options of jQuery ajax. The answer is: it's impossible and it's even impossible on the level of underlying XMLHttpRequest. I understand that you don't like it. I don't like it too. But the usage of `WinHttpSetOption` API (which can't be used in JavaScript and which exist only on Windows) is the only way which I know to prevent the redirection. Moreover I think that you misunderstand one thing: the redirection makes *the client* and not the server: the server just returns *the instruction to make the redirection*. – Oleg Apr 04 '16 at 10:15
  • This answer is just wrong. XmlHttpRequest IS following the redirect, by examining the Location header and if present silently making another call to that url. XmlHttpRequest is creating the network socket, interpreting the stream of bytes as HTTP protocol and is definitely responsible for the redirect. – enorl76 Aug 08 '18 at 04:30
  • @enorl76: It seems that you misunderstand the question. The question was: "How **to prevent** ajax requests to follow redirects **using jQuery**?". Do you know an option of [jQuery.ajax](http://api.jquery.com/jquery.ajax/), which *prevents* possible redirection of `jQuery.ajax` call? – Oleg Aug 09 '18 at 21:35
26

I don't believe it is possible. The underlying library (XHR) makes the new request transparently. That being said, what I have done in these situations (usually a session-timeout type of deal that takes me to a login page) is send back a custom response header. I also have setup a global ajax handler that checks for the presence of that header, and responds appropriately when present (for example, redirecting the whole page to the login screen).

In case you're interested, here's the jQuery code I have to watch for that custom header:

/* redirects main window when AJAX request indicates that the session has expired on the backend. */
function checkSession(event, xhr, ajaxOptions)
{
    if (xhr.readyState == 4)
    {
        if(xhr.getResponseHeader("Login-Screen") != null && xhr.getResponseHeader("Login-Screen").length)
        {
            window.location.href='sessionExpired.html'; //whatever
        }
    }
}

$(document).ajaxComplete(checkSession)
Jake Feasel
  • 16,785
  • 5
  • 53
  • 66
12

I found a feature to check if your call has been redirected. It's xhr.state(): if it's "rejected" then a redirection happened.

Example with success callback:

request.success(function(data, textStatus, xhr)
{
    if(xhr.state() == "resolved")
    {
        //no redirection
    }
    if(xhr.state() == "rejected")
    {
        //redirection
    }
});

Example with error callback:

request.error(function(xhr, textStatus)
{
    if (xhr.state() == "rejected")
    {
        //redirection
        location.href = "loginpage";
    } else
    {
        //some other error happened
        alert("error");
    }
});
Takman
  • 1,028
  • 10
  • 13
  • 1
    The tip in your answer really helped us in getting things work. For the sake of notes that would help others, we have WebSphere Portal having its security integrated with SiteMinder. We have a portlet which needs Ajax calls to a resource url and when it times out, it has the transparent redirects happening but we are nowhere getting handle on how to redirect to the login page. Checking the jqXHR.state() being "rejected" surely helped. Many thanks again. – Uresh K Nov 11 '15 at 21:15
  • Great find, however there can be false positives as "rejected" state can be triggered even if a promise is rejected and not just on redirect. jQuery explains when "rejected" state is sent https://api.jquery.com/deferred.state/ – Nitin Oct 27 '16 at 23:34
2

I can't possibly add to the insightful wisdom of the previous coders who've responded, but I will add a specific case that others may find useful to know about.

I came across this 302 silent redirect in the context of SharePoint. I have some simple Javascript client code that pings a SharePoint sub-site, and if it receives a 200 HTTP response, it relocates to that site, via window.location. If it receives anything else, it gives the user a notice that the site doesn't exist.

However, in the case where the site exists but the user does not have permission, SharePoint silently redirects to an AccessDenied.aspx page. SharePoint has already done the HTTP 401 authentication handshake at the server/farm level - the user has access to SharePoint. But the access to the sub-site is handled I suppose using database flags of some sort. The silent redirect bypasses my "else" clause, so I can't throw up my own error. In my case, this is not a show-stopper - it is consistent predictable behavior. But it was a little surprising, and I learned something about HTTP requests in the process!

2

While it is not possible to disable location redirect following in XmlHttpRequests, it is when using fetch():

fetch('url', {redirect: manual});
cweiske
  • 30,033
  • 14
  • 133
  • 194
  • Fetch won't tell you what was the redirected URL. You can disable the behavior, but "redirect: manual" is not intended to let the user handle the redirect themself has the name intents. – lcjury Nov 20 '19 at 00:57
1

I was interested in the same thing and could not find the state() method mentioned by Takman and did a little digging for myself. For the sake of people turning up here in search of an answer, here are my findings:

As stated multiple times, you cannot prevent redirects, but you can detect them. According to MDN you can use the responseURL of the XMLHttpRequestObject, which will contain the final URL the response came from, after all redirects. Only caveat is that it is not supported by Internet Explorer (Edge has it). Since the xhr/jqXHR passed into the success/done function of jquery is an extension of the actual XMLHttpRequest, it should be available there, too.

Rialgar
  • 753
  • 5
  • 9
0

I suppose you receive a 200 response because the second time there is no redirection, because the 404 page does not expire, it is saved in the cache. That is to say that the second time the browser gives you the page in the cache. There is a property "cache" in the ajax jquery. http://api.jquery.com/jQuery.ajax/

You should write it to "false"

netadictos
  • 7,602
  • 2
  • 42
  • 69
-2

I'm not sure if this will apply in your case, but you can write code to respond to specific status codes in AJAX function -

$.ajax({
    url: '/admin/secret/data',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    statusCode: {
        200: function (data) {
            alert('302: Occurred');
            // Bind the JSON data to the UI
        },
        401: function (data) {
            alert('401: Occurred');
            // Handle the 401 error here.
        }
    }
});
ipr101
  • 24,096
  • 8
  • 59
  • 61
  • 9
    I don't think this applies because OP said he _always_ gets a 200 status code due to the redirection happening in the background.. – Yes Barry Nov 23 '11 at 07:54
-4

In the request headers in the case of ajax request you will have the following

X-Requested-With    XMLHttpRequest

By this criteria on the server side you can filter requests.

Gfox
  • 307
  • 1
  • 6
  • He wanted to check the _response_ not the request (over which he already has control) – Yes Barry Nov 23 '11 at 07:52
  • Maybe Gfox suggested that for ajax requests returning 3xx are pointless, you could filter this kind of request and return 403 for example. when you have a website that serves pages that require forms authentication and those pages make ajax calls also and you want to put authorization logic in one place, then to me it is a valid answer. – maciejW Jun 21 '17 at 20:57