ASP.NET Core ResponseCache进行缓存操作

前言

本章将介绍客户端缓存将介绍浏览器缓存和服务端缓存,使用浏览器缓存将减少对web服务器的请求次数,同时可以提升性能,避免重复的运算浪费。

ASP.NET Core对于HTTP缓存分为两种:

  • 客户端(浏览器缓存)
  • 服务端缓存

客户端缓存

通过设置HTTP的响应头 Cache-Control 来完成页面存储到浏览器缓存中如下所示:
客户端缓存示意图
其实客户端缓存的话只需要进行设置 ResponseCache 特性就可以请看如下代码片段

[ResponseCache(Duration = 100,Location = ResponseCacheLocation.Client)]
public IActionResult Index()
{
      return View();
}

在这里插入图片描述

ResponseCacheAttribute 可应用于:

  • Razor 页面处理程序(类) – 特性不能应用于处理程序方法。
  • MVC 控制器(类)。
  • MVC 操作(方法) – 方法级特性覆盖类级特性中指定的设置。

[ResponseCache] 参数

  • Duration 设置缓存的存储时间(以秒为单位)。设置“Cache-control”中的“max-age”。

  • Location

    • Any 缓存在代理和客户端。设置“Cache-control”标题为“public”。
    • Client 只缓存在客户端。设置“Cache-control”标题为“private”。
    • None 每次有请求发出时,缓存会将请求发到服务器,服务器端会验证请求中所描述的缓存是否过期,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。报头设置为“no-cache”。
  • NoStore 缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。

  • VaryByHeader 使用vary头有利于内容服务的动态多样性。例如,使用Vary:User-Agent头,缓存服务器需要通过UA判断是否使用缓存的页面。

  • VaryByQueryKeys 可以按照相同页面,不同的参数进行相应的存储

  • CacheProfileName 设置缓存配置文件的值,可以通过设置不同的缓存参数

CacheProfileName使用请看如下代码片段

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.CacheProfiles.Add("default", new CacheProfile
        {
            Duration = 60
        });

        options.CacheProfiles.Add("test", new CacheProfile
        {
            Duration = 30
        });
    });
}

上述代码我们设置好了两份不一样的配置,那么我们就可以通过下面代码片段进行使用了

    [ResponseCache(CacheProfileName = "default")]
    public IActionResult Index()
    {
         return View();
   }

服务端缓存

服务端缓存可以缓存页面数据和API数据,同时如果我们服务端存在数据,也就是缓存命中的情况下,会直接从缓存中取,不会再进入我们的方法。

