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

news/2025/11/2 14:38:14/文章来源:https://www.cnblogs.com/Alex80/p/19184953

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

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

1.1. 中间件原理

1.1.1. 什么是中间件

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

1.1.2. 中间件执行过程

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

1.1.3. 中间件的配置

中间件配置主要是用RunMapUse方法进行配置;简单的中间件可以直接使用匿名方法就可以搞定,如下代码:

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

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

1.2. 依赖注入中间件

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

首先需要要在IOC容器中注册类,就是Startup类中的ConfigureServices方法中进行注册,ConfigureServices方法会在Configure方法之前被执行。以便在用中间件时所有依赖都准备好了。

现在有一个Greeter类:

1
2
3
4
5
6
7
8
9
10
11
12
public class Greeter : IGreeter
{
  public string Greet()
  {
    return "Hello from Greeter!";
  }
}
 
public interface IGreeter
{
  string Greet();
}

第一步在ConfigureServices方法中进行注册

1
2
3
4
public void ConfigureServices(IServiceCollection services)
{
  services.AddTransient<IGreeter, Greeter>();
}

笔者这里使用的是AddTransient进行注册,该方法在每次请求时创建该类的新实例。可以选择其它方法:AddSingleton,AddScoped或简单的Add(所有在幕后前使用)。整个DI系统在官方文档中有所描述。

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

1
2
3
4
5
6
app.Use(async (ctx, next) =>
  {
    IGreeter greeter = ctx.RequestServices.GetService<IGreeter>();
    await ctx.Response.WriteAsync(greeter.Greet());
    await next();
  });

如果Greeter类有一个参数化的构造函数,它的依赖关系也必须在其中注册ConfigureServices

中间件可以很容易解决依赖关系。可以向中间件构造函数添加其他参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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方法中:

1
2
3
4
5
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类里面需要做以下几个操作:

  1. 使用任何一个实现了IDistributedCache接口的服务来启用内存缓存,
  2. 设置AddSession回调,由于AddSession是在Microsoft.AspNetCore.Session包内实现的,所以必须在Nuget中添加Microsoft.AspNetCore.Session包
  3. UseSession回调

具体示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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服务

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
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.AzureStorage和Microsoft.AspNetCore.DataProtection.Redis包将密钥保存到Azure或Redis中。这里选择将密钥保存到Redis。

利用Microsoft.AspNetCore.DataProtection.Redis包提供的PersistKeysToRedis重载方法将密钥保存到Redis里面去。所以这里需要在ConfigureServices方法中添AddDataProtection()

1
2
3
4
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
Redis Session Value:ViewData["Redis"]

app2项目

Startup.cs
配置同app1配置一样。

HomeControler.cs

1
2
3
4
5
6
7
8
9
10
11
12
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

1
ViewData["Redis"]

运行效果

app1 项目

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

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

app2项目,直接在浏览器访问:http://localhost:5001

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

