A little while ago we were working on a proof-of-concept of converting our store to AJAX browsing. That is, every click after the first load would load pages entirely using AJAX, complete with browser history and bookmarkable URLs (courtesy of the YUI Browser History Manager). We had it working beautifully, but once it dawned on us that the Google bot can't crawl AJAX URLs we had to scrap the whole thing. (Or, only ever build PageRank for 1 URL - after a brief conversation we decided this was a Bad Thing. The topic of AJAX URLs is a bit too in depth to get into here). We did develop a few cute solutions along the way however, one of which I hope can be of some use to other AJAXheads out there.
The basic problem is this: when a server issues a redirect (3xx) response, modern browsers will automatically follow the redirect without notifying the user. Normally this is fine, except that Yahoo Browser Manager requires you to call YAHOO.util.History.navigate for each URL you want to place in the history. Since the actual URL the user lands on gets intercepted and handled by the browser, our history will not have the right entry. For example, if submitting a form redirects to a confirmation page, we will only get a history entry for the form submit URL instead of the confirmation page (when what we really want to happen is the opposite).
The solution: don't use a 3xx for redirects when you're AJAX browsing. We decided to instead use 204 - No Content which I thought was closest to the semantics we wanted.
Here's the code to do this server-side:
- class ActionController::Base
- def redirect_to_with_ajax_browsing_check(options = {}, response_status = {})
- if request.query_parameters['ajax_browsing']
- url = ajax_browsing_url(url_for(options))
- redirect_to_without_ajax_browsing_check(url)
- if request.method == :post
- response.headers["Status"] = "204 No Content"
- end
- else
- redirect_to_without_ajax_browsing_check(options, response_status)
- end
- end
- alias_method_chain(:redirect_to, :ajax_browsing_check)
- end
The param ajax_browsing is tacked on to each request (the ajax_browsing_url method does this on the server-side, with a Javascript counterpart client-side) to distinguish from normal (non-AJAXy) requests.
Now the redirect response will come through to the client so that we can handle it properly. Here's the client code:
- postFormData: function(target, params) {
- new Ajax.Request(target, {
- method: 'post',
- parameters: params,
- onSuccess: function(transport) {
- Tobi.updateContentWrapper(transport.responseText);
- },
- on204: function(transport) {
- var currentLocation = YAHOO.util.History.getCurrentState("tobi");
- var newLocation = transport.getHeader('Location');
- // If we are being redirected to the same location, then the browser
- // history callback will not fire since the history hasn't changed.
- // So we should manually replace the content in this case
- if (newLocation == currentLocation) Tobi.navigateContentFrame(newLocation);
- else YAHOO.util.History.navigate("tobi", newLocation);
- }
- });
- }
References
PageRank
Prototype Ajax.Request
RFC 2616 - HTTP Status Codes
YUI Browser History Manager