public void ConfigureServices(IServiceCollection services)
{
      services.AddResponseCaching(options =>
    {
        options.UseCaseSensitivePaths = false;
        options.MaximumBodySize = 1024;
        options.SizeLimit = 100 * 1024*1024;
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseResponseCaching();
}

服务端缓存配置如下

属性描述
MaximumBodySize响应正文的最大可缓存大小(以字节为单位)。 默认值为 64 * 1024 * 1024 (64 MB)。
SizeLimit响应缓存中间件的大小限制(以字节为单位)。 默认值为 100 * 1024 * 1024 (100 MB)。
UseCaseSensitivePaths确定是否将响应缓存在区分大小写的路径上。 默认值是 false。
[ResponseCache(Duration = 30, VaryByQueryKeys = new[] { "q" })]
public IActionResult Index(int q)
{
    _logger.LogWarning($"我是一个路径:{HttpContext.Request.Host}");
    return View(model:DateTime.Now.ToString());
}

VaryByQueryKeys

使用 MVC/web API 控制器或 Razor Pages 页面模型时, [ResponseCache]属性指定为响应缓存设置适当的标头所需的参数。 严格需要中间件的 [ResponseCache] 属性的唯一参数 VaryByQueryKeys,这与实际 HTTP 标头不对应。 有关详细信息,请参阅 响应缓存在 ASP.NET Core。
如果不使用 [ResponseCache] 属性,响应缓存可能会与 VaryByQueryKeys不同。
我们再看看如上代码效果
在这里插入图片描述

ResponseCache中间件使用的 HTTP 标头

响应头描述
Authorization如果标头存在,则不会缓存。
Cache-Control中间件仅考虑用 public 缓存指令标记的缓存响应。
Pragma请求中的 Pragma: no-cache 标头将产生与 Cache-Control: no-cache相同的效果。 如果存在此标头,则由 Cache-Control 标头中的相关指令重写。 考虑向后兼容 HTTP/1.0
Set-Cookie如果标头存在,则不会缓存响应。 请求处理管道中设置一个或多个 cookie 的任何中间件会阻止响应缓存中间件缓存响应(例如,基于 cookieTempData 提供程序)。
VaryVary 标头用于根据另一个标头改变缓存的响应。 例如,通过编码来缓存响应,包括 Vary: Accept-Encoding 响应头,该响应头将缓存标头为 Accept-Encoding: gzipAccept-Encoding: text/plain 的请求的响应。 永远不会存储响应头值为 * 的响应。
Expires除非被其他 Cache-Control 标头重写,否则不会存储或检索此响应头过时的响应。
If-None-Match如果值不为 *,响应的 ETag 与提供的任何值都不匹配,则将从缓存中提供完整响应。 否则,将提供304(未修改)响应。
If-Modified-Since如果 If-None-Match 标头不存在,则在缓存的响应日期比提供的值更新时,将从缓存中提供完整响应。 否则,将提供304-未修改响应
Date从缓存提供时,如果未在原始响应中提供,则中间件会设置 Date 标头。
Content-Length从缓存提供时,如果未在原始响应中提供,则中间件会设置 Content-Length 标头。
Age忽略原始响应中发送的 Age 标头。 中间件在为缓存的响应提供服务时计算一个新值。

缓存条件

  • 请求必须导致服务器响应,状态代码为200(正常)。
  • 请求方法必须为 GETHEAD
  • Startup.Configure中,响应缓存中间件必须置于需要缓存的中间件之前。
  • Authorization 标头不得存在。
  • Cache-Control 标头参数必须是有效的,并且响应必须标记为 “public” 且未标记为 “private”。
  • 如果 Cache-Control 标头不存在,则 Pragma: no-cache 标头不得存在,因为 Cache-Control 标头在存在时将覆盖 Pragma 标头。
  • Set-Cookie 标头不得存在。
  • Vary 标头参数必须有效且不等于 *
  • Content-Length 标头值(如果已设置)必须与响应正文的大小匹配。
  • 不使用 IHttpSendFileFeature
  • Expires 标头和 max-ages-maxage 缓存指令指定的响应不能过时。
  • 响应缓冲必须成功。 响应的大小必须小于配置的或默认 SizeLimit。 响应的正文大小必须小于配置的或默认的 MaximumBodySize
  • “请求” 或 “响应” 标头字段中不得存在 “no-store” 指令。
<think>好的,我需要回答用户关于用C#开发Web服务器的问题。首先,我得确定用户是想了解基本概念还是具体的实现步骤。可能他们想了解如何从零开始构建一个Web服务器,或者使用现有的框架如ASP.NET Core。 接下来,我应该分步骤介绍,确保结构清晰。先从基础开始,解释Web服务器的基本工作原理,比如处理HTTP请求和响应。然后,提到C#中的相关类库,比如System.Net和System.Net.Sockets,这些是处理网络通信的基础。 然后,可能需要介绍如何使用更高级的框架,比如ASP.NET Core,因为它简化了Web服务器的开发,内置了Kestrel服务器。这里需要说明两种方式的区别:手动处理Socket和使用框架的优势,比如开发效率、功能支持等。 还要注意用户可能的实际需求,比如他们是否需要处理高并发、中间件、路由等。因此,在介绍ASP.NET Core时,要提到中间件管道、依赖注入这些特性,以及如何配置启动类和路由。 另外,可能需要比较两种方法的适用场景。手动实现适合学习底层机制,而ASP.NET Core适合实际生产环境,因为它提供了更多功能和更好的扩展性。 最后,确保语言简洁,避免技术术语过多,但又要准确。检查是否有遗漏的重要点,比如安全性、性能优化,或者常见的工具和库,比如Swagger用于API文档,Entity Framework Core用于数据库操作。 可能还需要给出简单的代码示例,比如使用HttpListener或ASP.NET Core的最小API,这样用户能更直观地理解。同时提醒用户在实际开发中需要注意的事项,比如异常处理、资源管理等。 总结下来,回答的结构应该是:介绍基本概念,分步骤讲解两种实现方式(手动和框架),比较它们的优缺点,给出示例代码,最后提到相关工具和最佳实践。确保内容真实可靠,结构清晰,符合用户的需求。</think>### 用 C# 开发 Web 服务器的分步指南 #### 1. **基本概念** - **Web 服务器**:核心任务是接收 HTTP 请求并返回响应(HTML、JSON 等)。 - **C# 的优势**:通过 `System.Net` 命名空间提供底层网络操作,或使用高级框架(如 ASP.NET Core)快速构建。 #### 2. **两种实现方式** **方式一:手动实现(基于 Socket 或 HttpListener)** - **适用场景**:学习底层原理或特殊需求。 - **关键类库**: - `System.Net.Sockets`:处理 TCP 协议。 - `System.Net.HttpListener`:封装 HTTP 协议。 - **代码示例(HttpListener)**: ```csharp using System; using System.Net; class Program { static void Main() { var listener = new HttpListener(); listener.Prefixes.Add("http://localhost:8080/"); listener.Start(); Console.WriteLine("Listening..."); while (true) { var context = listener.GetContext(); var response = context.Response; string responseString = "<html><body>Hello World</body></html>"; byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); response.ContentLength64 = buffer.Length; response.OutputStream.Write(buffer, 0, buffer.Length); response.Close(); } } } ``` **方式二:使用 ASP.NET Core** - **优势**:内置高性能服务器 Kestrel,支持中间件、依赖注入等。 - **步骤**: 1. 安装 .NET SDK。 2. 创建项目:`dotnet new web -o MyWebServer` 3. 修改 `Program.cs`(最小 API 示例): ```csharp var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World"); app.Run(); ``` 4. 运行:`dotnet run` #### 3. **核心功能实现(以 ASP.NET Core 为例)** - **路由**:使用 `MapGet`、`MapPost` 等方法定义端点。 ```csharp app.MapGet("/api/users", () => new { Name = "Alice", Age = 30 }); ``` - **中间件**:处理请求管道逻辑(如日志、认证)。 ```csharp app.Use(async (context, next) => { Console.WriteLine($"Request: {context.Request.Path}"); await next(); }); ``` - **静态文件托管**:启用 `UseStaticFiles()` 提供 HTML/CSS/JS 文件。 - **依赖注入**:内置容器管理服务生命周期。 ```csharp builder.Services.AddSingleton<IMyService, MyService>(); ``` #### 4. **生产环境关键配置** - **Kestrel 设置**:在 `appsettings.json` 中配置端口和协议。 ```json { "Kestrel": { "EndPoints": { "Http": { "Url": "http://0.0.0.0:5000" } } } } ``` - **反向代理**:通过 Nginx/IIS 转发请求,处理 HTTPS 和负载均衡。 - **日志与监控**:集成 Serilog 或 Application Insights。 #### 5. **工具与扩展** - **Swagger/OpenAPI**:自动生成 API 文档。 ```csharp builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); app.UseSwagger(); app.UseSwaggerUI(); ``` - **数据库集成**:使用 Entity Framework Core 操作 SQL Server/MySQL。 - **身份认证**:通过 `AddAuthentication` 集成 JWT 或 OAuth。 #### 6. **性能优化** - 异步编程:使用 `async/await` 避免阻塞线程。 - 响应压缩:`app.UseResponseCompression()` - 缓存策略:`[ResponseCache(Duration = 60)]` #### 7. **部署** - 发布命令:`dotnet publish -c Release` - 容器化:通过 Dockerfile 构建镜像。 - 云托管:部署到 Azure/AWS 的容器服务。 ### 总结 - **手动实现**适合学习 HTTP 协议细节,但功能有限。 - **ASP.NET Core** 提供企业级功能(路由、中间件、依赖注入),是生产级开发的首选。通过结合 Kestrel 和反向代理,可构建高并发、安全的 Web 服务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值