Themelia Pro A powerful new development platform built on ASP.NET.
 
Storing and Retreiving Http Data
 
 

When working with any web application, it's important to be able to read and write data efficiently and effectively. Whether it's storing a boolean that represents a if a person is logged in, reading a cookie for session persistence, or setting context data to pass down the ASP.NET pipeline, you need mechanisms to easily set and get the data in a unobtrusive manner. Therefore, Themelia provides the Themelia.Web.HttpData class for quick access to various portions of HTTP data.

Generic State Access

While there are many methods for interacting with data, some of the most basic are the following:

  • T GetItem<T>(String name)
  • T GetCacheItem<T>(String name)
  • T GetSessionItem<T>(String name)
  • void SetItem<T>(String name, T item)
  • void SetCacheItem<T>(String name, T item)
  • void SetSessionItem<T>(String name, T item)

As you can see, access to Themelia data isn't normally access, it's generic access. By using these, you never need to cast anything ever again. If you want to store a Boolean, store it as a Boolean. If you want to store your MyCustomDataContact class, store it as a MyCustomDataContact class. Now, while the cache and session methods are so self explanatory there's no reason to say another word, however, the GetItem and SetItem methods may be confusing to some.

In any web development platform, there are different storage mechanisms for your data and you must carefully choose which mechanism to use for each and every piece of data. For example, if you need something accessible for every user of an application, you need to use a static class (no, not "application state" as that was deprecated as ASP.NET 1.0 and only exists for classic ASP migration) If you need something accessible for one user for the entire life time of his or her own current instance of the application, you need to use session state. However, if you need something accessible at various points in the ASP.NET pipeline for a particular user, neither a static class nor session state is appropriate. For this you need to use context state (accessible via HttpContext.Current.Items). This state is present only for the life time of the current request. This is what SetItem and GetItem represent in the HttpData class.

With the information about context state in mind, you can easily do something like this:

namespace ABCCorp.Web
{
    public class BrowserInitProcessor : Themelia.Web.Processing.InitProcessorBase
    {
        public const String BrowserVersion = "BrowserVersion";
 
        //- @Execute -//
        public override InitProcessorBase Execute(System.Web.HttpContext context, params Object[] parameterArray)
        {
            if (!Themelia.Web.Processing.PassThroughHttpHandler.ForceUse)
            {
                String version = CheckBrowser(context);
                if (String.IsNullOrEmpty(version) && Http.GetUrlPart(Http.Position.Ultima) != "unsupported")
                {
                    Http.Redirect("/unsupported/");
                }
                //+
                HttpData.SetItem<String>(BrowserVersion, version);
            }
            //+
            return null;
        }
 
        //- $CheckBrowser -//
        private String CheckBrowser(System.Web.HttpContext context)
        {
            //++
            //TODO: detect web browser here
            //++
            return String.Empty;
        }
    }
}

This preprocessor will run for all non-pass through entities (which includes items in the /Resource_/ folder; see PassThrough Handling for more information) and checks for a proper browser via a method which returns the browser information or a blank string if using an invalid browser. If there isn't a proper browser, check to see if we're already on the /unsupported/ branch, if not, then redirect the user there. However, if there is a proper browser, save the browser information to context state.

By using this preprocessor, you may easily access the browser information at any point in the rest of the Themelia or ASP.NET pipelines or even in the destination web form by the following:

String version = HttpData.GetItem(BrowserPreProcessor.BrowserVersion)

Now, let's change the situation a little. Say you have the following preprocessor:

public class MyCustomInitProcessor : Themelia.Web.Processing.InitProcessorBase
{
    //- @Info -//
    public class Info
    {
        public const String Name = "Name";
    }
 
    //+
    //- @OnPreProcessorExecute -//
    public override InitProcessorBase Execute(HttpContext context, params object[] parameterArray)
    {
        HttpData.SetItem<String>(Info.Name, GetName());
        return null;
    }
 
    //- $GetName -//
    private String GetName()
    {
        //+ return name or...
        return String.Empty;
    }
}

Here were simply setting the name context item do it may be accessed by the following:

String name = HttpData.GetItem<String>(MyCustomInitProcessor.Info.Name);

Scoped Generic State Access

This is all well and good until you install a 3rd party component or write someone else that wants to use the "Name" context item. The moment someone else wants to use "Name", you have a conflict. Therefore, HttpData provides scoped (that is, namespaced) interaction with the same data:

  • T GetScopedItem(String scope, String name)
  • T GetScopedCacheItem(String scope, String name)
  • T GetScopedSessionItem(String scope, String name)
  • void SetScopedItem(String scope, String name, T item)
  • void SetScopedCacheItem(String scope, String name, T item)
  • void SetScopedSessionItem(String scope, String name, T item)

