Archive for the ‘Ajax’ Category

URL Rewriting: Retain URL in Browser Address Bar on Postback

June 16, 2010

The Reason For All Human Existence

We have a HTTPModule which rewrites .html file extensions to .aspx. The aspx file is served but the user sees the URL as .html. This apparently gives us mega Google points which is fundamentally the reason for all human existence and therefore intrinsically justified.

Form Action Attribute: Enemy of Civilisation

The HTTPModule does the URL Rewriting via HTTPContent.Current.RewritePath(realfilename.aspx).
This leaves the the URL that appears in the browser untouched but causes realfilename.aspx to be served to satisfy the Page Request.

BUT RewritePath sets the underlying Form Action Attribute is also set to realfilename.aspx which means that on Postback realfilename.aspx is POSTed to and realfilename.aspx appears in the Browser Address Bar. This drains Google Juice and makes life, work and all existence meaningless. Worse still , the behaviour of the web page was entirely accurate. The page does need to post back to the real page (not the fake one) in order to process the Postback. That happens through the Form Action Attribute: that’s what its for.

False and True Friends

The #1 recommended solution to this problem on teh Greater Google is to remove the Form Action Attribute entirely from the page by implementing a custom HTML Form Control and overriding the Render method. Once this is done, the Form apparently posts back to the URL in the Address Bar. But I didn’t want to mangle my page that way as it leads to downstream problems and besides, I really did need to postback to realfilename.aspx.

Examining one of our existing Web Pages that handled this scenario correctly, I noticed the form was wrapped in an AJAX UpdatePanel. Yeah, that made sense, the full postback would be suppressed maybe changing some behaviours, so I did the same thing – but no bananas – still the postback put realfilename.aspx in the Browser Address Bar.

I then unleashed Firefox’s glorious FIREBUG to have a closer look at the requests generated on the existing good website and the new bad website. The Net Panel showed me that a POST operation was appended to the list of Requests for the page, while on the bad website a brand new GET was invoked, in Firebug’s Net Panel the GET would clear all the entries and repopulate it with entries with the fresh Request. Suck.

An even closer look at the existing website showed me that not only was the whole HTML Form wrapped in an UpdatePanel but that the Web Page was actually a Content Page belonging to a Master Page, not a Web Page in its own right. Would that really make a difference ? YES!

Live Worth Living Again

To suppress the full .NET Postback with an AJAX Postback I had to refactor the Web Page so that it was a Content Page and (of course) link it to a Master Page and wrap the whole of the Content in an UpdatePanel. Like so:

asp:Content ID=”Content1″ ContentPlaceHolderID=”ContentPlaceHolder1″ runat=”server”
!– Whole page content is wrapped in an UpdatePanel.
This means all postbacks will be ajax postbacks which means that html extension will
be retained on postback which is what we need for URL Rewriting.
If we permitted a full postback, the postback would be posted to index.aspx and the address bar would
be updated with the aspx URL thus overwriting the pretty html extension. All this occurs because
the form action attribute is set to index.aspx (the real page) even though the URL has been rewritten
to index.html (the pretty URL)–
asp:UpdatePanel runat=”server” ID=”upIndex”
ContentTemplate
…oodles of Content here…
/ContentTemplate

The corresponding Master Page is but a shell and looks basically like this:
body
form id=”form1″ runat=”server”
asp:ScriptManager runat=”server” /
div
asp:contentplaceholder id=”ContentPlaceHolder1″ runat=”server”
/div
/form
/body

I don’t really know why moving all the content to a Content Page should be required to suppress the full postback. But it works for me.

Advertisements

Ajax Client Side Event Cycle

November 11, 2008

Our team lead recently passed around a brilliant diagram compiled by Milan Negovan of AspNetResources which explains the ASP.NET Ajax Client Side Event Cycle.

I’m linking to it here so I can reference it in the future and hopefully give it a bit extra publicity which it well deserves. Have a look.

Milan shows how the page events, Application events and PageRequestManager events interract during AJAX calls and clearly explains how to hook into those events i.e. add your own handlers. It also explains exactly where UpdatePanel fits into the picture and clearly states when and who calls what event.

To whet your appetite…

When partial-page rendering with UpdatePanel controls is enabled, the key client
events are the events of the PageRequestManager class. These events enable you to
handle many common scenarios. These include the ability to cancel postbacks, to give
precedence to one postback over another, and to animate UpdatePanel controls when
their content is refreshed.

To add or remove handlers for events raised by the Application and
PageRequestManager classes, use the add_eventname and remove_eventname methods
of those classes…

Top work Milan!

TabContainer LoadClientState Failed

October 24, 2008

We have a set of small Javascript functions that retain the ActiveIndex of an ASP Ajax Toolkit TabContainer control and restores it on postback. Sometimes, perhaps due to increased sunspot activity, our TabContainer ClientState disappears. Resulting in the intruiging and quite humorous error “When casting from a number, the value must be a number less than infinity.”

