Monday, April 27, 2009

How to handle multiple windows with different states in a web application

I know a lot of people have written a lot of ways in which to do it. But, I have a way which I think works when you actually consider that a new window is a "GET" on the server. Most of the implementations floating around are for "POST". The really difficult problem to crack is that, there is no way for the server to know whether the "GET" which is being processed is a page refresh request OR a File >> New Window Request. There are two ways of doing this, depending upon which kind of application you are:

The Easy Way

The easy way is only possible when your application opens in a full window mode (business application), and you do not show anything like the address bar (even if it is shown, this has to be read only), and you do not show any browser buttons OR right clicks.

This is simple:

1. Every time a window is opened, have a window identifier in the query string.
2. Use this identifier on the server as a part of the key while storing anything in any shared storage for the user (like session or cache).
3. This ensures that for different windows, the server stores the information with different keys.

This works very, very well. Users like to use the cool "New Window" icon to open a new window. 

The Difficult Way

I went through a struggle to get this going because, in a regular browser application, the user can hit the browser buttons and also, change the query string. So, there is no apparent way to handle all these events and ensure that we get a unique window key when the user presses, Page >> New Window, because there is no way to detect whether the user did that from the client.

This is how I finally did it:

1. Used a base class to do all the processing.
2. Uses logic similar to method 1 (first section) for window identifiers.
3. What I did was ensure that before every user request from the client, an AJAX call is made to the server to update a variable.
4. It is possible to detect page refreshes done by the user through any mechanism using the below:

        window.onbeforeunload = function()
    {
        if((window.event.clientX<0) || (window.event.clientY<0))
        {
            HandlePageRefreshButtonClick(); >> Make Ajax call inside this function, and then in callback event.ReturnValue = true so that refresh will happen
        }
        else 
        {
            HandleRightClickPageRefreshClick();
        }
    }

5. So, page refresh is covered, for any actions from your page, you can do the AJAX call to update the server FLAG before doing the actual GET request.
6. Any request which goes to the server which does not have the FLAG set will thus be the "NEW WINDOW OPEN" request. This can be handled, and you can redirect the user to the same url but with a new window identifier.
7. Clear the FLAG at the end of every page load, so that it is set only before a new request comes from the server.
8. This means, before you redirect also, you need to set the FLAG in the session. An easy way is to handle all redirect methods through a static method in your class rather than the ASP.NET Response object.
9. Ensure that all links in all pages call a JS method, and this method makes the AJAX call before you open a new window or go to another page. This can again be abstracted into a generic JS class.
10. On the server, to see whether you need to redirect with a new window identifier:
         a. Ignore for login page.
         b. Ignore is page is posted back.
         c. If FLAG is null, user did not request the window explicitly - so handle this case.

11. If a user modifies the window key manually, then handle that case by again generating a new window identifier on the server and redirecting to the same page with the new identifier.
12. You can handle Ctrl + N also, and ensure before page refresh to make AJAX call.
13. Remember to use ALL session variables WITH the window identifier as the key, otherwise, the session will leak to the other windows open.
14. If you encrypt the query string, remember you will not find it easy to send new values through query strings anymore. Remember to allow for url decode and encode after encryption and before decryption.
15. Even the script manager used to have the web service reference on every page can be injected into every page from the base page

The method is kind of complex, but it does work for 2 GETs. I have tested thoroughly.

No comments:

Post a Comment