ASP.NET Core中间件实现分布式 Session

1.1. 中间件原理

1.1.1. 什么是中间件

中间件是段代码用于处理请求和响应,通常多个中间件链接起来形成管道,由每个中间件自己来决定是否要调用下一个中间件。

1.1.2. 中间件执行过程

举一个示例来演示中间件的执行过程(分别有三个中间件:日志记录、权限验证和路由):当请求进入应用程序时,执行执行日志记录的中间件,它记录请求属性并调用链中的下一个中间件权限验证,如果权限验证通过则将控制权传递给下一个中间件,不通过则设置401 HTTP代码并返回响应,响应传递给日志中间件进行返回。

1.1.3. 中间件的配置

中间件配置主要是用RunMapUse方法进行配置,三者的不同参见上篇ASP.NET Core 运行原理剖析;简单的中间件可以直接使用匿名方法就可以搞定,如下代码:

app.Run(async (context,next) =>
{
await context.Response.WriteAsync("environment " + env);
await next();
});

如果想重用中间件,就需要单独封装到一个类中进行调用。

1.2. 依赖注入中间件

在实际项目中,中间件往往需要调用其它对象的方法。所以要创建对象之间的依赖,由于ASP.NET Core 内置的依赖注入系统,写程序的时候可以创建更优雅的代码。

首先需要要在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系统在官方文档中有所描述。

在注册了依赖项后,就可以使用它们了。IApplicationBuilder实例允许在Configure方法中有一个RequestServices属性用于获取Greeter实例。由于已经注册了这个IGreeter接口,所以不需要将中间件与具体的Greeter实现相结合。


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系统知道这些参数的类型,则在类被实例化时,它们将被自动解析。很简单!

1.3. Cookies和session中间件

1.3.1. Session

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中间件的每个请求都会重置超时时间。

1.3.2. Session保存到Redis中

实现分布式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里面取到呢?接下来就证明给大家看。

1.3.3. 实现分布Session

前面已经将Session保存到Redis中,但是大家不清楚这个值是否是真的保存到Redis里面去了还是在项目内存中;所以这里就实现在两个不的应用程序(或两台不同的机器)中共享Session,也就是实现分布式Session,分布式即代表了不同的机器不同的应用程序,但往往有下面的一种尴尬的情况,就算是每个HTTP请求时都携带了相同的cookie值。

造成这个的问题的原因是每个机器上面的ASP.NET Core的应用程序的密钥是不一样的,所以没有办法得到前一个应用程序保存的Session数据;为了解决这个问题,.NET Core团队为提供了Microsoft.AspNetCore.DataProtection.AzureStorageMicrosoft.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

配置步骤

  • 同时创建两个项目,分别为app1和app2
  • 添加Microsoft.AspNetCore.DataProtection.RedisStackExchange.Redis.StrongName

  • 由于在同一台机器上,ASP.NET Core程序默认启动的时候端口为5000,由于app1已经占用了,所以将app2的启端口设置为5001

完整代码

  • app1项目

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项目

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"]

运行效果

  • app1 项目

首次打开进入ShowRedis页面,Session值为空

点击SetSessionValue以后,再回到ShowRedis页面:

以上是用Redis实现分布式Session示例。

1.4. 总结

本节讲解了中间件的运行原理及配置过程,中间件之间对象依赖关系的配置和平时项目中常用到Session的配置问题。并在实际代码展示了怎样使用中间件实现分布式Session。

作者:xdpie 出处: http://www.cnblogs.com/vipyoumay/p/7771237.html

以上内容有任何错误或不准确的地方请大家指正,不喜勿喷! 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

ASP.NET Core中间件实现分布式 Session(转载)的更多相关文章

  1. ASP.NET Core中间件实现分布式 Session

    1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...

  2. [转]ASP.NET Core 中间件详解及项目实战

    本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...

  3. [转帖]ASP.NET Core 中间件(Middleware)详解

    ASP.NET Core 中间件(Middleware)详解   本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/ ...

  4. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  5. ASP.NETCore学习记录(二) —— ASP.NET Core 中间件

    ASP.NET Core 中间件 目录: 什么是中间件 ? IApplicationBuilder 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间 ...

  6. (4)ASP.NET Core 中间件

    1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...

  7. 【转】asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

    最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionl ...

  8. ASP.NET Core 中间件的使用(二):依赖注入的使用

    写在前面 上一篇大家已经粗略接触了解到.NET Core中间件的使用:ASP .Net Core 中间件的使用(一):搭建静态文件服务器/访问指定文件, .NET Core框架中很多核心对象都是通过依 ...

  9. ASP.NET Core 中间件的使用(三):全局异常处理机制

    前言 我们经常听到"秒修复秒上线",觉得很厉害的样子. 其实不然,这只是一个调侃而已,出现问题的方式很多(逻辑漏洞.代码异常.操作方式不正确等). 我们今天来说代码异常问题怎么快速 ...

随机推荐

  1. 使 nodejs 代码 在后端运行(nohup)

    1.代码 nohup node server.js & 说明: nohup 命令对 server.js 进程做了三件事 (1)阻止SIGHUP信号发到这个进程. (2)关闭标准输入.该进程不再 ...

  2. 前端知识点回顾——koa和模板引擎

    koa 基于Node.js的web框架,koa1只兼容ES5,koa2兼容ES6及以后. const Koa = requier("koa"); const koa = new K ...

  3. Python 今天抽空学习了@Property

    1.@Property有啥用 1) @property使方法像属性一样调用 @property可以把一个实例方法变成其同名属性,以支持.号访问,它亦可标记设置限制,加以规范 2) @property成 ...

  4. SQL-W3School-高级:SQL JOIN

    ylbtech-SQL-W3School-高级:SQL JOIN 1.返回顶部 1. SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据. Join 和 Key 有时为了得到完 ...

  5. Android-Handler消息机制实现原理)(转)

    Android-Handler消息机制实现原理   一.消息机制流程简介 在应用启动的时候,会执行程序的入口函数main(),main()里面会创建一个Looper对象,然后通过这个Looper对象开 ...

  6. AnimationDrawable

    ①先定义一个AnimationDrawable的xml资源文件: <?xml version="1.0" encoding="utf-8"?> &l ...

  7. kotlin数据解构

    fun main(arg: Array<String>) { val person = person("tom") var (name) = person//解构 pr ...

  8. Throughput Controller(吞吐量控制器) 感觉就像个线程控制器来的

    Percent Executions  下的 Throghput 意思是跑总线程的百分之多少. 如 10线程循环一次, Throghput 设置为80,则有8个线程会跑这个请求 Total Execu ...

  9. Linux 常用高效操作

    空行处理 linux系统下删除空行,用vim底行模式'%s/^n$//g' 可以删除空行并真正修改文件,但文件数量太大时耗时不可预估,于是操作文件删除空行并重定向到一个新的文件是不错的选择. 常用特殊 ...

  10. 查看进程的命令ps

    查看进程的命令:ps aux strace -p pid(进程id) 杀死进程:kill pid(进程id)强制杀死进程:kill -9 pid(进程id) linux ps 命令查看进程状态linu ...