ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

.NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中。对于分布式缓存,.NET Core提供了针对Redis和SQL Server的原生支持。除了这个独立的缓存系统之外,ASP.NET Core还借助一个中间件实现了“响应缓存”,它会按照HTTP缓存规范对整个响应实施缓存。不过按照惯例,在对缓存进行系统介绍之前,我们还是先通过一些简单的实例演示感知一下如果在一个ASP.NET Core应用中如何使用缓存。

目录
一、将数据缓存在内存中
二、基于Redis的分布式缓存
三、基于SQL Server的分布式缓存
四、缓存整个HTTP响应

一、将数据缓存在内存中

与针对数据库和远程服务调用这种IO操作来说,应用针对内存的访问性能将提供不止一个数量级的提升,所以将数据直接缓存在应用进程的内容中自然具有最佳的性能优势。与基于内存的缓存相关的应用编程接口定义在NuGet包“Microsoft.Extensions.Caching.Memory”中,具体的缓存实现在一个名为MemoryCache的服务对象中,后者是我们对所有实现了IMemoryCache接口的所有类型以及对应对象的统称。由于是将缓存对象直接置于内存之中,中间并不涉及持久化存储的问题,自然也就无需考虑针对缓存对象的序列化问题,所以这种内存模式支持任意类型的缓存对象。

针对缓存的操作不外乎对缓存数据的存与取,这两个基本的操作都由上面介绍的这个MemoryCache对象来完成。如果我们在一个ASP.NET Core应用对MemoryCache服务在启动时做了注册,我们就可以在任何地方获取该服务对象设置和获取缓存数据,所以针对缓存的编程是非常简单的。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {        
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddMemoryCache())
   8:             .Configure(app => app.Run(async context =>
   9:                 {
  10:                     IMemoryCache cache = context.RequestServices.GetRequiredService<IMemoryCache>();
  11:                     DateTime currentTime;
  12:                     if (!cache.TryGetValue<DateTime>("CurrentTime", out currentTime))
  13:                     {
  14:                         cache.Set("CurrentTime", currentTime = DateTime.Now);
  15:                     }
  16:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  17:                 }))
  18:             .Build()
  19:             .Run();        
  20:     }
  21: }

在上面这个演示程序中,我们在WebHostBuilder的ConfigureServices方法中通过调用ServiceCollection的扩展方法AddMemoryCache完成了针对MemoryCache的服务注册。在WebHostBuilder的Configure方法中,我们通过调用ApplicationBuilder的Run方法注册了一个中间件对请求做了简单的响应。我们先从当前HttpContext中得到对应的ServiceProvider,并利用后者得到MemoryCache对象。我们接下来调用MemoryCache的Set方法将当前时间缓存起来(如果尚未缓存),并指定一个唯一的Key(“CurrentTime”)。通过指定响应的Key,我们可以调用另一个名为TryGetValue<T>的方法获取缓存的对象。我们最终写入的响应内容实际上是缓存的时候和当前实施的时间。由于缓存的是当前时间,所以当我们通过浏览器访问该应用的时候,显示的时间在缓存过期之前总是不变的

1

虽然基于内存的缓存具有最高的性能,但是由于它实际上是将缓存数据存在承载ASP.NET Core应用的Web服务上,对于部署在集群式服务器中的应用会出现缓存数据不一致的情况。对于这种部署场景,我们需要将数据缓存在某一个独立的存储中心,以便让所有的Web服务器共享同一份缓存数据,我们将这种缓存形式称为“分布式缓存”。ASP.NET Core为分布式缓存提供了两种原生的存储形式,一种是基于NoSQL的Redis数据库,另一种则是微软自家关系型数据库SQL Server。

二、基于Redis的分布式缓存

