《深入浅出.NET框架设计与实现》阅读笔记(二)

IHostedService

允许开发人员在后台创建多个任务(托管服务),并支持在web应用程序后台运行。生命周期为单例生命周期
需要引用Microsoft.Extensions.Hosting Nuget包
示例:

 public class MyHostService : IHostedService
 {
     public MyHostService() { }
     public async Task StartAsync(CancellationToken cancellationToken)
     {
         while (true)
         {
             await Task.Delay(5000);
             await Console.Out.WriteLineAsync("hello word");
         }
     }

     public Task StopAsync(CancellationToken cancellationToken)
     {
         Console.WriteLine("结束");
         return Task.CompletedTask;
     }
 }
var host = new HostBuilder().ConfigureServices((hostContext, services) =>
{
    services.AddHostedService<MyHostService>();
});
await host.RunConsoleAsync();

可以实现实时监控后台数据的变化。


中间件(Middleware)

是一种从装配到应用管道以处理请求和响应的软件。
中间件是构建在管道之上的一层组件,多个中间件以责任链模式形成一种串行的顺序结构,每个中间件都会在其传递时进行处理并传递到下一个中间件,而响应内容则以相反的顺序返回。

自定义中间件

  • 带有RequestDelegate参数的构造函数
  • 一个返回Task类型和名为Invoke或InvokeAsync的方法,并且参数为HttpContext
public class CustomMiddleware
{
    private readonly ILogger<CustomMiddleware> _logger;
    private readonly RequestDelegate _next;

    public CustomMiddleware(RequestDelegate next, ILogger<CustomMiddleware> logger)
    {
        this._next = next;
        this._logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogWarning($"Before Invoke {context.Request.Path}");
        await _next(context);
        _logger.LogWarning($"After Invor {context.Request.Path}");
    }
}

制作简单的API统一响应格式与自动包装

项目中,需要统一对外访问的响应内容格式,这样开发人员就需要定义统一的结构化内容,但是如果每个方法都按照一个响应类进行包装,那么响应类的格式发生修改的时候,可能需要修改的地方有很多。因此,可以使用中间件的形式对所有的响应数据进行包装,无需关注返回的内容。对HTTP请求响应是将内容进行包装,生成统一的响应格式,并返回给客户端。

自定义响应数据格式(CustomApiResponse类)

    public class CustomApiResponse
    {
        #region 属性
        /// <summary>
        /// 状态码
        /// </summary>
        public int Status { get; set; }
        /// <summary>
        /// 返回结果
        /// </summary>
        public object Result { get; set; }
        /// <summary>
        /// 请求的唯一编码
        /// </summary>
        public string RequestId { get; set; }
        #endregion

        internal CustomApiResponse(int status, object result, string requestId)
        {
            Status = status;
            Result = result;
            RequestId = requestId;
        }

        public static CustomApiResponse Create(int statusCode, object result, string requestId)
        {
            return new CustomApiResponse(statusCode, result, requestId);
        }
    }

自定义中间件(CustomMiddleware类)

    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            this._next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            //记录当前的stream信息
            var originalResponseBodyStream = context.Response.Body;
            using var memoryStream = new MemoryStream();
            context.Response.Body = memoryStream;
            //把新建memoryStream传递给下一个中间件
            await _next(context);
            context.Response.Body = originalResponseBodyStream;
            //对memoryStream中内存流指针进行重新定位,定位到开始的位置
            memoryStream.Seek(0, SeekOrigin.Begin);
            //对memoryStream返回拿到的数据进行读取
            var readToEnd = await new StreamReader(memoryStream).ReadToEndAsync();
            //string转成对象
            var objResult = JsonConvert.DeserializeObject(readToEnd);
            //创建我们自定义返回的类型格式
            var result = CustomApiResponse.Create(context.Response.StatusCode, objResult,context.TraceIdentifier);
            //将序列化后的参数写入HTTP的响应输出流中
            await context.Response.WriteAsync(JsonConvert.SerializeObject(result));
        }
    }

使用自定义的中间件

在Program.cs中,使用WebApplication的UseMiddleware方法添加自定义的中间件

var app = builder.Build();
//使用中间件
app.UseMiddleware<CustomMiddleware>();

示例返回的数据格式

{
  "Status": 200,
  "Result": [
    {
      "date": "2023-10-30T14:36:27.9761972+08:00",
      "temperatureC": -3,
      "temperatureF": 27,
      "summary": "Mild"
    },
    {
      "date": "2023-10-31T14:36:27.9764839+08:00",
      "temperatureC": -11,
      "temperatureF": 13,
      "summary": "Chilly"
    },
    {
      "date": "2023-11-01T14:36:27.9764854+08:00",
      "temperatureC": 18,
      "temperatureF": 64,
      "summary": "Bracing"
    },
    {
      "date": "2023-11-02T14:36:27.9764856+08:00",
      "temperatureC": 29,
      "temperatureF": 84,
      "summary": "Freezing"
    },
    {
      "date": "2023-11-03T14:36:27.9764857+08:00",
      "temperatureC": 48,
      "temperatureF": 118,
      "summary": "Bracing"
    }
  ],
  "RequestId": "0HMUO95F213AK:00000005"
}

中间件常用的扩展方法

IApplicationBuilder的扩展方法Run方法

使用了Run中间件会返回相应的结果而且终止请求,不会在调用Run后面的中间件。

IApplicationBuilder的扩展方法Use方法

将中间件注册到管道中,不会终止请求。

IApplicationBuilder的扩展方法Map方法

