在之前的版本中,Session存在于System.Web中,新版ASP.NET 5中由于不在依賴于System.Web.dll庫了,所以相應的,Session也就成了ASP.NET 5中一個可配置的模塊(middleware)了。
ASP.NET 5中的Session模塊存在于Microsoft.AspNet.Session類庫中,要啟用Session,首先需要在PRoject.json中的dependencies節點中添加如下內容:
"Microsoft.AspNet.Session": "1.0.0-beta3"
然后在ConfigureServices中添加Session的引用(并進行配置):
services.AddCaching(); // 這兩個必須同時添加,因為Session依賴于Cachingservices.AddSession();//services.ConfigureSession(null); 可以在這里配置,也可以再后面進行配置
最后在Configure方法中,開啟使用Session的模式,如果在上面已經配置過了,則可以不再傳入配置信息,否則還是要像上面的配置信息一樣,傳入Session的配置信息,代碼如下:
app.UseInMemorySession(configure:s => { s.IdleTimeout = TimeSpan.FromMinutes(30); });//app.UseSession(o => { o.IdleTimeout = TimeSpan.FromSeconds(30); });//app.UseInMemorySession(null, null); //開啟內存Session//app.UseDistributedSession(null, null);//開啟分布式Session,也即持久化Session//app.UseDistributedSession(new RedisCache(new RedisCacheOptions() { Configuration = "localhost" }));
對于UseInMemorySession方法,接收2個可選參數,分別是:IMemoryCache
可用于修改Session數據的默認保存地址;Action<SessionOptions>
委托則可以讓你修改默認選項,比如Session cookie的路徑、默認的過期時間等。本例中,我們修改默認過期時間為30分鐘。
注意:該方法必須在app.UseMvc之前調用,否則在Mvc里獲取不到Session,而且會出錯。
獲取和設置Session對象,一般是在Controller的action里通過this.Context.Session
來獲取的,其獲取的是一個基于接口ISessionCollection
的實例。該接口可以通過索引、Set、TryGetValue等方法進行Session值的獲取和設置,但我們發現在獲取和設置Session的時候,我們只能使用byte[]類型,而不能像之前版本的Session一樣可以設置任意類型的數據。原因是因為,新版本的Session要支持在遠程服務器上存儲,就需要支持序列化,所以才強制要求保存為byte[]
類型。所以我們在保存Session的時候,需要將其轉換為byte[]
才能進行保存,并且獲取以后要再次將byte[]
轉換為自己的原有的類型才行。這種形式太麻煩了,好在微軟在Microsoft.AspNet.Http
命名空間(所屬Microsoft.AspNet.Http.Extensions.dll
中)下,為我們添加了幾個擴展方法,分別用于設置和保存byte[]
類型、int
類型、以及string
類型,代碼如下:
public static byte[] Get(this ISessionCollection session, string key);public static int? GetInt(this ISessionCollection session, string key);public static string GetString(this ISessionCollection session, string key);public static void Set(this ISessionCollection session, string key, byte[] value);public static void SetInt(this ISessionCollection session, string key, int value);public static void SetString(this ISessionCollection session, string key, string value);
所以,在Controller
里引用Microsoft.AspNet.Http
命名空間以后,我們就可以通過如下代碼進行Session的設置和獲取了:
Context.Session.SetString("Name", "Mike");Context.Session.SetInt("Age", 21);ViewBag.Name = Context.Session.GetString("Name");ViewBag.Age = Context.Session.GetInt("Age");
前面我們說了,要保存自定義類型的Session,需要將其類型轉換成byte[]數組才行,在本例中,我們對bool類型的Session數據進行設置和獲取的代碼,示例如下:
public static class SessionExtensions{ public static bool? GetBoolean(this ISessionCollection session, string key) { var data = session.Get(key); if (data == null) { return null; } return BitConverter.ToBoolean(data, 0); } public static void SetBoolean(this ISessionCollection session, string key, bool value) { session.Set(key, BitConverter.GetBytes(value)); }}
定義bool類型的擴展方法以后,我們就可以像SetInt/GetInt那樣進行使用了,示例如下:
Context.Session.SetBoolean("Liar", true);ViewBag.Liar = Context.Session.GetBoolean("Liar");
另外,ISessionCollection接口上還提供了Remove(string key)和Clear()兩個方法分別用于刪除某個Session值和清空所有的Session值的功能。但同時也需要注意,該接口并沒提供之前版本中的Abandon方法功能。
使用分布式Session,其主要工作就是將Session保存的地方從原來的內存換到分布式存儲上,本節,我們以Redis存儲為例來講解分布式Session的處理。
先查看使用分布式Session的擴展方法,示例如下,我們可以看到,其Session容器需要是一個支持IDistributedCache
的接口示例。
public static IapplicationBuilder UseDistributedSession([NotNullAttribute]this IApplicationBuilder app, IDistributedCache cache, Action<SessionOptions> configure = null);
該接口是緩存Caching的通用接口,也就是說,只要我們實現了緩存接口,就可以將其用于Session的管理。進一步查看該接口發現,該接口中定義的Set方法還需要實現一個ICacheContext類型的緩存上下文(以便在調用的時候讓其它程序進行委托調用),接口定義分別如下:
public interface IDistributedCache{ void Connect(); void Refresh(string key); void Remove(string key); Stream Set(string key, object state, Action<ICacheContext> create); bool TryGetValue(string key, out Stream value);}public interface ICacheContext{ Stream Data { get; } string Key { get; } object State { get; } void SetAbsoluteExpiration(TimeSpan relative); void SetAbsoluteExpiration(DateTimeOffset absolute); void SetSlidingExpiration(TimeSpan offset);}
接下來,我們基于Redis來實現上述功能,創建RedisCache
類,并繼承IDistributedCache
,引用StackExchange.Redis
程序集,然后實現IDistributedCache
接口的所有方法和屬性,代碼如下:
using Microsoft.Framework.Cache.Distributed;using Microsoft.Framework.OptionsModel;using StackExchange.Redis;using System;using System.IO;namespace Microsoft.Framework.Caching.Redis{ public class RedisCache : IDistributedCache { // KEYS[1] = = key // ARGV[1] = absolute-expiration - ticks as long (-1 for none) // ARGV[2] = sliding-expiration - ticks as long (-1 for none) // ARGV[3] = relative-expiration (long, in seconds, -1 for none) - Min(absolute-expiration - Now, sliding-expiration) // ARGV[4] = data - byte[] // this order should not change LUA script depends on it private const string SetScript = (@" redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4]) if ARGV[3] ~= '-1' then redis.call('EXPIRE', KEYS[1], ARGV[3]) end return 1"); private const string AbsoluteExpirationKey = "absexp"; private const string SlidingExpirationKey = "sldexp"; private const string DataKey = "data"; private const long NotPresent = -1; private ConnectionMultiplexer _connection; private IDatabase _cache; private readonly RedisCacheOptions _options; private readonly string _instance; public RedisCache(IOptions<RedisCacheOptions> optionsaccessor) { _options = optionsAccessor.Options; // This allows partitioning a single backend cache for use with multiple apps/services. _instance = _options.InstanceName ?? string.Empty; } public void Connect() { if (_connection == null) { _connection = ConnectionMultiplexer.Connect(_options.Configuration); _cache = _connection.GetDatabase(); } } public Stream Set(string key, object state, Action<ICacheContext> create) { Connect(); var context = new CacheContext(key) { State = state }; create(context); var value = context.GetBytes(); var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key }, new RedisValue[] { context.AbsoluteExpiration?.Ticks ?? NotPresent, context.SlidingExpiration?.Ticks ?? NotPresent, context.GetExpirationInSeconds() ?? NotPresent, value }); // TODO: Error handling return new MemoryStream(value, writable: false); } public bool TryGetValue(string key, out Stream value) { value = GetAndRefresh(key, getData: true); return value != null; } public void Refresh(string key) {
新聞熱點
疑難解答