中間件是段代碼用於處理請求和響應,一般多箇中間件連接起來造成管道,由每一箇中間件本身來決定是否要調用下一個中間件。html
舉一個示例來演示中間件的執行過程(分別有三個中間件:日誌記錄、權限驗證和路由):當請求進入應用程序時,執行執行日誌記錄的中間件,它記錄請求屬性並調用鏈中的下一個中間件權限驗證,若是權限驗證經過則將控制權傳遞給下一個中間件,不經過則設置401 HTTP代碼並返回響應,響應傳遞給日誌中間件進行返回。linux
中間件配置主要是用Run
、Map
和Use
方法進行配置,三者的不一樣參見上篇ASP.NET Core 運行原理剖析;簡單的中間件能夠直接使用匿名方法就能夠搞定,以下代碼:redis
app.Run(async (context,next) => { await context.Response.WriteAsync("environment " + env); await next(); });
若是想重用中間件,就須要單獨封裝到一個類中進行調用。ubuntu
在實際項目中,中間件每每須要調用其它對象的方法。因此要建立對象之間的依賴,因爲ASP.NET Core 內置的依賴注入系統,寫程序的時候能夠建立更優雅的代碼。windows
首先須要要在IOC容器中註冊類,就是Startup
類中的ConfigureServices
方法中進行註冊,ConfigureServices
方法會在Configure
方法以前被執行。以便在用中間件時全部依賴都準備好了。瀏覽器
如今有一個Greeter類:緩存
public class Greeter : IGreeter { public string Greet() { return "Hello from Greeter!"; } } public interface IGreeter { string Greet(); }
第一步在ConfigureServices
方法中進行註冊服務器
public void ConfigureServices(IServiceCollection services) { services.AddTransient<IGreeter, Greeter>(); }
筆者這裏使用的是AddTransient進行註冊,該方法在每次請求時建立該類的新實例。能夠選擇其它方法:AddSingleton,AddScoped或簡單的Add(全部在幕後前使用)。整個DI系統在官方文檔中有所描述。markdown
在註冊了依賴項後,就可使用它們了。IApplicationBuilder
實例容許在Configure
方法中有一個RequestServices
屬性用於獲取Greeter
實例。因爲已經註冊了這個IGreeter
接口,因此不須要將中間件與具體的Greeter
實現相結合。cookie
app.Use(async (ctx, next) => { IGreeter greeter = ctx.RequestServices.GetService<IGreeter>(); await ctx.Response.WriteAsync(greeter.Greet()); await next(); });
若是Greeter
類有一個參數化的構造函數,它的依賴關係也必須在其中註冊ConfigureServices
。
中間件能夠很容易解決依賴關係。能夠向中間件構造函數添加其餘參數:
public class MyMiddleware { private readonly RequestDelegate _next; private readonly IGreeter _greeter; public MyMiddleware(RequestDelegate next, IGreeter greeter) { _next = next; greeter = greeter; } public async Task Invoke(HttpContext context) { await context.Response.WriteAsync(_greeter.Greet()); await _next(context); } }
或者,能夠將此依賴關係添加到Invoke方法中:
public async Task Invoke(HttpContext context, IGreeter greeter) { await context.Response.WriteAsync(greeter.Greet()); await _next(context); }
若是DI系統知道這些參數的類型,則在類被實例化時,它們將被自動解析。很簡單!
HTTP是一個無狀態協議,Web服務器將每個請求都視爲獨立請求。而且不保存以前請求中用戶的值。
Session 狀態是ASP.NET Core提供的一個功能,它能夠在用戶通應用訪問網絡服務器的時候保存和存儲用戶數據。由服務器上的字典和散列表組成,Session狀態經過瀏覽器的請求中獲得,Session的數據保存到緩存中。
ASP.NET Core經過包含Session ID的Cookie來維護會話狀態,每一個請求都會攜帶此Session ID。
在Microsoft.AspNetCore.Session
包中提供的中間件用來管理Session狀態。要啓用Session中間件,Startup
類裏面須要作如下幾個操做:
IDistributedCache
接口的服務來啓用內存緩存,AddSession
回調,因爲AddSession
是在Microsoft.AspNetCore.Session
包內實現的,因此必須在Nuget中添加Microsoft.AspNetCore.Session
包UseSession
回調具體示例代碼以下:
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using System; public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); // 添加一個內存緩存 services.AddDistributedMemoryCache(); services.AddSession(options => { // 設置10秒鐘Session過時來測試 options.IdleTimeout = TimeSpan.FromSeconds(10); options.Cookie.HttpOnly = true; }); } public void Configure(IApplicationBuilder app) { app.UseSession(); app.UseMvcWithDefaultRoute(); } }
上面代碼中IdleTimeout
屬性用來肯定用戶多久沒有操做時丟棄Session。此屬性和Cookie超時無關,經過Session中間件的每一個請求都會重置超時時間。
實現分佈式Session方法官方提供有Redis、Sql Server等。可是Sql Server效率對於這種以key/value獲取值的方式遠遠不及Redis效率高,因此這裏筆者選用Redis來做示例實現分佈式Session。
準備Redis
因爲目前Redis還不支持windows,因此你們在安裝Redis的時候準備一臺linux操做系統,筆者這裏的系統是ubuntu 16.04;下載及安裝方式能夠參考官方示例。
安裝成功之後啓動Redis 服務,若是看到如下信息,就表明Redis啓動成功:
相關配置
首先須要用Nuget
安裝包Microsoft.Extensions.Caching.Redis
,安裝成功之後就能夠在app.csproj
文件中能夠看到。
在Configure
方法中添加app.UseSession()
;而後再ConfigureServices
添加Redis服務
public void ConfigureServices(IServiceCollection services){ services.AddDistributedRedisCache(options=>{ options.Configuration="127.0.0.1"; //多個redis服務器:{RedisIP}:{Redis端口},{RedisIP}:{Redis端口} options.InstanceName="sampleInstance"; }); services.AddMvc(); services.AddSession(); }
以上代碼中筆者只用一個Redis服務器來做測試,實際項目中須要多個Redis服務器;配置方法如:options.Configuration="地址1:端口,地址2:端口";
,這裏筆者並無給端口而是用的默認端口6379
完整代碼
Startup.cs
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Caching.Redis; using Microsoft.Extensions.Caching.Distributed; namespace app{ public class Startup{ public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services){ services.AddDistributedRedisCache(options =>{ options.Configuration = "127.0.0.1"; options.InstanceName = "sampleInstance"; }); services.AddMvc(); services.AddSession(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env){ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseSession(); app.UseStaticFiles(); app.UseMvc(routes =>{ routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
HomeControler.cs
public class HomeController : Controller { public IActionResult Index() { HttpContext.Session.Set("apptest",Encoding.UTF8.GetBytes("apptestvalue")); return View(); } public IActionResult ShowRedis() { byte[] temp; if(HttpContext.Session.TryGetValue("apptest",out temp)) { ViewData["Redis"]=Encoding.UTF8.GetString(temp); } return View(); } }
Index頁面只作一件事給Session設置值:"apptestvalue",ShowRedis頁面顯示Session值。
ShowRedis.cshtml
Redis Session Value:ViewData["Redis"]
演示結果
如今開始運行頁面,首先直接進入到ShowRedis頁面,Session值顯示爲空
當點擊SetSessionValue
之後,再次回到ShowRedis
頁面,Session就值顯示出來了
看到apptestvalue
表明Session值已經存到Redis裏面,怎樣證實apptestvalue
值是從Redis裏面取到呢?接下來就證實給你們看。
前面已經將Session保存到Redis中,可是你們不清楚這個值是不是真的保存到Redis裏面去了仍是在項目內存中;因此這裏就實如今兩個不的應用程序(或兩臺不一樣的機器)中共享Session,也就是實現分佈式Session,分佈式即表明了不一樣的機器不一樣的應用程序,但每每有下面的一種尷尬的狀況,就算是每一個HTTP請求時都攜帶了相同的cookie值。
形成這個的問題的緣由是每一個機器上面的ASP.NET Core的應用程序的密鑰是不同的,因此沒有辦法獲得前一個應用程序保存的Session數據;爲了解決這個問題,.NET Core團隊爲提供了Microsoft.AspNetCore.DataProtection.AzureStorage
和Microsoft.AspNetCore.DataProtection.Redis
包將密鑰保存到Azure或Redis中。這裏選擇將密鑰保存到Redis。
利用Microsoft.AspNetCore.DataProtection.Redis
包提供的PersistKeysToRedis
重載方法將密鑰保存到Redis裏面去。因此這裏須要在ConfigureServices
方法中添AddDataProtection()
var redis = ConnectionMultiplexer.Connect("127.0.0.1:6379"); services.AddDataProtection() .SetApplicationName("session_application_name") .PersistKeysToRedis(redis, "DataProtection-Keys");
下面演示怎樣實現分佈式Session
配置步驟
Microsoft.AspNetCore.DataProtection.Redis
和StackExchange.Redis.StrongName
包完整代碼
Startup.cs
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Caching.Redis; using Microsoft.Extensions.Caching.Distributed; namespace app1{ public class Startup{ public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services){ var redis = ConnectionMultiplexer.Connect("127.0.0.1:6379"); services.AddDataProtection() .SetApplicationName("session_application_name") .PersistKeysToRedis(redis, "DataProtection-Keys"); services.AddDistributedRedisCache(options =>{ options.Configuration = "127.0.0.1"; options.InstanceName = "sampleInstance"; }); services.AddMvc(); services.AddSession(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env){ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseSession(); app.UseStaticFiles(); app.UseMvc(routes =>{ routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
HomeControler.cs
public class HomeController : Controller { public IActionResult Index() { HttpContext.Session.Set("app1test",Encoding.UTF8.GetBytes("app1testvalue")); return View(); } public IActionResult ShowRedis() { byte[] temp; if(HttpContext.Session.TryGetValue("app1test",out temp)) { ViewData["Redis"]=Encoding.UTF8.GetString(temp); } return View(); } }
ShowRedis.cshtml
Redis Session Value:ViewData["Redis"]
Startup.cs
配置同app1配置同樣。
HomeControler.cs
public class HomeController : Controller { public IActionResult Index() { byte[] temp; if(HttpContext.Session.TryGetValue("app1test",out temp)) { ViewData["Redis"]=Encoding.UTF8.GetString(temp); } return View(); } }
Index.cshtml
ViewData["Redis"]
運行效果
首次打開進入ShowRedis頁面,Session值爲空
點擊SetSessionValue
之後,再回到ShowRedis頁面:
以上是用Redis實現分佈式Session示例。
本節講解了中間件的運行原理及配置過程,中間件之間對象依賴關係的配置和平時項目中經常使用到Session的配置問題。並在實際代碼展現了怎樣使用中間件實現分佈式Session。
做者:xdpie 出處: http://www.cnblogs.com/vipyoumay/p/7771237.html