New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page anchor in url not updated after redirect #1571

Closed
tenor opened this Issue May 5, 2011 · 17 comments

Comments

Projects
None yet
10 participants
@tenor

tenor commented May 5, 2011

Say I navigate from page1 to page2, but page2 returns a 302 found and directs the browser to page3.
jQuery mobile reads the 302 properly and navigates to page3, however the anchor is not updated.

The address bar will be http://localhost/#/page2 instead of http://localhost/#/page3.

This causes the data-url to have the wrong value for the active page3.

It also leads to issues during page refreshes, for example:
I have a page1 that posts to page2, however page2 is a 'post-only' processor page.
page2 performs its task and redirects to page3.

But the url in the address bar is http://localhost/#/page2 or http://localhost/page1/#/page2 (depending on the first page).

Now, if the user refreshes the page, jQuery Mobile will detect the first page and the anchor page
and attempt to GET them. It will be able to get the first page, but will fail to get the second page because that page does not accept GETs.

If the anchor page on the url was the correct page3, the GET would have succeeded and the state restored.

The error occurs for redirects that were preceded by either GET or POST requests.

This issue was observed in FF 3.6.17 and Safari 5.0.5

I have a demonstration available if you need to reproduce this bug.

@ckoning

This comment has been minimized.

Show comment
Hide comment
@ckoning

ckoning May 15, 2011

This bug is also present in Chrome.

ckoning commented May 15, 2011

This bug is also present in Chrome.

@toddparker

This comment has been minimized.

Show comment
Hide comment
@toddparker

toddparker Jun 23, 2011

Contributor

Is this still an issue with Beta 1?

Contributor

toddparker commented Jun 23, 2011

Is this still an issue with Beta 1?

@tenor

This comment has been minimized.

Show comment
Hide comment
@tenor

tenor Jun 23, 2011

Yes, the issue is still present in Beta 1.
I reproduced it with FF 3.6.18 and Safari 5.0.5

tenor commented Jun 23, 2011

Yes, the issue is still present in Beta 1.
I reproduced it with FF 3.6.18 and Safari 5.0.5

@limcheepei

This comment has been minimized.

Show comment
Hide comment
@limcheepei

limcheepei Jul 14, 2011

I also have the same issue. I am using ASP.NET MVC3, JQM Beta 1.
JQM seems unable to update the URL address bar when there is a redirection.

limcheepei commented Jul 14, 2011

I also have the same issue. I am using ASP.NET MVC3, JQM Beta 1.
JQM seems unable to update the URL address bar when there is a redirection.

@ahutch

This comment has been minimized.

Show comment
Hide comment
@ahutch

ahutch Jul 17, 2011

Me too, this logs-out a user on the server and redirects to root.

 <a href="/signout" class="ui-btn-right" data-ajax="false" data-method="delete" rel="nofollow">logout</a>

However instead of root I get http://localhost:3000/#/signout which is not a real page.

I am using Beta 1, Safari 5.0.5

ahutch commented Jul 17, 2011

Me too, this logs-out a user on the server and redirects to root.

 <a href="/signout" class="ui-btn-right" data-ajax="false" data-method="delete" rel="nofollow">logout</a>

However instead of root I get http://localhost:3000/#/signout which is not a real page.

I am using Beta 1, Safari 5.0.5

@scottjehl

This comment has been minimized.

Show comment
Hide comment
@scottjehl

scottjehl Jul 17, 2011

Contributor

The only way for the clientside to know about your redirects is to serve your pages with the new url already populated on the page element (ajax responses come with no location headers to tell us that a redirect occurred). To do this, the data-url attr should be an absolute path to the page being viewed.

You can read more about this at the bottom of this docs page: http://jquerymobile.com/test/docs/pages/docs-pages.html

Contributor

scottjehl commented Jul 17, 2011

The only way for the clientside to know about your redirects is to serve your pages with the new url already populated on the page element (ajax responses come with no location headers to tell us that a redirect occurred). To do this, the data-url attr should be an absolute path to the page being viewed.

You can read more about this at the bottom of this docs page: http://jquerymobile.com/test/docs/pages/docs-pages.html

@scottjehl scottjehl closed this Jul 17, 2011

@tenor

This comment has been minimized.

Show comment
Hide comment
@tenor

tenor Jul 17, 2011