指映射到路径上,并指定独立的执行管道。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/test", appMap =>
{
    appMap.Run(async con =>
    {
        await con.Response.WriteAsync("test.Map方法");
    });
});

IApplicationBuilder的扩展方法MapWhen方法

map方法主要是通过路由来判断是否进入分支管道,MapWhen是通过自己定义的判断条件来决定是否进入分支管道

 public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Func<HttpContext, bool> predicate, Action<IApplicationBuilder> configuration);

第二个参数Func<HttpContext, bool> predicate 用来判断是否进入分支管道
第三个参数Action configuration 添加到分支管道的中间件的Action委托


内存缓存

IMemoryCache

内存缓存的核心对象

Set扩展方法用来设置缓存

        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, MemoryCacheEntryOptions options);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, IChangeToken expirationToken);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, DateTimeOffset absoluteExpiration);
        public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value, TimeSpan absoluteExpirationRelativeToNow);

测试了简单的set方法

TryGetValue扩展方法来获取数值

返回bool值,判断但钱键是否存在

public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out TItem value);

Get扩展方法

可以实现获取没有的时候,新建一个键加入

        public static object Get(this IMemoryCache cache, object key);
        public static TItem Get<TItem>(this IMemoryCache cache, object key);
        public static TItem GetOrCreate<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factory);
        public static Task<TItem> GetOrCreateAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factory);

ICacheEntry

CacheEntry对象承载了缓存的特征,通过IMemoryCache 接口对象使用CreateEntry()方法

    public interface ICacheEntry : IDisposable
    {
        //获取或设置对象的绝对过期日期。
        DateTimeOffset? AbsoluteExpiration { get; set; }
        //获取或设置一个有效期,从添加的时候开始计算,如设置20分钟失效
        TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }
        //提供导致缓存内容过期的IChangeToken实例
        IList<IChangeToken> ExpirationTokens { get; }
        //存储对象的键
        object Key { get; }
        //获取或设置在缓存中失效后出发的回调
        IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }
        //获取或设置缓存项优先级。有四个等级
        CacheItemPriority Priority { get; set; }
        //获取或设置缓存的大小
        long? Size { get; set; }
        //获取或设置指定时间内不活动或不常访问的缓存失效策略
        TimeSpan? SlidingExpiration { get; set; }
        //缓存的值
        object Value { get; set; }
    }

MemoryCache对象

可以通过设置MemoryCacheOptions对象的SizeLimit属性来限制缓存的大小。如果启用了缓存大小限制,那么所有的条目都必须指定Size属性

var cache = new ServiceCollection().AddMemoryCache(opt =>
{
    opt.SizeLimit = 1;
}).BuildServiceProvider().GetRequiredService<IMemoryCache>();

IDistributedCache对象

分布式管理的核心对象,在Microsoft.Extensions.Caching.Distributed 工作集下。可以使用扩展方法Set和SetAsync来设置缓存,但是需要自己序列化成byte数组。也可以使用Setstring和SetStringAsync方法,节省序列化的操作

    public interface IDistributedCache
    {
        byte[] Get(string key);
        Task<byte[]> GetAsync(string key, CancellationToken token = default);
        void Refresh(string key);
        Task RefreshAsync(string key, CancellationToken token = default);
        void Remove(string key);
        Task RemoveAsync(string key, CancellationToken token = default);
        void Set(string key, byte[] value, DistributedCacheEntryOptions options);
        Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default);
    }
  • Get / GetAsync 用来获取缓存,返回一个byte类型的数组
  • Set / SetAsync 用来设置或添加缓存
  • Refresh / RefreshAsync 用来刷新缓存条目中最后访问时间,并将它设置为当前时间
  • Remove / RemoveAsync 用来删除缓存

DistributedCacheEntryOptions类

分布式缓存可以通过DistributedCacheEntryOptions对象来指定相关的过滤策略,设置绝对过期时间和滑动时间。

public class DistributedCacheEntryOptions
{
    public DistributedCacheEntryOptions();

    public DateTimeOffset? AbsoluteExpiration { get; set; }

    public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }

    public TimeSpan? SlidingExpiration { get; set; }
}

基于Redis的分布式缓存

需要引用Microsoft.Extensions.Caching.StackExchangeRedis Nuget包

注册Redis服务

var cache = new ServiceCollection().AddStackExchangeRedisCache(opt =>
{
    opt.Configuration = "localhost";//连接redis数据库的配置
}).BuildServiceProvider().GetRequiredService<IDistributedCache>();

cache.SetString("key1", "value1", new DistributedCacheEntryOptions()
{
    AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(30),
    SlidingExpiration = TimeSpan.FromSeconds(10)
});

HTTP缓存

有强缓存(本地缓存)和协商缓存(弱缓存),主要用于提高资源的获取速度,减少网络传输的开销,缓解服务端的压力。一般只有GET请求和HEAD请求可以缓存。

ResponseCachingMiddleware中间件

添加到服务器的请求管道中

builder.Services.AddResponseCaching(opt =>
{
    opt.UseCaseSensitivePaths = true;
    opt.MaximumBodySize = 1024;
});
var app = builder.Build();
app.UseResponseCaching();
ResponseCachingOptions属性
  • SizeLimit : 缓存响应中间件缓存的大小。默认100M
  • MaximumBodySize : 设置响应正文的最大值,默认64M
  • UseCaseSensitivePaths : 标记缓存是否区分请求路径的大小写

ResponseCachingMiddleware中间件会用IMemoryCache对象设置缓存响应内容,并且通过CachedResponse类定义结构化的缓存内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baobao熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值