1.4. 总结

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
  • 如何在ASP.NET Core中使用Session的示例代码
  • 如何解决asp.net负载均衡时Session共享的问题
  • Asp.Net Core中基于Session的身份验证的实现
  • 解析Asp.net Core中使用Session的方法
  • asp.net(C#)清除全部Session与单个Session的方法
  • 详解ASP.NET中Session的用法
  • ASP.NET ASHX中获得Session的方法
  • ASP.NET将Session保存到数据库中的方法
  • asp.net session的使用与过期实例代码
  • Asp.net中判断一个session是否合法的方法
  • ASP.NET MVC在基控制器中处理Session

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/953618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET周刊【10月第3期 2025-10-19】

国内文章 史诗级警报:ASP.NET Core 被曝 CVSS 9.9 分漏洞,几乎所有.NET 版本无一幸免! https://www.cnblogs.com/netry/p/19147223/CVE-2025-55315 2025 年 10 月的微软补丁星期二更新中,ASP.NET Core 漏洞 CVE-20…

2025 年 11 月快速卷帘门厂家最新推荐,聚焦高端定制需求与全案交付能力!

当前快速卷帘门应用场景日益多元,高端定制需求与全案交付能力成为采购关键。据行业权威门窗协会 2025 年 10 月调研显示,近 42% 的采购方因厂家定制能力不足,导致产品与场景适配偏差,额外改造成本增加 30%;而交付…

【大模型应用开发】之调用大模型

调用大模型 大模型接口规范 大模型接口说明大模型开发是通过访问模型对外暴露的API接口,实现与大模型的交互。 大多数大模型都遵循OpenAI接口规范,是基于Http协议的接口。因此请求路径、参数和返回值信息都是类似的。…

11/2

找第k小的数的分治算法描述:先选最右元素作基准,partition 函数将数组分区,小于等于基准的放左,大于的放右,返回基准位置 p。find 函数递归:若 p 左元素数 c 等于 k,返回 a [p];k 小于 c 则在左分区找 k;否则…

2025 年 11 月快速卷帘门厂家最新推荐,技术实力与市场口碑深度解析!

快速卷帘门行业的技术迭代与市场反馈,是采购决策的关键依据。据行业权威门窗协会 2025 年 10 月发布的调研数据,技术落后导致的产品淘汰率达 32%,而市场口碑差的品牌客户复购率不足 20%。为精准筛选优质厂家,本次联…

2025 年 11 月快速卷帘门厂家最新推荐,实力品牌深度解析采购无忧之选!

快速卷帘门采购过程中,品牌实力不足、服务缺失往往导致采购风险,据行业权威门窗协会 2025 年 10 月数据显示,近 38% 的采购方因选择非实力品牌,遭遇交货延迟、售后缺位等问题,额外成本增加 25%。为助力采购无忧,…

基于Opengauss的餐厅管理系统

项目名称:基于Opengauss的餐厅管理系统这个项目属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13480作业要求 作业链接作业的目标 小组组队,完成团队展示及选题,讨论团…

2025 年 11 月杀虫公司最新推荐,聚焦资质、案例、售后的五家机构深度解读!

近期,行业权威协会开展杀虫服务机构专项测评,以 “资质合规性、案例适配性、售后完善度” 为核心维度,覆盖 120 家服务商。测评采用 “三维验证法”:资质层面审核有害防制 A 级证书、三体系认证等 12 项核心资质;…

WSL2安装perf的简易方法

前言 由于WSL2使用的是微软定制的内核,并非标准的Ubuntu内核,因此直接使用apt安装linux-tools包会失败。 网上给出的方法很多是直接下载微软的 wsl2 内核源码并对其中的 perf 进行手动编译来实现,具体步骤有些繁琐。…

从图像到文本:手写体汉字识别的技术路径与产业赋能

当笔尖在纸面沙沙划过,留下的不仅是墨迹,更是带着个人体温与风格的独特印记。这些千变万化的手写汉字,曾长期是机器难以理解的“天书”。而今,手写体汉字识别技术正如同一位博学的“解码者”,架起了一座连接人类随…

2025 年 11 月杀虫公司最新推荐,高性能与可靠性兼具的优质品牌!

近日,行业权威协会针对杀虫服务机构的 “性能表现” 与 “服务可靠性” 开展专项测评,覆盖 108 家服务商。测评采用 “多场景性能测试 + 长期可靠性跟踪” 的科学方法:在家庭、企业、商业等不同场景中,测试机构消杀…

2025 年 11 月杀虫公司最新推荐,聚焦高端定制需求与全案交付能力!

近期,行业权威协会针对杀虫服务机构的 “高端定制” 与 “全案交付” 能力开展专项测评,覆盖 105 家服务商。测评采用 “需求匹配度评估 + 方案定制能力考核 + 全流程交付跟踪” 的方法:先模拟家庭、企业、商业等不…

微信小脚本的校园生活助手系统

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025 年 11 月不锈钢厂家推荐排行榜,不锈钢板,不锈钢管,不锈钢卷,不锈钢带,不锈钢材批发公司推荐!

2025年11月不锈钢厂家推荐排行榜:不锈钢板、不锈钢管、不锈钢卷、不锈钢带、不锈钢材批发公司深度解析 行业背景与发展趋势 不锈钢作为现代工业的重要基础材料,其应用范围已从传统的建筑装饰扩展至医疗器械、航空航天…

震卦、困卦、中孚卦

震卦 震來虩虩,後笑言啞啞,吉。 象曰:震来虩虩,恐致福也。笑言哑哑,后有则也。 雷声突然而至,令人惊恐不安;等到雷声过去,人又能谈笑如常,这是吉祥的征兆。 《象传》解释说:雷声来时令人害怕,但正因为有所畏…

[2025.11.2 鲜花] trick or treat

(☝`˘ω˘)☝[2025.11.2 鲜花] trick or treat 可恶额啊,万圣节怎么在周五过了,错过了万圣节接龙企划 不知不觉上了差不多三四个月的高三了,下周就是第一次参加月考,不过还是没有参加年级的,似乎十二月月考就…

基于MATLAB绘制CALIPSO Level 2产品中体积退偏比垂直廓线和频率分布直方图

基于MATLAB绘制CALIPSO Level 2产品中体积退偏比(Volume Depolarization Ratio, VDR)垂直廓线和频率分布直方图一、数据读取与预处理 %% 读取CALIPSO Level 2数据 FILE_NAME_L2 = CAL_LID_L2_VFM-ValStage1-V3-41.20…

Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用

Redis是一个开源的高性能键值数据库,支持多种类型的数据结构。以下是Redis支持的主要数据结构及其应用:字符串(Strings) : 这是最基本的类型,一个键对应一个值,可以包含任何数据。例如可以用来缓存用户信息或者进行…

2025 年 11 月弹簧片厂家推荐排行榜,304弹簧片,301弹簧片,不锈铁,430不锈钢板材公司推荐

2025年11月弹簧片厂家推荐排行榜:304弹簧片、301弹簧片、不锈铁、430不锈钢板材公司深度解析 行业背景与发展现状 弹簧片作为工业制造领域的关键基础材料,其性能和质量直接影响着终端产品的可靠性和使用寿命。随着制…

2025 年 11 月办公家具厂家推荐排行榜,办公桌,办公椅,文件柜,会议桌,办公沙发公司推荐,品质与设计双重保障!

2025年11月办公家具厂家推荐排行榜:品质与设计双重保障 在当今商业环境中,办公家具已不仅仅是功能性的工作工具,更是企业文化、空间美学和员工福祉的重要载体。随着企业对办公环境重视程度的提升,办公家具行业正经…