I am not a big fan of frame/iframe solutions but iframes are sometimes necessary if you need to incorporate a legacy application within a new portal/application.

One of my main problems with iframes is that the address bar does not change when you navigate to a sub page within the iframe. This means that you cannot copy the current url from the address bar or bookmark the page and expect to see the same page when you revisit the bookmark.

I thought I would try to work around these problems using the url routing engine in ASP.NET MVC. I started with this url routing rule:

routes.MapRoute("FramedApp",
                "framedApp/{*page}",
                 new { controller = "FramedApp", action = "ViewPage", page = "" }); 

Here I use a wildcard rule to capture the complete url after "framedApp/" and pass in that url to the ViewPage method that looks like this:

public ActionResult ViewPage(string page)
{
    ViewData["page"] = page;
    return View();
}

Not much going on here, I just simply pass the captured page url to the view. The view can then use this url to generate the src attribute for the iframe:

<viewdata page="string" />

<div class="framed-application">
    <iframe src="http://localhost/legacyApp/${page}" frameborder="0"></iframe>    
</div>

So what has this accomplished? Well now we can directly navigate to a sub page within the framed legacy app by a simple url syntax, for example: /framedApp/urlFor/legacyPage.aspx. This will make linking to specific pages in the legacy app a lot easier but there is still one big issue left. The url is not going to change when you move from legacyPage.aspx to legacyPage2.aspx, so you still cannot bookmark some pages.

This problem is a little problematic to work around, my first thought was to change the url by javascript everytime you load a page in the iframe, so if you click on a link to go to legacyPage2.aspx (from within the iframe) you could update the address bar to reflect the new page. However you cannot from javascript change the browser url without causing the browser to reload the page, which is not something we want to do. What you can do from javascript is to set the window location hash, the hash is the text after the "#" . The text after the hash sign is normally used to make the browser jump to a specific named element.

The fact that the text after the hash sign can be set by javascript is used to by many ajax applications to support bookmarking and browser history (back button). For example if you view a label in gmail the browser url looks like this:

image

The problem though is that the text after the hash sign is not sent to the server so the url routing solution I tested above will not work. I need to change it into a javascript solution.

Here is what I ended up with (using JQuery):

$(document).ready(function() {
  if (window.location.hash.length > 0)
  {
    var iframe = $('iframe').get(0);        
    iframe.src = "http://localhost/legacyApp/" + window.location.hash.substring(1);    
  }
        
  $('iframe').load(function() {
    var page = this.contentDocument.baseURI.substring("http://localhost/legacyApp/".length);
    window.location.hash = page;            
  });
});

What I do here is on document load I check if the browser url contains any hash argument, if it does I take the value and append it to the legacyApp url. I also hookup the iframe load event to update the hash when a new iframe page has loaded, for example after a you click a link to go to another page in the iframe. 

image

As you can see from the above screenshot the url now contains the url for the framed application, and it will be updated as you navigate the framed application. So bookmarking and copying the url to send to a friend will work. This makes working with web applications that use frames, at least for me a lot less frustrating as you can clearly see the url for the main page and the framed page. No need to right click This Frame->View Frame Info to find out what page you are currently viewing!