我们在写网站一定会使用到Session
今天就跟大家分享自製微型 Asp.net Session
分析Session->实作Session->使用Session
在实作之前您必须先了解甚么是Session
网路上一大堆介绍Session文章在此我就不多介绍
或可以点进之前小弟的介绍文来简单了解 SessionID.cookie,Session傻傻分不清楚??
简单说明:
Http协议是一个无状态协议。核心是 请求=>处理=>回应
每次请求都是独立不会记住上一次做了甚么
Session可以帮我们把资料存在Server记忆体,方便我们下次请求使用
上网连线众多使用者,Server怎么知道哪份资料,属于哪个使用者的? 这就要依靠 SessonID
SessionID就像使用者的号码牌,可以到Server拿相对应的资料
分析:
使用者请求页面时会携带该网域下Cookies。Asp.net接收到并使用Key为SessionID的Cookie,使用Cookie的Value来SessionPool中查找属于使用者的Session。如果是第一次请求或是没有SessionID 会帮他产生一个新的并加入回应的Cookie中取得Session物件后就可以在程式中使用。
分析如下图:
我们作出几个核心来完成模拟Session:
SessionPool来存放目前所有SessionSessionObject (支援快取在系统记忆体中)模拟HttpContext封装Session
实作:
我要简单呈现就选择使用轻便 [泛型处理常式]
ApplicationContext 模拟HttpContext封装SessionPool
创建一个静态的SessionPool物件,因为程式都共用此SessionPool
/// <summary>/// 请求上下文/// </summary>public class ApplicationContext{ /// <summary> /// 存在Cookie中的SessionID /// </summary> private readonly string MySessionID = "MySessionID"; public HttpRequest Request { get; private set; } public HttpResponse Respone { get; private set; } public ApplicationContext(HttpContext context) { Respone = context.Response; Request = context.Request; } private static SessionPool _container = new SessionPool(); public SessionObject Session { get { return GetSessionObj(); } } /// <summary> /// 从SessionPool中取得Session对象 /// </summary> /// <returns></returns> private SessionObject GetSessionObj() { Guid sessionGuid; HttpCookie CookieSessionID = Request.Cookies[MySessionID]; //如果没有MySessionID的cookie,做一个新的 if (CookieSessionID == null) { sessionGuid = Guid.NewGuid(); HttpCookie cookie = new HttpCookie(MySessionID, sessionGuid.ToString()) { Expires = DateTime.Now.AddDays(60) }; Respone.Cookies.Add(cookie); } else { sessionGuid = Guid.Parse(CookieSessionID.Value); } return _container[sessionGuid]; }}
CacheDictionary 负责快取
使用一个 Dictionary 来对Session存取物件设置快取
/// <summary>/// 掌管物件存活时间的集合/// </summary>private readonly Dictionary<string, CancellationTokenSource> _expireContaner = new Dictionary<string, CancellationTokenSource>();
在Task.Delay可以让物件存放在工作执行绪中 等Delay时间到就呼叫 ContinueWith 将物件消毁
/// <summary>/// 设置快取对象/// </summary>/// <typeparam name="T"></typeparam>/// <param name="key"></param>/// <param name="create"></param>/// <param name="expireIn"></param>/// <returns></returns>public T Set<T>(string key, Func<T> create, TimeSpan expireIn){ //如果此Key被使用 将原本的内容移除 if (_expireTasks.ContainsKey(key)) { _expireTasks[key].Cancel(); _expireTasks.Remove(key); } var expirationTokenSource = new CancellationTokenSource(); var expirationToken = expirationTokenSource.Token; //物件快取 Task.Delay(expireIn, expirationToken).ContinueWith(_ => Expire(key), expirationToken); _expireTasks[key] = expirationTokenSource; return (T)(this[key] = create());}
SeesionPool 存放所有Session
取Session会判断此Guid是否有对应的Session物件,没有会帮她创建一个放在池子中
/// <summary>/// 存放所有Session池子/// </summary>public class SessionPool{ private Dictionary<Guid, SessionObject> _SessionContain = new Dictionary<Guid, SessionObject>(); public SessionObject this[Guid index] { get { SessionObject obj; if (_SessionContain.TryGetValue(index, out obj)) { return obj; } else { obj = new SessionObject(); _SessionContain.Add(index, obj); } return obj; } }}
SessionObject 控制读取时的值 (一般我们所使用的Session)
/// <summary>/// Session物件/// </summary>public class SessionObject{ private CacheDictionary cache = new CacheDictionary(); public object this[string index] { get { return GetObj(index); } set { SetCache(index, value); } } private void SetCache(string key, object value) { cache.Set(key, () => value); } private object GetObj(string key) { return cache.GetOrDefault(key, () => default(object)); }}
使用:
在建构子中创建一个 ApplicationContext 之后,即可Asp.net那样来使用Session
private ApplicationContext app;public SessionHanlder(){ app = new ApplicationContext(HttpContext.Current);}public void ProcessRequest(HttpContext context){ if (null == app.Session["Time"]) { app.Session["Time"] = $"Hello {DateTime.Now.ToString("yyyy-MM-dd hh-mm-ss")}"; } context.Response.Write(app.Session["Time"]); context.Response.ContentType = "text/plain";}
上面程式是简单模拟Session核心作用的程式
但并未处理多执行绪并发读写...等等问题,所以建议别再实际专案中使用XD!!
专案使用 VS2015 GitHub原始码