ASP.NET code behind incompatible with back() in Android browser

AID: 6170
  • Status: Published

2050 points

  • Byalexcohn
  • TypeTips/Tricks
  • Posted on2011-06-12 at 12:31:14
User art_snob encountered strange behavior of Android Web browser on his Mobile Web site. It took a while to find the true cause. It happens so, that the Android Web browser (at least up to OS ver. 2.3.3) tends to ignore the no-cache HTTP headers, and decides at any rate to avoid page reload. This causes severe problems with ASP.NET, which relies on synchronization of code-behind (e.g. a.aspx.vb) with the Web page (a.aspx).

Here is a stripped-down example:
a.aspx
<%@ Page Language="VB" CodeFile="a.aspx.vb" Inherits="a" EnableViewStateMac="False" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<HTML xmlns="http://www.w3.org/1999/xhtml">

<HEAD id="Head1" runat="server">
	<TITLE>a.aspx</TITLE>
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
</HEAD>

<BODY>
<FORM id="form1" runat="server">
	<div id="container">
		<asp:ScriptManager ID="ScriptManager1" runat="server">
		</asp:ScriptManager>  
		<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
			<ContentTemplate>
				<asp:Button ID="ServerButton" runat="server" OnClick="Button_Click" text="go to b.html on server" /><p />
				<asp:Button ID="ClientButton" runat="server" onClientClick="location = 'b.html'" text="go to b.html on client" /><p />
			</ContentTemplate>
		</asp:UpdatePanel>
	</div>
</FORM>
</BODY>
</HTML>
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:

Select allOpen in new window

a.aspx.vb
Partial Class a
    Inherits Page

    Protected Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs) Handles ServerButton.Click
        Response.Redirect("b.html", False)
    End Sub

End Class
                                    
1:
2:
3:
4:
5:
6:
7:
8:

Select allOpen in new window


Now, on any known browser, the ServerButton and the ClientButton produce the same result:
b.html
<html>
	<head>
		<title>b.html</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	</head>
	<body>
			<input name="goback" onclick="history.back()" type="button" value="go back" /></p>
			<input name="gotoA" onclick="location='a.aspx'" type="button" value="go to a.aspx" /></p>
			<input name="replaceA" onclick="location.replace('a.aspx')" type="button" value="replace to a.aspx" /></p>
			<input name="openA" onclick="window.open('a.aspx', 'top')" type="button" value="open a.aspx" /></p>
	</body>
</html>
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:

Select allOpen in new window


And on all known browsers, all four buttons in b.html display a.aspx again and let you repeat the process. Except goback button and the Android browser.
If you start at a.aspx and then click goback, or the system Back button, you will be still able to use the  ClientButton, but the ServerButton will not work at all. Under the hood it will get an ASP.NET exception:
E/browser (xxx): Console: Uncaught Sys.WebForms.PageRequestManagerServerErrorException: Sys.WebForms.PageRequestManagerServerErrorException: An unknown error occurred while processing the request on the server. The status code returned from the server was: 0 http://xxx/ScriptResource.axd?d=31hrpqnrjCS7Ljksbfakhjr6vQHbaDyxah__zHgcOttSylOBq8Zbq2kxgVMdA3MThu4aquTF9WtwbL8sY19YcnLh9RlqCkMMQlaKeoC5pMVR-tgb3iU8M6inww0QtoQYLpIsOrH70stFuO0SE_LVrEyvT7miOJgBVPvhCD-3ekjwH95jk8pHDxgtqXOa4aGu0&t=2610f696:5
                                    
1:

Select allOpen in new window


I have explained above what causes this problem. Now our task is to find an easy and reliable plumbing.
I will use an XMLHttpRequest to verify that the copy of the page is not coming from local cache, but is fresh from the server. This way, we can guarantee that code behind will be live.
Here is the Page_Load procedure in a.aspx.vb:
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        Dim uni = Request.QueryString("getUnique")
        If uni = "" Then
           HttpContext.Current.Session("QQQ") = Now().toString("ms")
        Else
           If (HttpContext.Current.Session("QQQ") Is Nothing)
              uni = "9999"
           Else
              uni = HttpContext.Current.Session("QQQ")
           End If

           Response.Write(uni)
           Response.Flush()
        End If
    End Sub
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