Redis数目前较为流行NoSQL数据库,很多的编程平台都将它作为分布式缓存的首选,接下来我们来演示如何在一个ASP.NET Core应用中如何采用基于Redis的分布式缓存。考虑到一些人可能还没有体验过Redis,所以我们先来简单介绍一下如何安装Redis。Redis最简单的安装方式就是采用Chocolatey(https://chocolatey.org/) 命令行,后者是Windows平台下一款优秀的软件包管理工具(类似于NPM)。

   1: PowerShell prompt :
   2: iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
   3:  
   4: CMD.exe:
   5: @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

我们既可以采用PowerShell (要求版本在V3以上)命令行或者普通CMD.exe命令行来安装Chocolatey ,具体的命令如上所示。在确保Chocolatey 被本地正常安装情况下,我们可以执行执行如下的命令安装或者升级64位的Redis。

   1: C:\>choco install redis-64
   2: C:\>choco upgrade redis-64

Redis服务器的启动也很简单,我们只需要以命令行的形式执行redis-server命令即可。如果在执行该命名之后看到如下图所示的输出,则表示本地的Redis服务器被正常启动,输出的结果会指定服务器采用的网络监听端口。

2

接下来我们会对上面演示的实例进行简单的修改,将基于内存的本地缓存切换到针对Redis数据库的分布式缓存。针对Redis的分布式缓存实现在NuGet包“Microsoft.Extensions.Caching.Redis”之中,所以我们需要确保该NuGet包被正常安装。不论采用Redis、SQL Server还是其他的分布式存储方式,针对分布式缓存的操作都实现在DistributedCache这个服务对象向,该服务对应的接口为IDistributedCache。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddDistributedRedisCache(options =>
   8:                 {
   9:                     options.Configuration    = "localhost";
  10:                     options.InstanceName     = "Demo";
  11:                 }))
  12:             .Configure(app => app.Run(async context =>
  13:                 {
  14:                     var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
  15:                     string currentTime = await cache.GetStringAsync("CurrentTime");
  16:                     if (null == currentTime)
  17:                     {
  18:                         currentTime = DateTime.Now.ToString();
  19:                         await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
  20:                     }
  21:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  22:                 }))
  23:             .Build()
  24:             .Run();
  25:     }
  26: }

从上面的代码片段可以看出,针对分布式缓存和内存缓存在总体编程模式上是一致的,我们需要先注册针对DistributedCache的服务注册,但是利用依赖注入机制提供该服务对象来进行缓存数据的设置和缓存。我们调用IServiceCollection的另一个扩展方法AddDistributedRedisCache注册DistributedCache服务,在调用这个方法的时候借助于RedisCacheOptions这个对象的Configuration和InstanceName属性设置Redis数据库的服务器和实例名称。由于采用的是本地的Redis服务器,所以我们将前者设置为“localhost”。其实Redis数据库并没有所为的实例的概念,RedisCacheOptions的InstanceName属性的目的在于当多个应用共享同一个Redis数据库的时候,缓存数据可以利用它来区分,当缓存数据被保存到Redis数据库中的时候,对应的Key会以它为前缀。修改后的应用启动后(确保Redis服务器被正常启动),如果我们利用浏览器来访问它,依然会得到与前面类似的输出。

对于基于内存的本地缓存来说,我们可以将任何类型的数据置于缓存之中,但是对于分布式缓存来说,由于涉及到网络传输甚至是持久化存储,放到缓存中的数据类型只能是字节数组,所以我们需要自行负责对缓存对象的序列化和反序列化工作。如上面的代码片段所示,我们先将表示当前时间的DateTime对象转换成字符串,然后采用UTF-8编码进一步转换成字节数组,最终调用DistributedCache的SetAsync方法将后者缓存起来。实际上我们也可以直接调用另一个扩展方法SetStringAsync,它会负责将字符串编码为字节数组。在获取缓存的时候,我们调用的是DistributedCache的GetStringAsync方法,它会将字节数组转换成字符串。

缓存数据在Redis数据库中是以散列(Hash)的形式存放的,对应的Key会将设置的InstanceName作为前缀(如果进行了设置)。为了查看究竟存放了哪些数据在Redis数据库中,我们可以按照如图3所示的形式执行Redis命名来获取存储的数据。从下图呈现的输出结果我们不难看出,存入的不仅仅包括我们指定的缓存数据(Sub-Key为“data”)之外,还包括其他两组针对该缓存条目的描述信息,对应的Sub-Key分别为“absexp”和“sldexp”,表示缓存的绝对过期时间(Absolute Expiration Time)和滑动过期时间(Slidding Expiration Time)。

3

三、基于SQL Server的分布式缓存

除了使用Redis这种主流的NoSQL数据库来支持分布式缓存,微软在设计分布式缓存时也没有忘记自家的关系型数据库采用SQL Server。针对SQL Server的分布式缓存实现在“Microsoft.Extensions.Caching.SqlServer”这个NuGet包中,我们先得确保该NuGet包被正常装到演示的应用中。

所谓的针对SQL Server的分布式缓存,实际上就是将标识缓存数据的字节数组存放在SQL Server数据库中某个具有固定结构的数据表中,因为我们得先来创建这么一个缓存表,该表可以借助一个名为sql-cache 的工具来创建。在执行sql-cache 工具创建缓存表之前,我们需要在project.json文件中按照如下的形式为这个工具添加相应的NuGet包“Microsoft.Extensions.Caching.SqlConfig.Tools”。

   1: {
   2:   …
   3:   "tools": {
   4:     "Microsoft.Extensions.Caching.SqlConfig.Tools": "1.1.0-preview4-final"
   5:   }
   6: }

