原文:Why isn't my session state working in ASP.NET Core? Session state, GDPR, and non-essential cookies
做者:Andrew Lock
譯文:https://www.cnblogs.com/lwqlun/p/10526380.html
譯者:Lamond Luhtml
在本篇博客中,我將描述一個關於會話狀態(Session State)的問題, 這個問題我已經被詢問了好幾回了。這個問題的場景以下:json
HttpContext.Session.SetString("theme", "Dark");
HttpContext.Session.GetString("theme");
, 可是獲得的結果倒是null
!這個問題的緣由是ASP.NET Core 2.1中引入的GDPR功能與會話狀態互相影響了。在本篇博客中,我將描述爲何你會看到這種行爲,以及一些處理它的方法。小程序
GDPR中ASP.NET Core 2.1中引入的一個特性,若是你使用NET Core 1.x或2.0版本,你將不會遇到這個問題。可是請記住,自2019年6月27起,1.x版本即將失去支持,2.0版本已經不受支持了,所以你應該考慮升級到2.1及以上版本。c#
說明:瀏覽器
- 《通用數據保護條例》(General Data Protection Regulation,簡稱GDPR)爲歐洲聯盟的條例,前身是歐盟在1995年制定的《計算機數據保護法》。
- 2018年5月25日,歐洲聯盟出臺《通用數據保護條例》。
就像我前面所說的,若是你使用的是ASP.NET Core 2.0及之前的版本,你不會遇到這個問題。這裏我將藉助ASP.NET Core 2.0展現一下預期的行爲,以便說明遇到這個問題的人指望的會話狀態行爲。而後我將在ASP.NET Core 2.2中建立等效的應用程序,並顯示會話狀態再也不起做用了。緩存
會話狀態是一種能夠回溯到ASP.NET(非核心)的功能,你可使用它爲瀏覽站點的用戶存儲和檢索服務器端的值。 會話狀態常常在ASP.NET應用程序中普遍使用,但常常因爲一些緣由而出現問題,主要是性能和可伸縮性。服務器
ASP.NET Core中的你應該把會話狀態看做針對每用戶的緩存。 從技術角度來看,ASP.NET Core中的會話狀態的功能須要2個獨立的部分來完成:cookie
在通常的狀況下,我會盡可能避免使用會話狀態,使用會話狀態可能會有不少陷阱,若是不注意,就會引發一塊兒沒必要要的問題。例如:session
這裏咱們講解了什麼是會話狀態,以及其工做的原理。在下一節中,我將建立一個小程序,這個小程序會使用會話狀態存儲你訪問過的頁面,而後在首頁上顯示該列表。mvc
爲了說明ASP.NET Core 2.0版本和2.1以上版本的行爲變化,我將先建立一個ASP.NET Core 2.0項目,由於個人電腦上安裝了許多.NET Core SDK, 這裏我將使用2.0 SDK(版本號2.1.202)來構建一個2.0項目模板。
這裏咱們首先建立一個global.json, 將當前app目錄的SDK版本固定爲2.1.202版本。
dotnet new globaljson --sdk-version 2.1.202
而後使用dotnet new
命令建立一個新的ASP.NET Core MVC 2.0應用程序
dotnet new mvc --framework netcoreapp2.0
會話狀態默認狀況下是沒有啓用的,因此這裏你須要先添加必要的服務。咱們修改Startup.cs文件ConfigureServices
方法來添加會話服務。默認狀況下,ASP.NET Core將使用內存來存儲會話信息,這對於測試來講很友好,可是生產環境中可能就須要替換爲其餘方式。
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSession(); // add session }
固然,只添加服務是沒有用的,咱們還須要在管道中註冊會話中間件。只有註冊在會話中間件以後的中間件才能夠訪問會話狀態,因此你須要將會話中間件放在MVC中間件以前。
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ...其餘配置 app.UseSession(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
對於這個簡單的例子,我將使用會話密鑰"actions"來存儲並讀取一個字符串類型的會話值,這個會話值中會保存你訪問過的全部頁面。當你在不一樣的頁面間瀏覽時,咱們會將你訪問過的頁面以分號分隔的形式保存在"actions"會話值中。如今咱們更新HomeController
的代碼:
public class HomeController : Controller { public IActionResult Index() { RecordInSession("Home"); return View(); } public IActionResult About() { RecordInSession("About"); return View(); } private void RecordInSession(string action) { var paths = HttpContext.Session.GetString("actions") ?? string.Empty; HttpContext.Session.SetString("actions", paths + ";" + action); } }
注意:
Session.GetString(key)
是Microsoft.AspNetCore.Http
命名空間中的一個擴展方法。
最後,咱們修改Index.cshtml頁面的代碼以下,在頁面中顯示當前"actions"的會話值
@using Microsoft.AspNetCore.Http @{ ViewData["Title"] = "Home Page"; } <div> @Context.Session.GetString("actions") </div>
若是你如今運行應用程序並瀏覽幾回,你將看到會話頁面訪問歷史列表的構建。 在下面的示例中,我訪問了主頁三次,關於頁面兩次:
若是查看當前頁面關聯的Cookie信息,你就會看到一個名爲.AspNetCore.Session
的Cookie, 它的值就是一個加密會話ID, 若是你刪除這個Cookie, 你將會看到"actions"的值被重置,頁面訪問歷史列表丟失。
這種會話狀態的行爲就是大部分人所指望的,因此這裏沒有問題。可是當你使用ASP.NET Core 2.1/2.2版本建立相同項目以後,狀況就不同了。
爲了建立ASP.NET Core 2.2應用程序,我使用了幾乎相同的行爲,但此次我沒有固定SDK。 我安裝了ASP.NET Core 2.2 SDK(2.2.102),所以如下命令會生成一個ASP.NET Core 2.2 MVC應用程序:
dotnet new mvc
這裏你依然須要顯示註冊會話服務,並啓用會話中間件,這一部分代碼和前面如出一轍。
與之前的版本相比,較新的2.2模板已經簡化,所以爲了保持一致性,我從2.0應用程序複製了HomeController。 我還複製了Index.chtml,About.chtml和Contact.cshtml視圖文件。 最後,我更新了Layout.cshtml,爲標題中的About和Contact頁面添加了連接。
這2個應用程序,除了使用的ASP.NET Core版本不同,其餘的部分基本都是同樣的。然而此次運行的時候,當你瀏覽一些頁面以後,首頁只會顯示你訪問過首頁,而不會顯示你訪問過其餘頁面。
不要點擊隱私政策的橫幅 - 後面你將立刻知道緣由
如今若是你去查看一下你的Cookies, 你會發現加密會話ID.AspNetCore.Session
不存在。
一切都顯然配置正確,而且會話自己彷佛也在工做(由於能夠在Index.cshtml中成功檢索HomeController.Index中設置的值)。 但當頁面從新加載,或者在導航之間跳轉的時候,沒有保存會話狀態。
那麼爲何會話狀態在ASP.NET Core 2.0中正常工做, 在ASP.NET Core 2.1/2.2中反而沒有正常工做了呢?
問題的緣由,是由於ASP.NET Core 2.1版本以後,引入了一些新功能。爲了幫助開發人員遵照2018年生效的GDPR規則,ASP.NET Core 2.1版本引入了一些擴展點,以及模板的更新。
針對這些新功能的官方文檔寫的都很詳細,這裏我只作簡單總結:
因此問題是咱們須要用戶贊成使用Cookie。 若是單擊隱私橫幅上的「Accept」,則ASP.NET Core能夠編寫會話cookie,並恢復預期的功能。
根據你正在構建的程序,你可使用多種選項。哪個最適合你取決於你的使用場景,可是請注意,這些功能是爲了幫助開發人員遵照GDPR而添加的。
若是你不在歐洲國家,或者你認爲GDPR對本身沒有什麼影響,最好請閱讀一下https://andrewlock.net/session-state-gdpr-and-non-essential-cookies/ - GDPR可能依然適用於你
這裏主要的可選項以下:
我將在下面詳細介紹每一個選項,請記住考慮你的選擇可能會影響你是否遵照GDPR!
「最簡單」的選擇就是接受現有的行爲。 ASP.NET Core中的會話狀態一般只應用於臨時數據,所以你的應用程序須要可以處理會話狀態不可用的狀況。
這取決於你使用會話的目的,可能能夠實現或可能不能實現,但這是使用現有模板的最簡單方法,而且將你接觸GDPR問題方面風險降到了最低。
第二種選擇和第一種選擇相似,應爲你須要保持現有的行爲。區別在於第一種選項會將會話簡單的視爲緩存,所以你始終須要假設會話值是能夠讀取和保存的。而第二種選項略有不一樣,由於你須要明確知道系統中哪些部分是須要會話狀態的,並在用戶贊成Cookie以前,禁用它們。
例如, 你能夠須要一個會話狀態保存當前頁面選擇的主題。若是用戶沒有贊成Cookie, 那麼你只須要隱藏主題選擇的功能。只要用戶贊成,再將它顯示出來。
這感受就像是針對選擇一的改進,由於它主要改善了用戶體驗。若是你不考慮哪些功能是須要會話的,用戶可能會產生一些疑惑。例如,若是你使用選項一,用戶在切換主題的時候,程序永遠不會記住它們的選擇,這就很讓人沮喪。
若是你肯定不須要Cookie贊成功能,你也能夠很容易的禁用它。 默認模板在Startup.ConfigureServices
中顯式啓用了Cookie贊成功能。
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddSession(); // added to enable session services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
這裏CheckConsentNeeded
屬性是一個標記,它用於檢查是否應將非必要的cookie寫入響應。 若是函數返回true(如上所述,模板中的默認值),則跳過非必要的cookie。 將此更改成false而且會話狀態將起做用,而不須要用戶明確贊成cookie。
徹底禁用cookie贊成功能可能會對你的應用程序形成必定的負擔。 若是是這種狀況,你能夠將會話cookie標記爲必要。
services.AddSession
的重載方法,容許你傳入一個會話配置對象。你可使用它設置會話的超時時間,以及自定義會話Cookie。爲了將會話Cookie標記爲必要的,咱們須要顯式配置IsEssential
的值是true。
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddSession(opts => { opts.Cookie.IsEssential = true; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
使用這種方法,雖然應用程序依然會顯示Cookie贊成橫幅,而且在點擊以前不會寫入非必要的Cookie。 但會議狀態將在用戶贊成Cookie以前當即生效,由於它被認爲是必要的。
在這篇文章中,我描述了一個曾經屢次被問過問題。開發人員發現他們的會話狀態沒有正確保存。 這一般是因爲ASP.NET Core 2.1中引入的Cookie贊成和非必要cookie的GDPR功能引發的。
我展現了一個問題的實例,以及它在2.0 app和2.2 app之間的區別。 我描述了會話狀態如何依賴於默認狀況下被認爲是非必要的會話Cookie,所以在用戶贊成Cookie以前不會寫入響應。
最後,我描述了處理這種行爲的四種方法:
什麼也不作,接受它
禁用依賴會話狀態的功能,直到贊成爲止
取消贊成要求
標記會話Cookie爲必要的Cookie。
哪一種選擇最適合你將取決於你正在構建的應用程序,以及你對GDPR和相似法規的認識。