我們在寫網站一定會使用到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。
三.取得Session物件後就可以在程式中使用。
分析如下圖:
我們作出幾個核心來完成模擬Session:
- SessionPool來存放目前所有Session
- SessionObject (支援快取在系統記憶體中)
- 模擬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 控制讀取時的值
/// <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 原始碼
如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^