当针对上述这个NuGet包复原(Restore)之后,我们可以执行“dotnet sql-cache create”命令来创建,至于这个执行这个命令应该指定怎样的参数,我们可以按照如下的形式通过执行“dotnet sql-cache create --help”命令来查看。从下图可以看出,该命名需要指定三个参数,它们分别表示缓存数据库的链接字符串、缓存表的Schema和名称。

4

接下来我们只需要在演示应用所在的项目根目录(project.json文件所在的目录)下执行dotnet sql-cache create就可以在指定的数据库创建缓存表了。对于我们演示的实例来说,我们按照下图所示的方式执行这dotnet sql-cache create命令行在本机一个名为demodb的数据库中创建了一个名为AspnetCache的缓存表,该表采用dbo作为Schema。

5

在所有的准备工作完成之后,我们只需要对上面的程序做如下的修改即可将针对Redis数据库的缓存切换到针对SQL Server数据库的缓存。由于采用的同样是分布式缓存,所以针对缓存数据的设置和提取的代码不用做任何改变,我们需要修改的地方仅仅是服务注册部分。如下面的代码片段所示,我们在WebHostBuilder的ConfigureServices方法中调用IServiceCollection的扩展方法AddDistributedSqlServerCache完成了对应的服务注册。在调用这个方法的时候,我们通过设置SqlServerCacheOptions对象的三个属性的方式指定了缓存数据库的链接字符串和缓存表的Schema和名称。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs => svcs.AddDistributedSqlServerCache(options =>
   8:             {
   9:                 options.ConnectionString   = "server=.;database=demodb;uid=sa;pwd=password";
  10:                 options.SchemaName         = "dbo";
  11:                 options.TableName          = "AspnetCache";
  12:             }))
  13:             .Configure(app => app.Run(async context =>
  14:                 {
  15:                     var cache = context.RequestServices.GetRequiredService<IDistributedCache>();
  16:                     string currentTime = await cache.GetStringAsync("CurrentTime");
  17:                     if (null == currentTime)
  18:                     {
  19:                         currentTime = DateTime.Now.ToString();
  20:                         await cache.SetAsync("CurrentTime", Encoding.UTF8.GetBytes(currentTime));
  21:                     }
  22:                     await context.Response.WriteAsync($"{currentTime}({DateTime.Now})");
  23:                 }))
  24:             .Build()
  25:             .Run();
  26:     }
  27: }

如果想看看最终存入SQL Server数据库中的究竟包含哪些缓存数据,我们只需要直接在所在数据库中查看对应的缓存表了。对于演示实例缓存的数据,它会以下图所示的形式保存在我们创建的缓存表(AspnetCache)中,与基于Redis的缓存类似,与指定缓存数据的值一并存储的还包括缓存的过期信息。

6

四、缓存整个HTTP响应

上面演示的两种缓存都要求我们利用注册的服务对象以手工的方式存储和提取具体的缓存数据,而接下来我们演示的缓存则不再基于某个具体的缓存数据,而是将服务端最终生成的响应主体内容予以缓存,我们将这种缓存形式称为响应缓存(Response Caching)。标准的HTTP规范,不论是HTTP 1.0+还是HTTP 1.1,都会缓存做了详细的规定,这是响应规范的理论机制和指导思想。我们将在后续内容中详细介绍HTTP缓存,在这之前我们先通过一个简单的实例来演示一下整个响应内容是如何借助一个名为ResponseCachingMiddleware中间件被缓存起来的。该中间件由“Microsoft.AspNetCore.ResponseCaching”这个NuGet包提供。

通过同样是采用基于时间的缓存场景,为此我们编写了如下这个简单的程序。我们在WebHostBuilder的ConfigureServices方法中调用了IServiceCollection接口的扩展方法AddResponseCaching注册了中间件ResponseCachingMiddleware依赖的所有的服务,而这个中间件的注册则通过调用IApplicationBuilder接口的扩展方法UseResponseCaching完成。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs=>svcs.AddResponseCaching())
   8:             .Configure(app => app
   9:                 .UseResponseCaching()
  10:                 .Run(async context => {
  11:                     context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
  12:                         {
  13:                             Public = true,
  14:                             MaxAge = TimeSpan.FromSeconds(3600)
  15:                         };
  16:                     
  17:                     string utc = context.Request.Query["utc"].FirstOrDefault()??"";
  18:                     bool isUtc = string.Equals(utc, "true", StringComparison.OrdinalIgnoreCase);
  19:                     await context.Response.WriteAsync(isUtc? DateTime.UtcNow.ToString(): DateTime.UtcNow.ToString());
  20:                 }))
  21:             .Build()
  22:             .Run();    
  23:     }
  24: }