Select allOpen in new window


and here is the client-side javascript (put it somewhere inside a.aspx):
<script>
        originalServerTime = <%= HttpContext.Current.Session("QQQ") %>

	var request = new XMLHttpRequest();
	request.onreadystatechange = function()
	{
		if (request.readyState == 4)
		{
			newServerTime=parseInt(request.responseText);
console.log("newServerTime: " + newServerTime + " originalServerTime: " + originalServerTime);

			if (newServerTime != originalServerTime) // if not using Session persistence, allow for up to 2 sec delay
			{
				location.replace(location.href);
			}
		}
	};
	var ser = Math.round(Math.random()*1000000); // Anti-caching random number
	request.open('GET', location.href + '?' + 'getUnique=' + ser, true);
	request.send(null);
</script>
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:

Select allOpen in new window


So, we generate a new request, make sure that it is never cached (with random getUnique), and retrieve the value that should be identical to the one embedded with <%= %> when the page was generated. If the values differ, the page has lost its connection to code behind.
We reload the page by force, and use location.replace() to replace the unhealthy page in the browser history.

Actually, you could choose to do this without using the Session object. Simply generate the server time on the fly in response to the request. Unfortunately, this approach is less robust: you must leave a couple of seconds "freedom" because the server may be too busy between the two requests, and the client may decide to press Back button really quickly.
Asked On
2011-06-12 at 12:31:14ID6170
Tags

Android Web AJAX Session javascript VB.NET ASP.NET

Topic

Programming for ASP.NET

Views
915

Comments

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top ASP.NET Experts

  1. CodeCruiser

    420,756

    Wizard

    2,010 points yesterday

    Profile
    Rank: Genius
  2. kaufmed

    283,163

    Guru

    500 points yesterday

    Profile
    Rank: Genius
  3. BuggyCoder

    269,283

    Guru

    3,600 points yesterday

    Profile
    Rank: Sage
  4. tommyBoy

    171,028

    Guru

    0 points yesterday

    Profile
    Rank: Genius
  5. TheLearnedOne

    165,990

    Guru

    0 points yesterday

    Profile
    Rank: Savant
  6. ddayx10

    139,847

    Master

    0 points yesterday

    Profile
    Rank: Sage
  7. MlandaT

    122,463

    Master

    0 points yesterday

    Profile
    Rank: Genius
  8. techChallenger1

    107,384

    Master

    0 points yesterday

    Profile
    Rank: Guru
  9. navneethegde

    90,197

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  10. Masteraco

    82,909

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  11. Dhaest

    80,693

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  12. vs00saini

    76,565

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  13. Chinmay_Patel

    73,407

    Master

    0 points yesterday

    Profile
    Rank: Genius
  14. stephanonline

    69,461

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  15. nishantcomp2512

    68,521

    Master

    0 points yesterday

    Profile
    Rank: Wizard
  16. srosebabu

    67,676

    Master

    0 points yesterday

    Profile
    Rank: Guru
  17. markmiddlemist

    65,828

    Master

    0 points yesterday

    Profile
    Rank: Master
  18. jacko72

    65,660

    Master

    0 points yesterday

    Profile
    Rank: Genius
  19. sammySeltzer

    64,318

    Master

    0 points yesterday

    Profile
    Rank: Genius
  20. wdosanjos

    59,639

    Master

    0 points yesterday

    Profile
    Rank: Genius
  21. mroonal

    58,080

    Master

    0 points yesterday

    Profile
    Rank: Sage
  22. mas_oz2003

    56,140

    Master

    0 points yesterday

    Profile
    Rank: Genius
  23. Rouchie

    55,801

    Master

    0 points yesterday

    Profile
    Rank: Genius
  24. Lalit-Chandra

    54,514

    Master

    0 points yesterday

    Profile
    Rank: Master
  25. EaswaranP

    54,203

    Master

    0 points yesterday

    Profile
    Rank: Guru

Hall Of Fame