🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

【C#】我們來土砲一個 Asp.net Session (Session核心原理)

我們在寫網站一定會使用到 Session
今天就跟大家分享自製微型 Asp.net Session

分析Session->實作Session->使用Session

在實作之前您必須先了解甚麼是Session
網路上一大堆介紹Session文章在此我就不多介紹
或可以點進之前小弟的介紹文來簡單了解 SessionID.cookie,Session傻傻分不清楚??

簡單說明:
Http協議是一個無狀態協議。

核心是 請求=>處理=>回應

每次請求都是獨立不會記住上一次做了甚麼
Session可以幫我們把資料存在Server記憶體,方便我們下次請求使用
上網連線眾多使用者,Server怎麼知道哪份資料,屬於哪個使用者的? 這就要依靠 SessonID
SessionID就像使用者的號碼牌,可以到Server拿相對應的資料

分析:

  1. 使用者請求頁面時會攜帶該網域下Cookies。
  2. Asp.net接收到並使用Key為SessionID的Cookie,使用Cookie的Value來SessionPool中查找屬於使用者的Session。
    如果是第一次請求或是沒有SessionID 會幫他產生一個新的並加入回應的Cookie中
  3. 取得Session物件後就可以在程式中使用。

分析如下圖:

我們作出幾個核心來完成模擬Session:

  1. SessionPool來存放目前所有Session
  2. SessionObject (支援快取在系統記憶體中)
    模擬HttpContext封裝Session

實作:
我要簡單呈現就選擇使用輕便 [泛型處理常式]

ApplicationContext 模擬HttpContext封裝SessionPool
創建一個靜態的SessionPool物件,因為程式都共用此SessionPool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/// <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存取物件設置快取

1
2
3
4
5
/// <summary>
/// 掌管物件存活時間的集合
/// </summary>
private readonly Dictionary<string, CancellationTokenSource> _expireContaner =
new Dictionary<string, CancellationTokenSource>();

在Task.Delay可以讓物件存放在工作執行緒中 等Delay時間到就呼叫 ContinueWith 將物件消毀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// <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物件,沒有會幫她創建一個放在池子中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// <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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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原始碼

__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/own-session/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^