Click here to Skip to main content
15,885,309 members
Articles / Web Development / ASP.NET

Session, Cookie, Query String & Cache Variables Unified

Rate me:
Please Sign up or sign in to vote.
4.06/5 (8 votes)
1 Dec 2009Ms-PL5 min read 55.7K   297   34  
A wrapper to work with the session, cookie, query string or cache in a unified, flexible and type safe manner while offering full support for JSON serialization.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web;
using System.Collections;
using System.Text;
using System.Web.Security;
using System.Web.Caching;

#region Storage types
// The storage types from which the value is retrieved.
public enum Sources
{
    None = 0,
    Session = 1,
    Cookie = 2,
    QueryString = 4,
    Cache = 8,
    SharedCache = 16,
    ServerCookie = 32
}

// The storage types where the value is saved.
// Each type has a byte value that can be added up to denote several types within the same value.
public struct Targets
{
    public static readonly byte
    None = 0,
    Session = 1,
    Cookie = 2,
    QueryString = 4,
    Cache = 8,
    SharedCache = 16,
    ServerCookie = 32;
}
#endregion

namespace System.Web
{

    /// <summary>
    /// Static object to get/set Session, Cookie and Querystring variables
    /// </summary>
    public partial class LocalBase
    {
        //private static HttpContext _context;

        public static HttpContext Context
        {
            get { return HttpContext.Current; }
        }
        //        _context = HttpContext.Current ?? _context;
        //        return _context;
        //    }
        //    set
        //    {
        //        _context = HttpContext.Current = value;
        //    }
        //}

        public static char KeyDelimiter = '_';
        public static TimeSpan DefaultCookieLifeTime = TimeSpan.FromDays(100);
        public static TimeSpan DefaultCacheLifeTime = TimeSpan.FromMinutes(20);
        public static TimeSpan DefaultSessionLifeTime
        {
            get { return TimeSpan.FromMinutes(Context.Session.Timeout); }
            set { Context.Session.Timeout = (int)value.TotalMinutes; }
        }

        //                                       COOKIES

        public static bool CookiesSupported
        {
            get { return Context.Request.Browser.Cookies; }
        }

        public static bool SetCookie<T>(string key, T value)
        {
            return SetCookie<T>(key, value, DefaultCookieLifeTime, false, false, null, null, null, null);
        }

        public static bool SetCookie<T>(string key, T value, bool compress, bool encrypt)
        {
            return SetCookie<T>(key, value, DefaultCookieLifeTime, compress, encrypt, null, null, null, null);
        }

        public static bool SetCookie<T>(string key, T value, TimeSpan lifeTime, bool compress, bool encrypt)
        {
            return SetCookie<T>(key, value, lifeTime, compress, encrypt, null, null, null, null);
        }

        public static bool SetCookie<T>(string key, T value, TimeSpan lifeTime, bool compress, bool encrypt,
            string path, string domain, bool? httpOnly, bool? secure)
        {
            string strValue = Serialization.ToJson<T>(value);
            return SetCookie(key, strValue, lifeTime, compress, encrypt, path, domain, httpOnly, secure);
        }

        public static bool SetCookie(string key, string value)
        {
            return SetCookie(key, value, DefaultCookieLifeTime, false, false, null, null, null, null);
        }

        public static bool SetCookie(string key, string value, bool compress, bool encrypt)
        {
            return SetCookie(key, value, DefaultCookieLifeTime, compress, encrypt, null, null, null, null);
        }

        public static bool SetCookie(string key, string value, TimeSpan lifeTime)
        {
            return SetCookie(key, value, lifeTime, false, false, null, null, null, null);
        }

        public static bool SetCookie(string key, string value, TimeSpan lifeTime, bool compress, bool encrypt)
        {
            return SetCookie(key, value, lifeTime, compress, encrypt, null, null, null, null);
        }