To this day I have no idea what causes TabContainer to lose its state, but courtesy of a fellow traveller in pain, I can present a workaround.

Meet The Javascripts

Here’s our happy little Javascript family from the picturesque village of MarkUp

In Head


function saveCurrentTabIndex()
{
var tabcontainer = $get('').control;
var tabIndex = $get('');
if (tabcontainer != null)
{
tabIndex.value = tabcontainer.get_activeTabIndex();
}
}

function restoreCurrentTabIndex()
{
var tabIndex = $get('tabIndexHidden');
if (tabIndex.value > -1)
{
var tabcontainer = $find( '');
var index = tabcontainer.get_activeTabIndex();
if (index != tabIndex.value)
{
tabcontainer.set_activeTabIndex(tabIndex.value);
}
}
}

In Body
input type=”hidden” id=”tabIndexHidden” runat=”server”

Last thing in markup:


function pageLoad(sender,e)
{
restoreCurrentTabIndex();
}

Family Dysfunction

So, like I said, sometimes TabContainer ClientState is null causing the bizarre ‘number must be less than infinity’ error message, I assume because null is not less than infinity. To work around it use the Javascript parseInt function to make sure TabContainer has an ActiveTabIndex it can grok.

Resolving Our Little Differences

In saveCurrentTabIndex:
tabcontainer.set_activeTabIndex(parseInt(tabIndex.value));

Now the ActiveTabIndex will be less than Infinity to the relief of all concerned with the possible exception of Buzz Lightyear.

Our Family Counsellor can be located here.. Thanks djs25uk 🙂

typeof(Sys) == “Undefined” in ScriptResource.axd

August 7, 2008

A nasty happening popped up in DEV the other day where suddenly the pages were not rendering properly. They looked like raw HTML without the actual tags visible. Nothing was rendering correctly.

The debugger halted at an Exception thrown on the dynamic markup page here:

script src="/ScriptResource.axd?d=BjTbBXg2XH_V1XmJZ7yHuwSXPH-5-fSrlvV3UUmrU56yYz9IJKvRe5X8yQfCLMoTgHz2ltIqCKU4Xx5

&t=633324698329531250" type="text/javascript" /script
script type="text/javascript"

if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.');

The exception was being thrown because typeof(Sys) == “undefined”.

When In Doubt…
I Googled my brains out and ended up at this Microsoft ASP.NET forum thread which is full of lost souls wandering the dark shores of despair while watching a lucky few spontaneously slip into the parallel dimension of serendipitious suceess without anyone knowing why.

By working backwards from our last known good deployment, employing the 80/20 rule and examining the entrails of a slaughtered ferret the culprit turned out to be gzip compression of Script Resources in the Application Request. Please see this article in DDK OnLine for more details including why we were using gzip on the Request in the first place

As DDK explains, we stopped gzipping Script and Web Resources and our app returned rendered pages worthy of Da Vinci once more.

A BIG clue for us in the aforementioned thread was in the post by mking who turned off his or her WebResourceCompression httpModule and thus resumed sanity.

Why Does The Site Look Horrible ?

Script and Web Resources can contain CSS Style-Sheets. That’s why their failure to load makes the page look like raw HTML. In our case CSS Style-Sheets for Telerik controls were not loading.

Related Posts

Chris Riccio, Demystifying ‘Sys is undefined’, explains just exactly why Sys is undefined.

Addendum 13-Aug-2008

Incorrect Visual Studio Project Template
You will get a similar error ‘Sys is undefined’ if you attempt to use AJAX controls where no HttpHandler exists for the Ajax Partial Postbacks. This can come about if you cut and paste markup code from an Ajax-enabled web site to a non-Ajax enabled web page.

For example, let’s say you want to use Microsoft’s ASP.NET AJAX UpdatePanel, so you cut and paste the markup for asp:ScriptManager and asp:UpdatePanel controls from a web page in one Visual Studio project you’ve got to another. On the second it doesn’t work and you get a JScript error ‘Sys is undefined’. What has probably happened is that the first Visual Studio project was created from the ‘ASP.NET Ajax Enabled Web Site’ project template and the other was not.

If you examine the web.config file for the first site you will find that it defines HttpHandlers for Ajax whereas the second does not viz:


httpHandlers
add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"
/httpHandlers
httpModules
add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
/httpModules

These configuration entries are for ASP.NET Ajax 1.0. They tell your web site where to find the Handler and definitions for the XMLHttpRequest generated by the Ajax Controls. If they don’t exist then the Sys namespace cannot be found and hence is undefined.
See ‘Demystifying Sys is Undefined’, above.

How Does AJAX Work Anyway ?
This article, Ajax Tutorial, from W3Schools gives a very simple and clear explanation of how AJAX works and even shows you how to hand-code an AJAX Handler. Recommended.