对于最终实现的请求处理逻辑来说,我们仅仅是为响应添加了一个Cache-Control报头,并将它的值设置为“public, max-age=3600”(public表示缓存的是可以被所有用户共享的公共数据,而max-age则表示过去时限,单位为秒)。真正写入响应的主体内容就是当前时间,不给过我们会根据请求的查询字符串“utc”决定采用普通时间还是UTC时间。

要证明整个响应的内容是否被被缓存起来,我们只需要验证在缓存过期之前具有相同路径的多个请求对应的响应是否具有相同的主体内容,为此我们采用Fiddler来生发送的请求并拦截响应的内容。如下所示的两组请求和响应是在不同时间发送的,我们可以看出响应的内容是完全一致的。由于请求发送的时间不同,所以返回的缓存副本的“年龄”(对应于响应报头Age)也是不同的。

   1: GET http://localhost:5000/ HTTP/1.1
   2: User-Agent: Fiddler
   3: Host: localhost:5000
   4:  
   5: HTTP/1.1 200 OK
   6: Date: Sun, 12 Feb 2017 13:02:23 GMT
   7: Content-Length: 20
   8: Server: Kestrel
   9: Cache-Control: public, max-age=3600
  10: Age: 82
  11:  
  12: 2/12/2017 1:02:23 PM
  13:  
  14:  
  15: GET http://localhost:5000/ HTTP/1.1
  16: User-Agent: Fiddler
  17: Host: localhost:5000
  18:  
  19: HTTP/1.1 200 OK
  20: Date: Sun, 12 Feb 2017 13:02:23 GMT
  21: Content-Length: 20
  22: Server: Kestrel
  23: Cache-Control: public, max-age=3600
  24: Age: 85
  25:  
  26: 2/12/2017 1:02:23 PM

上面这个两个请求的URL并没有携带“utc”查询字符串,所以返回的是一个非UTC时间,接下来我们采用相同的方式生成一个试图返回UTC时间的请求。从下面给出的请求和响应的内容我们可以看出,虽然请求携带了查询字符串“utc=true”,但是返回的依然是之前缓存的时间。由于此可见,ResponseCachingMiddleware中间件在默认情况下是针对请求的路径对响应实施缓存的,它会忽略请求URL携带的查询字符串,这显然不是我们希望看到的结果。

   1: GET http://localhost:5000/?utc=true HTTP/1.1
   2: User-Agent: Fiddler
   3: Host: localhost:5000
   4:  
   5: HTTP/1.1 200 OK
   6: Date: Sun, 12 Feb 2017 13:02:23 GMT
   7: Content-Length: 20
   8: Server: Kestrel
   9: Cache-Control: public, max-age=3600
  10: Age: 474
  11:  
  12: 2/12/2017 1:02:23 PM

按照REST的原则,URL是网路资源的标识,但是资源的表现形式(Representation)会由一些参数来决定,这些参数可以体现为查询字符串,也可以体现为一些请求报头,比如Language报头决定资源的描述语言,Content-Encoding报头决定资源采用的编码方式。因此针对响应的缓存不应该只考虑请求的路径,还应该综合考虑这些参数。

对于演示的这个实例来说,我们希望将查询字符串“utc”纳入缓存考虑的范畴,这可以利用一个名为ResponseCachingFeature的特性来完成,该特性对应的接口为IResponseCachingFeature。如下面的代码片段所示,在将当前时间写入响应之后,我们得到这个特性并设置了它的VaryByQueryKeys属性,该属性包含一组决定输出缓存的查询字符串名称,我们将查询字符“utc”添加到这个列表中。

   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .ConfigureServices(svcs=>svcs.AddResponseCaching())
   8:             .Configure(app => app
   9:                 .UseResponseCaching()
  10:                 .Run(async context => {
  11:                     …
  12:                     var feature = context.Features.Get<IResponseCachingFeature>();
  13:                     feature.VaryByQueryKeys = new string[] { "utc" };                     
  14:                 }))
  15:             .Build()
  16:             .Run();    
  17:     }
  18: }

作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
259 10
|
3月前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
81 5
|
2月前
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
131 23
|
5月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
132 0
|
3月前
|
开发框架 算法 中间件
ASP.NET Core 中的速率限制中间件
在ASP.NET Core中,速率限制中间件用于控制客户端请求速率,防止服务器过载并提高安全性。通过`AddRateLimiter`注册服务,并配置不同策略如固定窗口、滑动窗口、令牌桶和并发限制。这些策略可在全局、控制器或动作级别应用,支持自定义响应处理。使用中间件`UseRateLimiter`启用限流功能,并可通过属性禁用特定控制器或动作的限流。这有助于有效保护API免受滥用和过载。 欢迎关注我的公众号:Net分享 (239字符)
82 1
|
4月前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
85 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
3月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
72 3
|
4月前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
4月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
210 2
|
6月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。

热门文章

最新文章