Introduction to ASP. NET Core middleware for Distributed Session and core Middleware
1.1. Middleware principles
1.1.1. What is middleware?
Middleware is a piece of code used to process requests and responses. Generally, multiple middleware links to form a pipeline, and each middleware determines whether to call the next middleware.
1.1.2. Middleware Execution Process
An example is provided to demonstrate the execution process of the middleware (three middleware: logging, permission verification, and routing): when a request enters the application, the middleware that executes the log record is executed, it records the request attributes and calls the next middleware permission verification in the chain. If the permission verification passes, the control is passed to the next middleware. If the permission verification fails, it sets the 401 HTTP code and returns the response, the response is passed to the Log Middleware for return.
1.1.3. Middleware Configuration
Middleware configuration is mainly usedRun
,Map
AndUse
Method Configuration; simple middleware can be done directly using the anonymous method, as shown in the following code:
app.Run(async (context,next) => { await context.Response.WriteAsync("environment " + env); await next(); });
To reuse middleware, You Need To encapsulate it into a class for calling.
1.2. Dependency injection Middleware
In actual projects, middleware often needs to call methods of other objects. Therefore, you need to create dependencies between objects. Because ASP. NET Core has a built-in dependency injection system, you can create more elegant code when writing programs.
First, you must register the class in the IOC container.Startup
ClassConfigureServices
Method,ConfigureServices
The method will beConfigure
Method. So that all dependencies are ready when middleware is used.
There is a Greeter class:
public class Greeter : IGreeter{ public string Greet() { return "Hello from Greeter!"; }}public interface IGreeter{ string Greet();}
Step 1:ConfigureServices
Method
public void ConfigureServices(IServiceCollection services){ services.AddTransient<IGreeter, Greeter>();}
Here I use AddTransient for registration. This method creates a new instance of this class in each request. You can select other methods: AddSingleton, AddScoped, or simple Add (all used before the scenes ). The entire DI system is described in the official document.
After registering dependencies, you can use them.IApplicationBuilder
The instance can beConfigure
There isRequestServices
Attribute is used to obtainGreeter
Instance. Because you have registered thisIGreeter
Therefore, the middleware and the specificGreeter
Implementation.
app.Use(async (ctx, next) => { IGreeter greeter = ctx.RequestServices.GetService<IGreeter>(); await ctx.Response.WriteAsync(greeter.Greet()); await next(); });
IfGreeter
A class has a parameterized constructor, and its dependency must also be registered in it.ConfigureServices
.
Middleware can easily solve dependencies. You can add other parameters to the middleware constructor:
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); }}
Alternatively, you can add this dependency to the Invoke method:
public async Task Invoke(HttpContext context, IGreeter greeter){ await context.Response.WriteAsync(greeter.Greet()); await _next(context);}
If the DI system knows the types of these parameters, they will be automatically parsed when the class is instantiated. Very easy!
1.3. Cookies and session Middleware
1.3.1. Session
HTTP is a stateless protocol. The Web server regards every request as an independent request. The user values in the previous request are not saved.
Session status is a feature provided by ASP. NET Core. It stores user data when users access the network server through applications. The Session state is obtained through a browser request and the Session data is saved to the cache.
ASP. NET Core maintains the Session status through cookies containing the Session ID. Each request carries this Session ID.
InMicrosoft.AspNetCore.Session
The middleware provided in the package is used to manage the Session status. To enable Session middleware, you must perform the following operations in the Startup class:
- Enable the memory cache using any service that implements the IDistributedCache interface,
- Set AddSession callback. Because AddSession is implemented in the Microsoft. AspNetCore. Session package, you must add the Microsoft. AspNetCore. Session package to Nuget.
- UseSession callback
The sample code is as follows:
Using Microsoft. aspNetCore. builder; using Microsoft. extensions. dependencyInjection; using System; public class Startup {public void ConfigureServices (IServiceCollection services) {services. addMvc (); // Add a memory cache service. addDistributedMemoryCache (); services. addSession (options => {// set the 10-second Session expiration time to test options. idleTimeout = TimeSpan. fromSeconds (10); options. cookie. httpOnly = true ;});} public void Configure (IApplicationBuilder app) {app. useSession (); app. useMvcWithDefaultRoute ();}}
In the above CodeIdleTimeout
This attribute is used to determine how long the user has not discarded the Session. This attribute has nothing to do with Cookie timeout. Each request through Session middleware resets the timeout time.
1.3.2. Save the Session to Redis.
The distributed Session method is officially provided by Redis and SQL Server. However, the efficiency of SQL Server is far less efficient than that of Redis in obtaining values using key/value. Therefore, I use Redis as an example to implement distributed sessions.
Prepare Redis
Currently, Redis does not support windows, so we have prepared a linux operating system when installing Redis. The system here is ubuntu 16.04. For download and installation methods, refer to the official example.
After the installation is successful, start the Redis service. If the following information is displayed, it indicates that Redis is successfully started:
Related Configuration
First, use the Nuget installation package Microsoft. Extensions. Caching. Redis. After the installation is successful, you can see it in the app. csproj file.
Add app. UseSession () in the Configure method, and then add the Redis service to ConfigureServices.
Public void ConfigureServices (IServiceCollection services) {services. addDistributedRedisCache (options => {options. configuration = "127.0.0.1"; // multiple redis servers: {RedisIP }:{ Redis port}, {RedisIP }:{ Redis port} options. instanceName = "sampleInstance" ;}); services. addMvc (); services. addSession ();}
In the above code, I only use one Redis server for testing. In actual projects, multiple Redis servers are required. The configuration method is as follows:Options. Configuration = "address 1: Port, address 2: Port ";,
The default port 6379 is used instead of the port.
Complete code
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(); }}
The Index page only sets the value for the Session: "apptestvalue". The ShowRedis page displays the Session value.
ShowRedis. cshtml
Redis Session Value:ViewData["Redis"]
Demo result
Now on the run page, go directly to the ShowRedis page. The Session value is blank.
After you click SetSessionValue, return to the ShowRedis page again, and the Session value is displayed.
The apptestvalue indicates that the Session value has been stored in Redis. How can we prove that the apptestvalue value is obtained from Redis? Next we will prove it to you.
1.3.3. Distributed sessions
The Session has been saved to Redis, but it is unclear whether the value is actually saved to Redis or in the project memory; so here we can share sessions in two non-applications (or two different machines), that is, implementing distributed sessions. distributed represents different applications on different machines, but there is often one of the following embarrassing situations, even if each HTTP request carries the same cookie value.
The cause of this problem is the ASP.. NET Core applications have different keys, so there is no way to obtain the Session data saved by the previous application. To solve this problem ,. the NET Core team provides Microsoft. aspNetCore. dataProtection. azureStorage and Microsoft. aspNetCore. dataProtection. the Redis package saves the key to Azure or Redis. Save the key to Redis.
Use the PersistKeysToRedis overload method provided by the Microsoft. AspNetCore. DataProtection. Redis package to save the key to Redis. So here we need to add AddDataProtection () in the ConfigureServices Method ()
var redis = ConnectionMultiplexer.Connect("127.0.0.1:6379"); services.AddDataProtection() .SetApplicationName("session_application_name") .PersistKeysToRedis(redis, "DataProtection-Keys");
The following shows how to implement distributed sessions.
Procedure
Create two projects at the same time: app1 and app2
AddMicrosoft.AspNetCore.DataProtection.Redis
AndStackExchange. Redis. StrongName package
On the same machine, ASP. NET Core uses port 5000 by default when it is started. Because app1 is occupied, set the Start port of app2 to 5001.
Complete code
App1 Project
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"]
App2 Project
Startup. cs
The configuration is the same as that of 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"]
Running Effect
App1 Project
The ShowRedis page is displayed for the first time. The Session value is blank.
Click SetSessionValue and return to the ShowRedis page:
App2 project, directly access: http: // localhost: 5001 in the browser
The above is an example of using Redis to implement distributed Session.
1.4. Summary
This section describes the running principle and configuration process of middleware, and the configuration of object dependencies between middleware and Session configuration issues that are frequently used in time projects. The actual code shows how to use middleware to implement distributed sessions.
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.