ASP.NET 網站預設 session state 只存活在對應 web server 的 IIS 中,也就是說在多台 web server 服務或具有 failover 機制下,當切到另一台 web server 的時候喪失所有 session state 資訊,因此可以考慮將 session state 存放在 SQL Server 中,讓所有 web server 共享相同 session state 資訊。
前言
傳統開發方式如 ASP.NET WebForm / MVC 都可以方便存取 session 狀態資料,但預設 session state 只存活在對應 web server 的 IIS 中,也就是說在多台 web server 服務或具有 failover 機制下,當切到另一台 web server 的時候喪失所有 session state 資訊,當然登入資訊也習慣放置在 session 中,因此會造成用戶莫名被登出的情況;這時候我們可以透過 SQL Server 作為 session state 的容器,透過資料庫保存這些狀態,不論哪一台服務的 web server 都可以取得相同 session 資料,以下進行介紹。
建立 SQL Server 環境
利用 aspnet_regsql.exe 工具建立 session state 存放的資料庫環境,該工具位於 C:\Windows\Microsoft.NET\Framework\v4.0.30319 資料夾中,因此先切換目錄於此。
執行後會建立出名為 ASPState 資料庫,並且包含 2 張資料表及相關 Stored Procedure 程式。
設定 Web.config
可以在專案 web.config 加入以下設定,表示以 session state 是以 SQLServer 模式進行。
<system.web>
...
<!--session state from SQL Server-->
<sessionState mode="SQLServer" timeout="20" sqlConnectionString="server=OO.OOO.OOO.OO;uid=OO;pwd=OOOOO;"></sessionState>
...
</system.web>
加註 Serializable 標籤
執行後第一個面臨到的問題就是那些原本存放在 session 中的物件,若沒加註 Serializable 標籤就會報錯。
System.Runtime.Serialization.SerializationException: 未將類型 'ResultSsoLoginDto' (於組件 'MyUtility, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 中) 標記為可序列化。
那是因為現在 session state 都會放置在 DB 中,所以都需要執行序列及反序列化動作,才可以順利存取該物件資料。這個解決方式就在所有須放置到 session 中的物件都加註 Serializable 標籤如下就可以了。
前端 JSON 格式逆襲
原本覺得一切都相當美好,但就在進行其他系統串接測試時,發現被註記 Serializable 標籤的物件,在被前端透過 web api 取得該物件時,獲得的 JSON 格式怪怪的造成前端判讀錯誤。
[
{
"<Token>k__BackingField":"CA1B21B2-B23C-4327-B99B-26AD34CB95C2",
"<ExpiredMin>k__BackingField":"20",
"<CName>k__BackingField":"王XX",
"<UserAccount>k__BackingField":"0c0312689f607d118001ede12c19147c",
"<IsForce>k__BackingField":false,
"<UnLockTime>k__BackingField":"0001-01-01T00:00:00",
"<PerserilNo>k__BackingField":"2639ef75171256d434ee07ab4c83ba5c",
"<EmpId>k__BackingField":"1010234",
"<CmpSerilNo>k__BackingField":"cae6fcca901c04220beeb67362447662",
"<IsFirstLogin>k__BackingField":false,
"<Result>k__BackingField":"100",
"<ResultMsg>k__BackingField":"成功",
"<ResultValue>k__BackingField":0
}
]
可以透過以下方法全域調整 JSON 序列化時忽略 Serializable 標籤。
private void Application_Start(object sender, EventArgs e)
{
// ...
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
};
}
在前端透過 web api 取得該物件時,獲得的 JSON 格式就正確了。
[
{
"Token":"9F151070-4A53-48C3-A729-F6DFB35EA462",
"ExpiredMin":"20",
"CName":"王XX",
"UserAccount":"0c0312689f607d118001ede12c19147c",
"IsForce":false,
"UnLockTime":"0001-01-01T00:00:00",
"PerserilNo":"2639ef75171256d434ee07ab4c83ba5c",
"EmpId":"1010234",
"CmpSerilNo":"cae6fcca901c04220beeb67362447662",
"IsFirstLogin":false,
"Result":"100",
"ResultMsg":"成功",
"ResultValue":0
}
]
後端反序列化的逆襲
接著又發現後端透過 httpClient 取得相同 web api 回傳物件時,在反序列化的時候出錯了。
最後直接移除 全域 JSON 序列化忽略 Serializable 標籤
的設定後,只要在原本加註 Serializable 標籤的物件中在加註 JsonObject 即可兼容。
參考資訊
如何設定與啟用 ASP.NET 的 SQLServer 工作階段狀態模式
Using Serializable attribute on Model in WebAPI
希望此篇文章可以幫助到需要的人
若內容有誤或有其他建議請不吝留言給筆者喔 !