@scottjehl , the example on that page is not a true HTTP redirect.
The client side can detect a redirect by examining the HTTP status code for a value of 302 and the location header for the url.

This stackover post shows how a redirect can be detected by a jQuery.ajax call.

JQM currently handles redirects properly. It performs a get on the new url and updates the DOM. It just doesn't replace the url hash in the browser with the updated url.

Also the data-url attribute solution will not work in some MVC scenarios because a view (which contains the page layout) can be shared by multiple controllers (which are the URL endpoints)

tenor commented Jul 17, 2011

@scottjehl , the example on that page is not a true HTTP redirect.
The client side can detect a redirect by examining the HTTP status code for a value of 302 and the location header for the url.

This stackover post shows how a redirect can be detected by a jQuery.ajax call.

JQM currently handles redirects properly. It performs a get on the new url and updates the DOM. It just doesn't replace the url hash in the browser with the updated url.

Also the data-url attribute solution will not work in some MVC scenarios because a view (which contains the page layout) can be shared by multiple controllers (which are the URL endpoints)

@scottjehl

This comment has been minimized.

Show comment
Hide comment
@scottjehl

scottjehl Jul 17, 2011

Contributor

Okay. Thanks, Tenor. I'll leave this open. Mind submitting a pull that implements this?

Contributor

scottjehl commented Jul 17, 2011

Okay. Thanks, Tenor. I'll leave this open. Mind submitting a pull that implements this?

@scottjehl scottjehl reopened this Jul 17, 2011

@ahutch

This comment has been minimized.

Show comment
Hide comment
@ahutch

ahutch Jul 17, 2011

One more question for this thread - I redirected to root which is the base page of multi-page document. I used data-ajax="false" because I wanted a full reload of the root page. However the redirect page is effectively blank. After inspecting the document I can see that the redirected page was loaded as a sub page of the root document. The document has two pages named root. The redirect root having data-url="/signout" added to it.

If JQM is handling the redirect properly then why do I get a sub-page? How do I avoid this?

ahutch commented Jul 17, 2011

One more question for this thread - I redirected to root which is the base page of multi-page document. I used data-ajax="false" because I wanted a full reload of the root page. However the redirect page is effectively blank. After inspecting the document I can see that the redirected page was loaded as a sub page of the root document. The document has two pages named root. The redirect root having data-url="/signout" added to it.

If JQM is handling the redirect properly then why do I get a sub-page? How do I avoid this?

@tenor

This comment has been minimized.

Show comment
Hide comment
@tenor

tenor Jul 18, 2011

@scottjehl was right and I was wrong. The client cannot detect this because the xmlHttpObject transparently auto follows 3xx responses.

It looks like the only way to fix this is to set the data-url on all redirect landing pages. le sigh.

@ahutch, it seems the issue you describe is different from this issue, although they may be related.
I suggest you open a new issue.

tenor commented Jul 18, 2011

@scottjehl was right and I was wrong. The client cannot detect this because the xmlHttpObject transparently auto follows 3xx responses.

It looks like the only way to fix this is to set the data-url on all redirect landing pages. le sigh.

@ahutch, it seems the issue you describe is different from this issue, although they may be related.
I suggest you open a new issue.

@tenor tenor closed this Jul 18, 2011

@scottjehl

This comment has been minimized.

Show comment
Hide comment
@scottjehl

scottjehl Jul 18, 2011

Contributor

Thanks, tenor. If it's any consolation, I wanted so badly for you to be right about that one! :) I'm not sure why this never made it into the xmlHttpReq spec... :/

Thanks for the update.

Contributor

scottjehl commented Jul 18, 2011

Thanks, tenor. If it's any consolation, I wanted so badly for you to be right about that one! :) I'm not sure why this never made it into the xmlHttpReq spec... :/

Thanks for the update.

@dyardy

This comment has been minimized.

Show comment
Hide comment
@dyardy

dyardy Sep 29, 2011

I do not have a solution to this problem. If there is a good solution please let me know.
I am using mvc 3 and when performing a redirecttoaction after a form post the url is not getting updated. Any recommendations on how to correct this situation?

thank you

dyardy commented Sep 29, 2011

I do not have a solution to this problem. If there is a good solution please let me know.
I am using mvc 3 and when performing a redirecttoaction after a form post the url is not getting updated. Any recommendations on how to correct this situation?

thank you

@tenor

This comment has been minimized.

Show comment
Hide comment
@tenor