        public static bool SetCookie(string key, string value, TimeSpan lifeTime, bool compress, bool encrypt,
            string path, string domain, bool? httpOnly, bool? secure)
        {
            if (!CookiesSupported)
                return false;

            HttpCookie cookie;
            HttpResponse response = Context.Response;
            HttpRequest request = Context.Request;

            try
            {
                // Compression is applied before encryption since the unencrypted text usually
                // contains more repetitive patterns the compression can use.
                if (compress)
                    value = Compression.CompressToBase64(value);

                if (encrypt)
                    value = CookieEncryption.Encrypt(value);

                //value = HttpUtility.HtmlAttributeEncode(value);

                // Split our key by the key delimiter. Any delimiter character after the 1st one is ignored.
                string[] keys = key.Split(new char[] { KeyDelimiter }, 2);
                // Determine if the key contains a subkey
                bool keyContainsParentAndChild = keys.Length > 1;

                if (!keyContainsParentAndChild)
                {
                    cookie = response.Cookies[key];
                    // This also resets the expiry date
                    cookie.Value = value;
                    // The request is also updated in case it is accessed before the response completes
                    request.Cookies[key].Value = value;
                }
                else // The key provided is in the {parent}_{child} format
                {
                    cookie = response.Cookies[keys[0]];
                    // This also resets the expiry date
                    cookie[keys[1]] = value;
                    // The request is also updated in case it is accessed before the response completes
                    request.Cookies[keys[0]][keys[1]] = value;
                }

                if (value == null)
                    cookie.Expires = DateTime.Now.AddDays(-1);
                else
                    cookie.Expires = DateTime.Now.Add(lifeTime);

                if (path != null)
                    cookie.Path = path;

                if (domain != null)
                    cookie.Domain = domain;

                if (httpOnly.HasValue)
                    cookie.HttpOnly = httpOnly.Value;

                if (secure.HasValue)
                    cookie.Secure = secure.Value;

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        public static T GetCookie<T>(string key)
        {
            return GetCookie<T>(key, false, false);
        }

        public static T GetCookie<T>(string key, bool uncompress, bool decrypt)
        {
            return Serialization.FromJson<T>(GetCookie(key, uncompress, decrypt));
        }

        public static string GetCookie(string key) { return GetCookie(key, false, false); }

        public static string GetCookie(string key, bool uncompress, bool decrypt)
        {
            if (!CookiesSupported) return null;

            string value = null;
            HttpCookie cookie;
            string[] keys = key.Split(new char[] { KeyDelimiter }, 2);

            try
            {
                cookie = Context.Request.Cookies[keys[0]];
            }
            catch (HttpRequestValidationException ex)
            {
                //string offensiveKey = ex.Message;
                //offensiveKey = offensiveKey.Substring(offensiveKey.IndexOf(" (") + 2);
                //offensiveKey = offensiveKey.Substring(0, offensiveKey.IndexOf("="));
                //SetCookie(offensiveKey, null);
                Context.Response.Cookies.Clear(); // Clear all cookies from response cookie collection
                Context.Request.Cookies.Clear(); // Clear all cookies from request cookie collection
                throw new HttpRequestValidationException(
                    ex.Message + "\n This error occured because some HTML like characters were found in the cookie." +
                    "\n Please note that as a security measure Univar clears all cookies when this happens.",
                    ex.InnerException);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message, ex.InnerException);
            }

            if (cookie != null)
            {
                if (keys.Length < 2)
                    value = cookie.Value;
                else
                    value = cookie[keys[1]];
            }

            if (decrypt)
                value = CookieEncryption.Decrypt(value, true);

            if (uncompress)
                value = Compression.UncompressFromBase64(value, true);

            return value; // HttpUtility.HtmlDecode(value);
        }


        //                                        SESSIONS

        /// <summary>
        /// Gets and sets the amount of time, in minutes, allowed between requests before
        /// the session-state provider terminates the session.
        /// </summary>
        /// <value>The time-out period, in minutes.</value>
        public static int SessionTimeOut
        {
            get { return Context.Session.Timeout; }
            set { Context.Session.Timeout = value; }
        }

        public static string GetSession(string key)
        {
            return GetSession<string>(key);
        }

        public static T GetSession<T>(string key)
        {
            if (Context.Session == null)
                throw new InvalidOperationException(
                    "The session state is null. This happens when it is accessed before the PreInit event of the page.");
            object value = Context.Session[key];
            if (value != null)
                return (T)value;
            else
                return default(T);
        }

        public static void SetSession(string key, object value)
        {
            SetSession<object>(key, value);
        }

        public static void SetSession<T>(string key, T value)
        {
            if (value != null)
                Context.Session[key] = value;
            else
                Context.Session.Remove(key);
        }


        //                                      QUERY STRINGS


        /// <summary>
        /// Gets the query string.
        /// </summary>
        /// <returns>Any information in the query string</returns>
        public static string GetQueryString()
        {
            return Context.Request.Url.Query;
        }

        public static T GetQueryString<T>(string key)
        {
            return GetQueryString<T>(key, false, false);
        }

        public static T GetQueryString<T>(string key, bool uncompress, bool decrypt)
        {
            return Serialization.FromJson<T>(GetQueryString(key, uncompress, decrypt));
        }

        public static string GetQueryString(string key)
        {
            return GetQueryString(key, false, false);
        }

        public static string GetQueryString(string key, bool uncompress, bool decrypt)
        {
            // Get the the value under the specified key.
            // It took me quite a long time to figure this out but for unknown reasons, the request
            // replaces every '+' character in the query with an empty character. 
            // To compensate all spaces need to be restored as their original '+' equivalent.
            string value = Context.Request.QueryString[key];

            if (value == null)
                return null;

            value = value.Replace(" ", "+");

            if (decrypt)
                value = CookieEncryption.Decrypt(value, true);

            if (uncompress)
                value = Compression.UncompressFromBase64(value, true);

            return HttpUtility.UrlDecode(value);
        }

        public static NameValueCollection SetQueryString(string key, string value)
        {
            return SetQueryString<string>(key, value, false, false, false, false);
        }

        public static NameValueCollection SetQueryString(string key, string value, bool skipRefresh)
        {
            return SetQueryString<string>(key, value, false, false, false, skipRefresh);
        }

        public static NameValueCollection SetQueryString(string key, string value, bool replaceOriginal, bool skipRefresh)
        {
            return SetQueryString<string>(key, value, replaceOriginal, false, false, skipRefresh);
        }

        public static NameValueCollection SetQueryString(NameValueCollection originalQueryString,
            string key, string value, bool compress, bool encrypt, bool skipRefresh)
        {
            return SetQueryString<string>(originalQueryString, key, value, compress, encrypt, skipRefresh);
        }

        public static NameValueCollection SetQueryString<T>(string key, T value)
        {
            return SetQueryString<T>(Context.Request.QueryString, key, value, false, false, false);
        }

        public static NameValueCollection SetQueryString<T>(string key, T value, bool compress, bool encrypt)
        {
            return SetQueryString<T>(Context.Request.QueryString, key, value, compress, encrypt, false);
        }

        public static NameValueCollection SetQueryString<T>(string key, T value, bool compress, bool encrypt, bool skipRefresh)
        {
            return SetQueryString<T>(Context.Request.QueryString, key, value, compress, encrypt, skipRefresh);
        }

        public static NameValueCollection SetQueryString<T>(string key, T value, bool replaceOriginal,
            bool compress, bool encrypt, bool skipRefresh)
        {
            return SetQueryString<T>(replaceOriginal ? null : Context.Request.QueryString, key, value,
                compress, encrypt, skipRefresh);
        }

        public static NameValueCollection SetQueryString<T>(NameValueCollection originalQueryString,
            string key, T value, bool compress, bool encrypt, bool skipRefresh)
        {
            NameValueCollection nvc = CloneQueryCollection(originalQueryString);
            string queryValue = Serialization.ToJson<T>(value, Format.None, false);

            // Compression is applied before encryption since the unencrypted text usually
            // contains more repetitive patterns the compression can use.
            if (compress)
                queryValue = Compression.CompressToBase64(queryValue);

            if (encrypt)
                queryValue = CookieEncryption.Encrypt(queryValue);

            if (value != null)
                if (nvc[key] == null)
                    nvc.Add(key, queryValue);
                else
                    nvc[key] = queryValue;
            else
                nvc.Remove(key);

            if (!skipRefresh)
                Context.Response.Redirect(Context.Request.Url.AbsolutePath + "?" + ConstructQueryString(nvc));

            return nvc;
        }

        public static string ConstructQueryString(NameValueCollection queryString)
        {
            string queryText = "";
            for (int i = 0; i < queryString.Count; i++)
                queryText += string.Format("{0}{1}={2}", i == 0 ? "" : "&", queryString.Keys[i], queryString[i]);

            if (queryText.Length > 2048)
            {
                throw new Exception(
                    "The size of the query string has reached " + queryText.Length + "(Maximum allowed is 2048).");
            }

            return queryText;// HttpUtility.UrlEncode(queryText);
        }

        public static string ConstructQueryString(bool clearActualQueryString, params object[] keyValuePairs)
        {
            // Get an editable copy of the query string collection which does not allow editing.
            NameValueCollection nvc = CloneQueryCollection(clearActualQueryString ? null : Context.Request.QueryString);
            if (keyValuePairs != null)
            {
                for (int i = 0; i < keyValuePairs.Length; i = i + 2)
                {
                    if (keyValuePairs[i + 1] != null) // Delete keys with null values
                    {
                        if (nvc[keyValuePairs[i].ToString()] == null)
                            nvc.Add(keyValuePairs[i].ToString(), keyValuePairs[i + 1].ToString());
                        else
                            nvc[keyValuePairs[i].ToString()] = keyValuePairs[i + 1].ToString();
                    }
                }
            }
            return ConstructQueryString(nvc);
        }

        /// <summary>
        /// The query string collection that the context request returns is inherently read only.
        /// This function constructs and returns an editable copy of it.
        /// </summary>
        /// <param name="originalQueryString">The query string collection to copy</param>
        /// <returns>An editable copy of the query string</returns>
        private static NameValueCollection CloneQueryCollection(NameValueCollection originalQueryString)
        {
            NameValueCollection nvc = new NameValueCollection();
            if (originalQueryString != null)
                nvc.Add(originalQueryString);
            return nvc;
        }

        public static void CopyQueryStringToSession(bool clearQueryString)
        {
            foreach (KeyValuePair<string, string> keyValue in Context.Request.QueryString)
                SetSession(keyValue.Key, keyValue.Value);

            if (clearQueryString)
                ClearAnchorAndQueryString();
        }

        public static void CopyQueryStringToCookie(TimeSpan lifetime, bool clearQueryString)
        {
            foreach (KeyValuePair<string, string> keyValue in Context.Request.QueryString)
                SetCookie(keyValue.Key, keyValue.Value, lifetime);

            if (clearQueryString)
                ClearAnchorAndQueryString();
        }

        public static void ClearAnchorAndQueryString()
        {
            Context.Response.Redirect(Context.Request.Url.AbsolutePath);
        }

        /// <summary>
        /// Gets the anchor string from a URL. Its usage is limited to code generated URLs only.
        /// </summary>
        /// <param name="url">The URL from which to get the anchor tag.
        /// Please note that the anchor tag is never sent as part of the HTTP request by any browser and 
        /// therefore it is inaccessible from the server side.
        /// So this method can only be used with code generated URLs only.
        /// </param>
        /// <returns></returns>
        public static string GetAnchor(string url)
        {
            string[] urlParts = url.Split('#');
            return urlParts.Length > 1 ? urlParts[1] : null;
        }

        /// <summary>
        /// Sets the anchor value in the actual page url.
        /// </summary>
        /// <param name="control">The control to which the page will be anchored to.</param>
        public static void SetAnchor(System.Web.UI.Control control)
        {
            RedirectTo(Context.Request.Url.PathAndQuery, "", control.ClientID);
        }

        /// <summary>
        /// Sets the anchor value in the actual page url.
        /// </summary>
        /// <param name="anchor">The client control ID to which the page will be anchored to.</param>
        public static void SetAnchor(string anchor)
        {
            RedirectTo(Context.Request.Url.PathAndQuery, "", anchor);
        }

        public static void RedirectTo(string url, string queryString) { RedirectTo(url, queryString, null); }

        public static void RedirectTo(string url, string queryString, string anchor)
        {
            //if (width + height > 0) ctx.Response.Redirect(url, "_blank",
            //    string.Format("menubar=0,width={0},height={1}", width, height));
            Context.Response.Redirect(CreateUrl(url, queryString.ToString(), anchor));
        }

        public static string CreateUrl(string baseUrl, string queryString, string anchor)
        {
            if (!string.IsNullOrEmpty(queryString))
                baseUrl = baseUrl.TrimEnd('?') + "?" + queryString.TrimStart('?');
            if (!string.IsNullOrEmpty(anchor))
                baseUrl = baseUrl.TrimEnd('#') + "#" + anchor.TrimStart('#');
            return baseUrl;
        }

        public static void RefreshPage()
        {
            Context.Response.Redirect(Context.Request.Url.PathAndQuery, true);
        }


        //                                         CACHE

        public static void SetCache(string key, object value)
        {
            SetCache<object>(key, value, DefaultCookieLifeTime, null, CacheItemPriority.Normal, null);
        }

        public static void SetCache<T>(string key, T value)
        {
            SetCache<T>(key, value, DefaultCookieLifeTime, null, CacheItemPriority.Normal, null);
        }

        public static void SetCache<T>(string key, T value, TimeSpan lifeTime)
        {
            SetCache<T>(key, value, lifeTime, null, CacheItemPriority.Normal, null);
        }

        /// <summary>
        /// Saves a value to the cache. The key associated with the value is associated with the session ID such that
        /// it is not shared with other users.
        /// </summary>
        /// <typeparam name="T">The object type.</typeparam>
        /// <param name="key">The key under which the value is to be saved.</param>
        /// <param name="value">The value to save.</param>
        /// <param name="lifeTime">The lifetime of the cache object.</param>
        /// <param name="cacheDependencies">The file or cache key dependencies for the cache object that determine when it must be removed.</param>
        /// <param name="cacheItemPriority">The cache priority that the cache object is given to determine when it must be removed.</param>
        /// <param name="cacheItemRemovedCallback">The event that is called whenever the cache object is removed.</param>
        public static void SetCache<T>(string key, T value, TimeSpan lifeTime, CacheDependency cacheDependencies,
            CacheItemPriority cacheItemPriority, CacheItemRemovedCallback cacheItemRemovedCallback)
        {
            SetSharedCache<T>(Context.Request.AnonymousID + KeyDelimiter + key, value, lifeTime, cacheDependencies,
                cacheItemPriority, cacheItemRemovedCallback);
        }

        public static string GetCache(string key)
        {
            return GetCache<string>(key);
        }

        public static T GetCache<T>(string key)
        {
            return GetSharedCache<T>(Context.Request.AnonymousID + KeyDelimiter + key);
        }


        //                                      SHARED CACHE


        public static void SetSharedCache(string key, object value)
        {
            SetSharedCache<object>(key, value, DefaultCacheLifeTime, null, CacheItemPriority.Normal, null);
        }

        public static void SetSharedCache<T>(string key, T value)
        {
            SetSharedCache<T>(key, value, DefaultCacheLifeTime, null, CacheItemPriority.Normal, null);
        }

        public static void SetSharedCache<T>(string key, T value, TimeSpan lifeTime)
        {
            SetSharedCache<T>(key, value, lifeTime, null, CacheItemPriority.Normal, null);
        }

        /// <summary>
        /// Saves a value to the cache. The key can be accessed by other users.
        /// </summary>
        /// <typeparam name="T">The object type.</typeparam>
        /// <param name="key">The key under which the value is to be saved.</param>
        /// <param name="value">The value to save.</param>
        /// <param name="lifeTime">The lifetime of the cache object.</param>
        /// <param name="cacheDependencies">The file or cache key dependencies for the cache object that determine when it must be removed.</param>
        /// <param name="cacheItemPriority">The cache priority that the cache object is given to determine when it must be removed.</param>
        /// <param name="cacheItemRemovedCallback">The event that is called whenever the cache object is removed.</param>
        public static void SetSharedCache<T>(string key, T value, TimeSpan lifeTime,
            CacheDependency cacheDependencies, CacheItemPriority cacheItemPriority,
            CacheItemRemovedCallback cacheItemRemovedCallback)
        {
            if (value != null)
                HttpRuntime.Cache.Insert(key, value, cacheDependencies, DateTime.Now.Add(lifeTime),
                  System.Web.Caching.Cache.NoSlidingExpiration, cacheItemPriority, cacheItemRemovedCallback);
            else
                HttpRuntime.Cache.Remove(key);
        }

        public static string GetSharedCache(string key)
        {
            return GetSharedCache<string>(key);
        }

        public static T GetSharedCache<T>(string key)
        {
            object value = Context.Cache[key];
            return value == null ? default(T) : (T)value;
        }

        //                                     AUTHENTICATION

        public static bool UserIsAuthenticated()
        {
            return (Context.User != null
                && Context.User.Identity != null
                && !string.IsNullOrEmpty(Context.User.Identity.Name));
        }

        public static string UserName
        {
            get
            {
                if (Context.User.Identity.IsAuthenticated)
                    return Context.User.Identity.Name;
                // If we have never seen them return the current anonymous key for the user
                string userName = GetCookie("Userkey");
                return !string.IsNullOrEmpty(userName) ? userName : Context.Profile.UserName;
            }
            set
            {
                SetCookie("Userkey", value, DefaultCookieLifeTime);
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior)
Mauritius Mauritius
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions