※この記事はかつて、わんくまBlogで書いていたものです。こちらに移行します
この記事は、Sessionの管理ってどうやるんでしょう?というための記事です。
IHttpModuleを使って、SessionをFilterし、管理クラス外から追加されたSessionの場合は、例外を生成するようにしてみました。
今回はわかりやすくしている都合上、例外を生成してますが、一番いいのは、管理クラス外で生成&追加されたSessionの場合は、
Sessionを削除(Remove)してしまうのが、本来のやり方かなと思います。
(本当は、SessionModuleをオリジナルで作るというものにしたかったんですが、やめました。
また機会があればSessionModuleで実験します)
また、無許可ですが、むらさんのところにあったSessionContainerクラスとSessionManagerクラスを一部改造しました。
(以前、むらさんから改造について、ご了承頂いたので、今は問題なしです。)
■Web.config
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<httpModules>
<!-- 本当はSessionModuleにしたかったので、Remove定義してましたが、今回は見送ったのでコメント(.NET標準のを使う) -->
<!--<remove name="Session" />-->
<add type="Kero.CustomSessionStateModule" name="test" />
</httpModules>
</system.web>
</configuration>
■SessionContainer.vb
Namespace Kero
'セッションコンテナ
Public Class SessionContainer
Private _Object As Object
Private _Expire As Date = DateTime.MaxValue
Public Sub New()
End Sub
Public Sub New(ByVal [object] As Object, ByVal expire As Date)
_Object = [object]
_Expire = [expire]
End Sub
'格納するオブジェクト
Public Property [Object]() As Object
Get
Return _Object
End Get
Set(ByVal value As Object)
_Object = value
End Set
End Property
'セッションの保持期間
Public Property Expire() As Date
Get
Return _Expire
End Get
Set(ByVal value As Date)
_Expire = value
End Set
End Property
End Class
End Namespace
■SessionManager.vb
Namespace Kero
Public Class SessionManager
'セッションに格納(有効期限自動設定)
Public Shared Sub Add(ByVal page As System.Web.UI.Page, _
ByVal key As String, _
ByVal [object] As Object)
page.Session.Add(key, New SessionContainer([object], DateTime.Now.AddMinutes(10)))
End Sub
' セッション格納(有効期限指定)
Public Shared Sub Add(ByVal page As System.Web.UI.Page, _
ByVal key As String, _
ByVal [object] As Object, _
ByVal [expire] As Date)
'コンテナをセッションに追加
page.Session.Add(key, New SessionContainer([object], [expire]))
End Sub
'セッション取得
Public Shared Function [Get]( _
ByVal session As System.Web.SessionState.HttpSessionState, _
ByVal key As String) As Object
'セッションを探す
Dim sc As SessionContainer = TryCast(session.Item(key), SessionContainer)
'取得できたら(かつSessionContainerに入っていたら)返す
If sc IsNot Nothing Then Return sc.Object
'取得できなければ何も返さない
Return Nothing
End Function
'セッション初期化
Public Shared Sub InitializeSession(ByVal page As System.Web.UI.Page)
For i As Integer = page.Session.Keys.Count - 1 To 0 Step -1
Dim name As String = page.Session.Keys(i)
If TypeOf (page.Session(name)) Is SessionContainer Then
' 有効期限切れSessionは削除
Dim sc As SessionContainer
sc = CType(page.Session.Item(name), SessionContainer)
If sc.Expire <= DateTime.Now Then
page.Session.Remove(name)
End If
End If
Next
End Sub
'セッション情報削除
Public Shared Sub Remove(ByRef page As System.Web.UI.Page, _
ByVal key As String)
'セッションに存在する場合、削除
If page.Session.Item(key) IsNot Nothing Then
page.Session.Remove(key)
End If
End Sub
End Class
End Namespace
■CustomSessionStateModule.vb
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports System.Collections
Imports System.Threading
Imports System.Web.Configuration
Imports System.Configuration
Namespace Kero
Public Class CustomSessionStateModule
Implements IHttpModule
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
Public Sub Init(ByVal httpApplication As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
' Webページの実行完了時イベントの登録
AddHandler httpApplication.PostRequestHandlerExecute, AddressOf CustomStateFilter
End Sub
' Webページの実行完了時イベント
Public Sub CustomStateFilter(ByVal sender As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(sender, HttpApplication)
Dim context As HttpContext = app.Context
If context.Session Is Nothing Then
Return
End If
Try
For Each sessionKey As String In context.Session
Dim sessionContainer As SessionContainer = TryCast(context.Session.Item(sessionKey), Kero.SessionContainer)
If sessionContainer Is Nothing Then
Throw New ApplicationException("管理クラス以外からのセッション操作は禁じられています")
ElseIf sessionContainer.Expire <> DateTime.MaxValue AndAlso sessionContainer.Expire <= DateTime.Now Then
Throw New ApplicationException("有効期限が過ぎているセッションが保存されました")
End If
Next
Catch ex As Exception
Throw ex
End Try
End Sub
End Class
End Namespace
■テスト用Webページ(Default.aspx.vb)
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
#If 1 Then
' これはNG
Context.Session("test") = "Hello World"#Else
' これはOK
Kero.SessionManager.Add(Me.Page, "kero", "Hello World", DateTime.Now.AddMinutes(10))
#End If
End Sub
End Class
■実行結果
■まとめ
やっぱりSessionを管理するというのは、いろんな方法や縛りをつけることはできますが、いろいろと面倒で厄介。
また、Session禁止!といっても、そうはいかない部分もあります。
Sessionの追加と削除が行える管理クラスを用意するのが、手っ取り早い対応策ですね。
Session保存先をDBにして、貯めるだけ貯めて、後で一括削除っていうのもありなんですが、
そうすると有効期限でSessionを消したいとかレスポンスを求められている場合などは、ちょっと苦しいですね。
(レスポンスは意識せず、大量データをSessionに保存する場合は、保存先をDBにするのが向いてます)
ご参考になれば幸いです。