如何管理 jQuery Ajax 调用后的重定向请求

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?

转载于:https://stackoverflow.com/questions/199099/how-to-manage-a-redirect-request-after-a-jquery-ajax-call

csdnceshi59
ℙℕℤℝ this link doanduyhai.wordpress.com/2012/04/21/… gives me the right solution
接近 6 年之前 回复
csdnceshi71
Memor.の Why cant the server return 401? In that case you can have a global $.ajaxSetup and use the status code to redirect the page.
接近 7 年之前 回复
csdnceshi53
Lotus@ The HttpContext.Response.AddHeader and check at ajaxsetup sucess is the way to go
接近 7 年之前 回复
csdnceshi64
游.程 See related question: stackoverflow.com/questions/5941933/…
接近 9 年之前 回复
csdnceshi73
喵-见缝插针 (not an answer as such) - I've done this in the past by editing the jquery library and adding a check for the login page on each XHR complete. Not the best solution because it would have to be done each time you upgrade, but it does solve the problem.
接近 12 年之前 回复

30个回答

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.

weixin_41568110
七度&光 i am trying this its not working for me
2 年多之前 回复
weixin_41568196
撒拉嘿哟木头 size is depreciated so you can use .length here in place of size
大约 4 年之前 回复
csdnceshi50
三生石@ Note that this can be shortened to function cbWrapper(funct) { return function(data) { if($("#myForm", data).size() > 0) top.location.href="login"; else funct(data); } } . You then only need cbWrapper(myActualCB) when calling .post. Yes, code in comments is a mess but it should be noted :)
接近 10 年之前 回复

I read this question and implemented the approach that has been stated regarding setting the response 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.

weixin_41568127
?yb? i want to redirect user to current page I am trying this its not working for me
2 年多之前 回复
csdnceshi72
谁还没个明天 Why NOT let browser handle the 302 transparently? Isn't it good enough?
4 年多之前 回复
csdnceshi57
perhaps? 200 codes aren't the only codes that jQuery ajax considers success. It also considers 304 (not modified) a successful request, and will trigger the success callback. I generally use the HTTP status code to indicate success as a boolean, by either return 200 for failure or 201 for success. This works brilliantly for MVC, because during failure you generally want to redisplay the page with the model errors, so you want to return a 200. On success, you may or may not want to return and display data, but in either case its very easy to check whether the status is 201 (for success) or not.
接近 5 年之前 回复
weixin_41568174
from.. This answer would be more helpful if it had shown how this is done on the server.
接近 5 年之前 回复
csdnceshi56
lrony* what would the server code look like? to have a return value of data.form and data.redirect. basically how do i determine if i put a redirect in it?
接近 7 年之前 回复
csdnceshi63
elliott.david This works only if you have control on server
8 年多之前 回复
weixin_41568184
叼花硬汉 Golos the reason is that if you do a HTTP redirect, the redirect actually never arrives to the ajax success callback. The browser processes the redirect delivering a 200 code with the content of the redirect's destination.
9 年多之前 回复
csdnceshi74
7*4 Any reason why it it wouldn't be better to use HTTP codes on the action. For example a 307 code which is HTTP Temporary Redirect?
9 年多之前 回复
csdnceshi59
ℙℕℤℝ As stated in the solution in stackoverflow.com/questions/503093/… it is better to use window.location.replace(data.redirect); than window.location.href = data.redirect;
接近 10 年之前 回复

I solved this issue by:

  1. Adding a custom header to the response:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. 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 = '/';
        }
    });
    
csdnceshi63
elliott.david Does anyone know how to do this on Spring Security?
接近 5 年之前 回复
csdnceshi62
csdnceshi62 And you could add this check somewhere in Global.asax. In Application_AuthenticateRequest event
6 年多之前 回复
weixin_41568127
?yb? Take care to set the header on the response AFTER the redirect. As described in other answers on this page, the redirect may be transparent to the ajaxSucces handler. Thus I included the header on the GET response of the login page (which was eventually and only triggering ajaxSuccess in my scenario).
7 年多之前 回复
weixin_41568196
撒拉嘿哟木头 I like the header approach but also think - like @mwoods79 - that the knowledge of where to redirect to should not be duplicated. I solved that by adding a header REDIRECT_LOCATION instead of a boolean.
接近 8 年之前 回复
csdnceshi79
python小菜 This solution requires the browser to know the URL for authentication. Although this solves the problem, it is (in my opinion) not a maintainable solution. If the authentication controller changes, now you are maintaining the routes file and the javascript. i.e. This is bad programming practice.
接近 8 年之前 回复
weixin_41568183
零零乙 It's ok in many cases, but what if your framework handles authorization ?
8 年多之前 回复
csdnceshi75
衫裤跑路 This worked great for me. Just set the javascript to reload the current page with location.reload(); as our model requires redirect to a separate authorization site.
8 年多之前 回复
csdnceshi77
狐狸.fox The header in what language?
大约 9 年之前 回复
csdnceshi59
ℙℕℤℝ I just did this and found that I needed ajaxComplete where I was using the $.get() function and any status other than 200 was not firing. In fact, I could have probably just bound to ajaxError instead. See my answer below for more details.
大约 10 年之前 回复
csdnceshi59
ℙℕℤℝ What an awesome solution. I like the idea of a one-stop solution. I need to check for a 403 status, but I can use the ajaxSuccess bind on body for this (which is what I was really looking for.) Thanks.
大约 10 年之前 回复

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...! ;)

weixin_41568196
撒拉嘿哟木头 To arrange for a redirect of the markup in main section without redirecting the page.
3 年多之前 回复
csdnceshi64
游.程 Agree. Transparently handling is just what I want. And I don't know why it is considered bad.
4 年多之前 回复
csdnceshi74
7*4 Doesn't it already handle them transparently? If a resource has moved, handling it transparently means repeating the request on the provided URL. That is what I would expect using the XMLHttpRequest API.
大约 7 年之前 回复
weixin_41568127
?yb? I for one move that 278 should become apart of the official HTTP Spec.
8 年多之前 回复

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");
}
csdnceshi65
larry*wei A 403 does not mean that the user isn't authenticated, it means that the (probably authenticated) user does not have permission to view the requested resource. So it should not redirect to the login page
6 年多之前 回复
weixin_41568184
叼花硬汉 the latter would hide any other errors making troubleshooting problematic
大约 9 年之前 回复

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 :)

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

weixin_41568196
撒拉嘿哟木头 Your answer is similar to this one from @Arpad. It worked for me; using Spring Security 3.2.9. stackoverflow.com/a/8426947/4505142
2 年多之前 回复
csdnceshi63
elliott.david Users might have voted down because you didn't mention: (1) the requisite client-side mods to detect your error response; (2) the requisite mods to Spring configuration to add your customized LoginUrlAuthenticationEntryPoint to the filter chain.
6 年多之前 回复
csdnceshi63
elliott.david If using Spring and also using JSF, then also check for this: ("partial/ajax").equalsIgnoreCase(request.getHeader("faces-request"));
6 年多之前 回复
csdnceshi51
旧行李 It would be helpful (to me) if down voters would explain why they are down voting. If there is something bad with this solution I would like to learn from my mistakes. Thanks.
接近 7 年之前 回复

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;
}

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.

Additionally you will probably want to redirect user to the given in headers URL. So finally it will looks like this:

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD: Opps. Have the same task, but it not works. Doing this stuff. I'll show you solution when I'll find it.

csdnceshi78
程序go This answer should be deleted by the author as he himself says that this doesn' work. He can post a working solution later. -1
3 年多之前 回复
共30条数据 1 3 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问