ASP.NET Core Advent Calendar 13日目です。
初日にSignalRでエントリ書きましたが、もともと予定していたセッションについて簡単に追いかけてみましょう。ASP.NET Core でSessionを使う場合、まずはこのドキュメントを読むのがよいでしょう。
Managing Application State | Microsoft Docs
で、Sessionを使うにはまずMicrosoft.AspNetCore.Sessionパッケージを追加しろとあります。その後のNoteに書いてあることを無視して、とりあえずこのパッケージだけ追加して、Startup.csを次のように書いてみましょう。
public void ConfigureServices(IServiceCollection services) { //services.AddDistributedMemoryCache(); services.AddSession(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseSession(); }
これで実行するとこんなエラーが出ます。
Unhandled Exception: System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'. at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
DIでIDistributedCacheを実装したサービスが見つからないということでエラーが出ます。無視したNoteにある通り、Sessionの機能はIDistributedCacheに依存して実装しているので少なくとも1つはその実装を追加しないといけません。実際に、Microsoft.AspNetCore.SessionはMicrosoft.Extensions.Caching.Abstractionsに依存していることもパッケージからわかります。
開発用途限定ですが、IDistributedCacheを実装するパッケージがMicrosoft.Extensions.Caching.Memoryです。これは特に設定の必要もなく、次のようにコード一行追加するだけで動きます。
public void ConfigureServices(IServiceCollection services) { services.AddDistributedMemoryCache(); services.AddSession(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseSession(); }
この開発用途限定と言っているパッケージ、名前の通りインメモリーにキャッシュを保存します。注意しないといけないのは複数サーバーでスケールアウトする場合で、当然各サーバーで別々にセッションをメモリーに保存するので、別のサーバーに行くとその前にアクセスしたセッションにはアクセスできません。というわけで本番環境では、IDistributedCacheを実装しているサービスを用意する必要があります。ASP.NET Coreチームから提供されているのはSQLServerとRedisに格納するものになります。
よし、これで複数サーバーでSticky Sessionの設定をせずともセッションを共有できる... という訳には実はいきません。そのことは実はだいぶ前のエントリで書いています。
この記事に書いている通り、ASP.NET Coreではセッションデータを格納する際に暗号化しています。これはIDataProtectionというインターフェースを実装するサービスがその役割をになっており、デフォルトはマシンごとに固有の鍵をローカルファイルシステムにファイルとして保存します。さらにこのインターフェースはIDistributedCacheとは独立しているので、Redis Cachingを使ったとしてもIDataProtectionはデフォルト実装のままです。デフォルト実装の際に保存されるファイルパスはこのコードにあります。
DataProtection/FileSystemXmlRepository.cs at rel/1.1.0 · aspnet/DataProtection · GitHub
実際、このようなファイルが保存されています。
.aspnet/DataProtection-Keys/key-c28289ea-0e92-4e6b-94ff-6985ba6700ac.xml
$ cat .aspnet/DataProtection-Keys/key-c28289ea-0e92-4e6b-94ff-6985ba6700ac.xml
<?xml version="1.0" encoding="utf-8"?>
<key id="c28289ea-0e92-4e6b-94ff-6985ba6700ac" version="1">
<creationDate>2016-11-30T12:24:55.245682Z</creationDate>
<activationDate>2016-11-30T12:24:55.220079Z</activationDate>
<expirationDate>2017-02-28T12:24:55.220079Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
<!-- Warning: the key below is in an unencrypted form. -->
<value>uT3l6pCiERM0Mwd6F8T4nOnnv9DmwSU6ii8AaZj6XG6qR0MorUN1Akxa5Lqivt8hKfu2L9TBmryC16I6BuOXrQ==</value>
</masterKey>
</descriptor>
</descriptor>
</key>
というわけですので、IDataProtectionも必要に応じて共有できる実装に切り替えましょう。Data Protectionについては別のドキュメントで説明されており、デフォルトでは同じ物理パスを指定してもマシン固有になることも書かれています。
Configuring Data Protection | Microsoft Docs
上に引用した記事を書いた時点では、.NET Core on Liniuxで(比較的)容易に用意できる共有先はNFSくらいだったのですが、現在はRedis実装がPreviewですが用意されています。またAzureStorage向けのものも用意されています。
といったところで長くなってきたので、実際に動くコードで検証するのは続編でかきたいと思います。