Persisting information across requests
Fact: The web is stateless.
As "maintaining state is extremely useful", there are plenty of ways to maintain the state across requests.
An introduction to states in a web environment
However, you have to be careful how you do it because there are many ways that a request can be done. The most common are:
- A direct GET request through a bookmark or an URL entered directly in the address bar
- A GET request coming from an external web site (this is essentially the same as a direct request in terms of state: there's no previous state)
- A GET request coming from an internal page of your web site
- A POST request. These usually come from an internal page of your web site, but you can't count on it
In the case of a GET request coming from an internal page, there's also the possibility that a new tab was requested, making the originating tab a valid state. If you've ever seen a revision graph (I sure hope you have), a state graph would be quite similar.
The merging is not exactly present as in the revision graph, but it's there. In fact, an opened tab/window could have an effect on any other tab, not only its parent. Let's say the user opened a tab from a listing of items to edit a particular item. It modifies the item and saves it. The next state in the listing would reflect (ideally) the modifications to the item.
Maintaining the state
Now that you are more familiar with the state in a web environment, let's get to the point: how to persist information across requests. From the discussion above, we can extract two key points:
- Never assume the request comes from an existing state, always check if a state existed before
- Never modify a state directly, always make a copy (in case of tabs)
Most web frameworks (if not all) offer one or more ways of maintaining the state. Most of them rely on the good old cookies, but you have to be careful with those because you don't want to change an existing valid state.
You could also use a session identifier, but you can't always rely on them because different browsers treat sessions differently. Firefox and IE both consider that each tab is in the same session as the originating tab while Chrome considers them differently, it treats each tab as a new window (which is a different session for every browser).
Another (very ugly) way, is to simply pass an identifier (usually a Guid) in the URL all the time. However, you can't assume that the user just won't delete the ?SessionID=... part from your URL before performing a request.
Finally, the Asp.Net WebForms framework simulates a stateful application by wrapping everything in a single form field and doing everything by using post backs (POST requests). A post back does not allow new tabs so it's safe to just change the originating state. It also makes something clear, every GET request is considered a new entry point and does not rely on the previous state to perform the actions.
Cleaning unused states
Persisting all those states can make a dent on your memory usage, that's why you'd want to remove unused (closed tabs/windows) states from the graph. To do so, I suggest that you implement some sort of keep-alive object that sends Ajax request to keep the state of the page alive every x seconds. Once the requests stop coming, you can safely delete a state*.
* I haven't explored the possibility of resurrecting a tab (via undo close tab), this might be an issue and a reason to never delete a state until the user session is dead.
Stateless programming is not a problem easily solved and thoughtful thinking is required before using any of the solutions presented above.