tenor Sep 29, 2011

I'm using MVC too. Here's my solution:

1. Create a class that holds information pertaining to JQM views.

    public class JQMOptions
    {
        public string Url { get; set; }

        public static string GetDataUrlAttribute(JQMOptions options)
        {
            if (options == null || String.IsNullOrEmpty(options.Url))
            {
                return string.Empty;
            }

            return "data-url=\"" + VirtualPathUtility.ToAbsolute( options.Url) + "\"";
        }
    }

2. Have the data-role=page div tag in your master page call the GetDataUrlAttribute method like so:

    <body>
        <div data-role="page" <%= JQMOptions.GetDataUrlAttribute(ViewData["JQMOptions"] as JQMOptions) %>  data-theme="d" >
            <asp:ContentPlaceHolder ID="HeaderContent" runat="server" />
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
            <asp:ContentPlaceHolder ID="FooterContent" runat="server" />
        </div>
    </body>

3. In controllers that serve the landing view (the page that is redirected to). Set ViewData["JQMOptions"] to an instance of JQMOptions that has the url you want to appear, like so:

        [HttpGet]
        public ActionResult SaveDataLanding()
        {
            //Do stuff

            //This is a landing page for redirects, so set the URL Hash manually for JQM mobile
            ViewData["JQMOptions"] = new JQMOptions() { Url = "~/MyController/SaveDataLanding" };

            return View();
        }

Hope that helps.

tenor commented Sep 29, 2011

I'm using MVC too. Here's my solution:

1. Create a class that holds information pertaining to JQM views.

    public class JQMOptions
    {
        public string Url { get; set; }

        public static string GetDataUrlAttribute(JQMOptions options)
        {
            if (options == null || String.IsNullOrEmpty(options.Url))
            {
                return string.Empty;
            }

            return "data-url=\"" + VirtualPathUtility.ToAbsolute( options.Url) + "\"";
        }
    }

2. Have the data-role=page div tag in your master page call the GetDataUrlAttribute method like so:

    <body>
        <div data-role="page" <%= JQMOptions.GetDataUrlAttribute(ViewData["JQMOptions"] as JQMOptions) %>  data-theme="d" >
            <asp:ContentPlaceHolder ID="HeaderContent" runat="server" />
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
            <asp:ContentPlaceHolder ID="FooterContent" runat="server" />
        </div>
    </body>

3. In controllers that serve the landing view (the page that is redirected to). Set ViewData["JQMOptions"] to an instance of JQMOptions that has the url you want to appear, like so:

        [HttpGet]
        public ActionResult SaveDataLanding()
        {
            //Do stuff

            //This is a landing page for redirects, so set the URL Hash manually for JQM mobile
            ViewData["JQMOptions"] = new JQMOptions() { Url = "~/MyController/SaveDataLanding" };

            return View();
        }

Hope that helps.

@gochev

This comment has been minimized.

Show comment
Hide comment
@gochev

gochev Feb 19, 2015

Nothing is fixed... is this even tested ?
I am returning an redirect from server side. on URL /frontend/base URL

The response code is Status Code:303 See Other

and in the header there is a new location

Content-Language:bg
Content-Length:0
Date:Thu, 19 Feb 2015 10:06:34 GMT
Location:/frontend/redirection-url
Server:Apache-Coyote/1.1

However JQuery Mobile doesnt change the location to /frontend/redirection-url instead ut uses /frontend/base URL... which is a POST url ..

Basically POST,REDIRECT,GET pattern doesnt work with JQuery Mobile ?

I am using 1.4.3 so nothing is fixed.. (more then 1 year later (facepalm))

gochev commented Feb 19, 2015

Nothing is fixed... is this even tested ?
I am returning an redirect from server side. on URL /frontend/base URL

The response code is Status Code:303 See Other

and in the header there is a new location

Content-Language:bg
Content-Length:0
Date:Thu, 19 Feb 2015 10:06:34 GMT
Location:/frontend/redirection-url
Server:Apache-Coyote/1.1

However JQuery Mobile doesnt change the location to /frontend/redirection-url instead ut uses /frontend/base URL... which is a POST url ..

Basically POST,REDIRECT,GET pattern doesnt work with JQuery Mobile ?

I am using 1.4.3 so nothing is fixed.. (more then 1 year later (facepalm))

@dexpect

This comment has been minimized.

Show comment
Hide comment
@dexpect