Now our preprocessor looks like this:

public class MyCustomInitProcessor : Themelia.Web.Processing.InitProcessorBase
{
    //- @Info -//
    public class Info
    {
        public const String Scope = "UserInformation";
        //+
        public const String Name = "Name";
    }
 
    //+
    //- @OnPreProcessorExecute -//
    public override InitProcessorBase Execute(HttpContext context, params object[] parameterArray)
    {
        HttpData.SetScopedItem<String>(Info.Scope, Info.Name, GetName());
        return null;
    }
 
    //- $GetName -//
    private String GetName()
    {
        //+ return name or...
        return String.Empty;
    }
}

With access to that item like this:

String name = HttpData.GetScopedItem<String>(MyCustomInitProcessor.Info.Scope, MyCustomInitProcessor.Info.Name);

Now anyone else can use "Name" all they want and there will be no conflicts. This same scoping of data is available for context, cache, and session state. It's also highly recommended that you always scope everything. You have no idea when a 3rd party component will want to use the same session name as you. Not only that, but when you catch an exception on your web site, it's normally a good idea to save all context and session state as well.

When you are using scoped information, you can see right away to what a particular context, cache, or session item relates. No more trying to figure out if a certain "item" variable represents a user name or a product name. In fact, our browser preprocessor should also be scoped to in the same way.

As a side tip, when doing scoping, it may be a good idea to prefix your system-level scopes with codes. For example, internally Themelia uses __$ for all Themelia related items (so... don't use __$!) By doing this you make it more obvious what data is for your system internals and what parts are application specific.

Importing Data

Sometimes you don't simply want to save one piece of information at a time. For example, if you just parsed a user name, email address, and user ID, you could go ahead and add them one at a time, but these are logically related entities. If it's something that's very important to the application, perhaps you should just create a class for it, but if not, you may create a Themelia map (of type Map where T is whatever you want), populate it with data, and import it into a certain state.

Here are the available import state methods:

  • void ImportItemMap<T>(Map<String, T> map)
  • void ImportCacheMap<T>(Map<String, T> map)
  • void ImportSessionMap<T>(Map<String, T> map)
  • void ImportScopedItemMap<T>(String scope, Map<String, T> map)
  • void ImportScopedCacheMap<T>(String scope, Map<String, T> map)
  • void ImportScopedSessionMap<T>(String scope, Map<String, T> map)

Here's an example:

Themelia.Map map = new Themelia.Map();
map.Add("UserName", "John Doe");
map.Add("Email", "jdoe@tempuri.org");
map.Add("ID", "JO3E43WNO");
//+
HttpData.ImportScopedItemMap<String>("UserData", map);

Here were are importing a Map (which is actually of type Map<String, String>) into context state. We could do the exact same thing for session state as well. We could even use the same idea to set a series of boolean settings into our cache.

Cookies

In addition to all these mechanisms for interacting with data, there's one more storage mechanism that's critical for applications: cookies. Though you can go wild with your usage of cookies, Themelia provides three methods to provide for most of the instances of cookie use:

  • String GetCookie(String name)
  • void SetCookie(String name, String value)
  • void SetCookie(String name, String value, TimeSpan timeSpan)
  • void SetCookie(String name, String value, DateTime dateTime)

Here are some basic examples of using these methods:

//+
HttpData.SetCookie("UserGuid", "6dd31c0a-9732-454f-a0d7-45f38ba40e2f");
//+
//+ Or set via time span
HttpData.SetCookie("UserGuid", "6dd31c0a-9732-454f-a0d7-45f38ba40e2f", new TimeSpan(30, 0, 0, 0));
//+
//+ Or set via date time
HttpData.SetCookie("UserGuid", "6dd31c0a-9732-454f-a0d7-45f38ba40e2f", DateTime.Now.AddMonths(1));
//+
//+ On the next request...
String userGuid = HttpData.GetCookie("UserGuid");

It's critical to remember that you never want to try to read a cookie after you set a cookie. You are reading the cookie from the request header and writing to the response header. There's no way to set a cookie (response) then immediately read it (request) as they are two different worlds. That's just the mechanics of how cookies work anywhere. If you just keep those mechanics in mind, you shouldn't have a problem using cookies.

There are actually a more ways of using the HttpData class to interact with data, however, these are the most basic and most general concepts. The rest are more specific to HTTP services, which is a topic for a different discussion.