-
Notifications
You must be signed in to change notification settings - Fork 0
MS_ASPNETCoreSession
少し難しい。
補足(何が「難しい」のか): ASP.NET Session と比べて、
前提が根本的に変わっているためである。
ASP.NET (System.Web) ASP.NET Core 既定で有効か 有効(何もしなくても使える) 無効(明示的に有効化が必要) 格納できる型 任意のオブジェクト(シリアライズ) byte[]とstringのみ保存先の抽象 SessionState プロバイダー IDistributedCache既定の保存先 InProc(プロセス内) メモリ内( AddDistributedMemoryCache)IsNewSessionあり 無い Cookie 名 ASP.NET_SessionId既定 .AspNetCore.Session(変更可)「難しい」の実体は、
の 3 点に整理できる。特に 3 が見落とされやすい。
文字列しか格納できなくなったので、複雑なオブジェクトは JSON に変換する。
- 参考
- Using Sessions and HttpContext in ASP.NET 5 and MVC6
https://benjii.me/2015/07/using-sessions-and-httpcontext-in-aspnet5-and-mvc6/
- Using Sessions and HttpContext in ASP.NET 5 and MVC6
補足(正確には
byte[]が基本):ISessionが持つのは
Set(string, byte[])/TryGetValue(string, out byte[])で、
SetString/GetString/SetInt32/GetInt32は
その上の拡張メソッドである。// 標準で用意されているのはこれだけ HttpContext.Session.SetString("name", "yamada"); HttpContext.Session.SetInt32("age", 30); var name = HttpContext.Session.GetString("name"); // オブジェクトは自分で JSON にする public static void SetObject<T>(this ISession s, string key, T value) => s.SetString(key, JsonSerializer.Serialize(value)); public static T? GetObject<T>(this ISession s, string key) => s.GetString(key) is { } v ? JsonSerializer.Deserialize<T>(v) : default;この「不便さ」は意図的である。
ASP.NET のBinaryFormatterによるセッション格納は、
.NET の Serialize で述べた通り
脆弱性(CWE-502)と版の非互換を抱えていた。
明示的に JSON へ変換させることで、
何が保存されるかを開発者が把握できるようにしている。設計上の指針: そもそも
セッションに大きなオブジェクトを入れない
(ID だけ入れて、実体は DB / キャッシュから引く)のが望ましい。
-
IsNewSession プロパティが無くなっている。
-
IsNewSession と言う Key を、Session ストアに突っ込むなど。
-
参考
- OpenTouryo/BaseMVControllerCore.cs at develop · OpenTouryoProject/OpenTouryo
https://github.com/OpenTouryoProject/OpenTouryo/blob/develop/root/programs/CS/Frameworks/Infrastructure/Framework/Presentation/BaseMVControllerCore.cs#L112
- OpenTouryo/BaseMVControllerCore.cs at develop · OpenTouryoProject/OpenTouryo
補足(
IsAvailableとの違い):ISessionには
IsAvailableというプロパティがあるが、これは
「セッションが読み込み済みで使える状態か」を表すもので、
「新規セッションか」ではない。原文の回避策(判定用のキーを自分で入れておく)は現在も有効で、
定石と言ってよい。if (HttpContext.Session.GetString("__init") is null) { HttpContext.Session.SetString("__init", "1"); // 新規セッション // 初期化処理 }注意: ASP.NET Core のセッションは
何か書き込むまで Cookie が発行されない(遅延生成)。
このため「新規かどうか」を判定するには、
上記のように何かを書き込む必要がある。
- Configure メソッド
// Sessionを使用する。
app.UseSession(new SessionOptions()
{
IdleTimeout = TimeSpan.FromMinutes(30), // ここで調整
IOTimeout = TimeSpan.FromSeconds(30),
Cookie = new CookieBuilder()
{
Expiration = TimeSpan.FromDays(1), // 効かない
HttpOnly = true,
Name = "mvc_session",
Path = "/",
SameSite = SameSiteMode.Strict,
SecurePolicy = CookieSecurePolicy.SameAsRequest
}
});- ConfigureServices メソッド
// Sessionを使用する。
services.AddSession();補足(
Expirationが「効かない」理由): 原文のコメントは正しい観測で、
仕様として無視される。セッション Cookie は ブラウザを閉じたら消える
(=有効期限を持たない)Cookie として発行される決まりであり、
CookieBuilder.Expirationを設定しても
SessionMiddlewareはそれを使わない。セッションの寿命 = IdleTimeout(サーバ側の保持期間) Cookie の寿命 = ブラウザ セッション(明示的な期限なし)有効期限を延ばしたい場合は
IdleTimeoutを調整する
(これがサーバ側でセッション データを保持する時間)。
補足(.NET 6 以降の書き方/最新化):
Startup.csは
最小ホスティング モデルにより既定で生成されなくなった
(ASP.NET Core における DI 参照)。
現在はProgram.csに書く。var builder = WebApplication.CreateBuilder(args); // ① セッションの保存先(既定はメモリ内。複数台なら分散キャッシュに差し替える) builder.Services.AddDistributedMemoryCache(); // ② セッション自体 builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.IOTimeout = TimeSpan.FromSeconds(30); options.Cookie.Name = "mvc_session"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; // 同意バナーの有無に関わらず発行する options.Cookie.SameSite = SameSiteMode.Strict; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; }); var app = builder.Build(); app.UseRouting(); app.UseSession(); // ← UseRouting の後、UseEndpoints/Map より前 app.UseAuthorization(); app.MapControllers(); app.Run();順序が重要である。
UseSession()は
UseRouting()の後、エンドポイント実行より前に置く
(前後を誤るとHttpContext.Sessionが使えず例外になる)。
IsEssential = trueも実務上重要で、
GDPR 同意機能(CookiePolicyOptions)を有効にしている場合、
これを立てないと同意前はセッション Cookie が発行されない。
補足(複数台構成で必要になるもの): ASP.NET Session の
StateServer / SQL Server モードに相当するのが
分散キャッシュ だが、
それだけでは足りない点に注意する。【複数台で セッションを共有するために必要なもの】 ① IDistributedCache の共有 … Redis / SQL Server → セッションの中身を共有する ② データ保護キーの共有 … [データ保護](MS_ASPNETCoreDataProtection) → Cookie の暗号化・復号キーを揃える → 揃っていないと、別サーバに飛んだ瞬間に Cookie を復号できず セッションが「新規」になる ③ ApplicationName の統一 … SetApplicationName()② の見落としが最頻の事故で、
「Redis を入れたのにセッションが切れる」という症状になる
(ASP.NET Coreのデータ保護)。
-
ASP.NET Core MVCでSession管理 - 今日もちょいつか
http://heinlein.hatenablog.com/entry/2017/11/21/141639 -
ASP.NET Core でのセッションと状態の管理
https://learn.microsoft.com/ja-jp/aspnet/core/fundamentals/app-state
- ASP.NET Core での分散キャッシュ
https://learn.microsoft.com/ja-jp/aspnet/core/performance/caching/distributed - ASP.NET Core のミドルウェアの順序
https://learn.microsoft.com/ja-jp/aspnet/core/fundamentals/middleware/#middleware-order
Tags: 移行, プログラミング, .NET開発, .NET Core, ASP.NET, ASP.NET MVC
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。