dexpect Apr 2, 2015

I am facing the same problem with v1.4.5, gochev.
tenor's solution is working for me as well. Currently it's working by setting the data-url-attribute manually, but i couldn't fix it within JQM by myself.

In combination with Cordova/PhoneGap there are some more problems. If there is a page for issues with Cordova, please tell me.

  1. Relative URLs are pointing to local file system (e.g. file:///) after a redirect
  2. Pages can get "broken" after redirecting from another hash (e.g. in combination with JQM Popup-Widget, which occurs the hash to be changed).

dexpect commented Apr 2, 2015

I am facing the same problem with v1.4.5, gochev.
tenor's solution is working for me as well. Currently it's working by setting the data-url-attribute manually, but i couldn't fix it within JQM by myself.

In combination with Cordova/PhoneGap there are some more problems. If there is a page for issues with Cordova, please tell me.

  1. Relative URLs are pointing to local file system (e.g. file:///) after a redirect
  2. Pages can get "broken" after redirecting from another hash (e.g. in combination with JQM Popup-Widget, which occurs the hash to be changed).
@gochev

This comment has been minimized.

Show comment
Hide comment
@gochev

gochev Apr 19, 2015

I removed the ajax page lod ... so now I use normal page refresh without transition this is the only way for me to fox it snce I dont have power to change the server code. However for your cordova app I guess this is not the best user expirience ;(

gochev commented Apr 19, 2015

I removed the ajax page lod ... so now I use normal page refresh without transition this is the only way for me to fox it snce I dont have power to change the server code. However for your cordova app I guess this is not the best user expirience ;(

@justinDevel

This comment has been minimized.

Show comment
Hide comment
@justinDevel

justinDevel Feb 18, 2018

ok everybody to solve this issue just do the following and it will work
In the case of a rich JavaScript client you would then have something like
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
But unfortunately the browser handles location redirects completely himself, wherefore the ajaxComplete callback won't fire with the 302 status code, but instead it will fire on the location redirect target (when that redirect already happened successfully). As a result, the approach before obviously won't work.

The solution is a bit hacky but it works and I didn't found a better one so far (comment if I'm wrong). Basically what you do is to inject a custom header in your Login page for which you then listen on your client-side. The trick is to inject a header like
LoginPage: http://www.mydomain.com/Login
where the url is the one of the invoked Login page due to the location redirect. On the client side you can now check for the presence of the header like
if(xhr.status === 200){
var loginPageHeader = xhr.getResponseHeader("LoginPage");
if(loginPageHeader && loginPageHeader !== ""){
window.location.replace(loginPageHeader);
}
}
You might wonder why I included the url directly in the header value. The problem is that you have no way of detecting the URL of the redirected request in the ajaxComplete callback. You just get the initial one you started, that is, when you execute a GET /person/1 and you're no more logged in (because the session expired) and the server sends you a 302 redirect to /login, then you'll see the /person/1 as the url in your ajaxComplete callback although the content of the response is the one of your login page. That's why I directly included the url in my custom header.

justinDevel commented Feb 18, 2018

ok everybody to solve this issue just do the following and it will work
In the case of a rich JavaScript client you would then have something like
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
But unfortunately the browser handles location redirects completely himself, wherefore the ajaxComplete callback won't fire with the 302 status code, but instead it will fire on the location redirect target (when that redirect already happened successfully). As a result, the approach before obviously won't work.

The solution is a bit hacky but it works and I didn't found a better one so far (comment if I'm wrong). Basically what you do is to inject a custom header in your Login page for which you then listen on your client-side. The trick is to inject a header like
LoginPage: http://www.mydomain.com/Login
where the url is the one of the invoked Login page due to the location redirect. On the client side you can now check for the presence of the header like
if(xhr.status === 200){
var loginPageHeader = xhr.getResponseHeader("LoginPage");
if(loginPageHeader && loginPageHeader !== ""){
window.location.replace(loginPageHeader);
}
}
You might wonder why I included the url directly in the header value. The problem is that you have no way of detecting the URL of the redirected request in the ajaxComplete callback. You just get the initial one you started, that is, when you execute a GET /person/1 and you're no more logged in (because the session expired) and the server sends you a 302 redirect to /login, then you'll see the /person/1 as the url in your ajaxComplete callback although the content of the response is the one of your login page. That's why I directly included the url in my custom header.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment