dotnet 6 ,在Filter中读取request.body

有一个需求,需要在读取request中的参数,记录日志,然后返回缓存内容。为啥不用url?因为这个参数比较复杂,比如json字符串,无法在url中传递。

首先写一个ResourceFilter

using Castle.Core.Resource;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using TournamentAuthCenter.Utility.ApiResult;
using TournamentAuthCenter.Utility.HttpHandle;
using TournamentAuthCenter.ViewModel.Request;

namespace TestResourseFilter.Utility.CacheHandler
{
    

    /// <summary>
    /// 同步版
    /// </summary>
    public class ClentAccessResourceAsyncFilter : Attribute, IAsyncResourceFilter
    {
        /// <summary>
        /// 缓存的区域--定义的保存数据的区域
        /// </summary>
        private readonly IMemoryCache _memoryCache;
        private ILogger<ClentAccessResourceAsyncFilter> _logger;

        public ClentAccessResourceAsyncFilter(
            IMemoryCache memoryCache, 
            ILogger<ClentAccessResourceAsyncFilter> logger)
        {
            _memoryCache = memoryCache;
            _logger = logger;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            //string key = context.HttpContext.Request.Path;
            string key = string.Empty;
            string body = string.Empty;
            HttpRequest request = context.HttpContext.Request;
            
            // 重点在这里
            context.HttpContext.Request.EnableBuffering();
            context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
            using (var ms = new MemoryStream())
            {
                request.Body.CopyTo(ms);
                var b = ms.ToArray();
                body = Encoding.UTF8.GetString(b);
            } 
            // ClientPostModel Post Action的
入参模型
            ClientPostModel postModel = JsonConvert.DeserializeObject<ClientPostModel>(body);
            
            if (postModel.ClientName is not null)
            {
                key = $"{context.HttpContext.Request.Path}/{postModel.ClientName}";//请求的路径
                _logger.LogInformation($"ClientAccess:key:{key},deviceinfo:{postModel.ClientDeviceInfo}");
            }
            else
            {
                throw new Exception("parameter is null");
            }
            
            //在这里就应该判断缓存
            var data = _memoryCache.Get(key);
            if (data != null)
            {   
                context.Result = (IActionResult)data;
            }
            else{
                // 重新开启流的可读性,并将指针置0
                context.HttpContext.Request.EnableBuffering();
                context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
                // 离开Filter继续向下
                ResourceExecutedContext resourceExecutedContext = await next.Invoke(); //执行这句就是继续往后执行控制器构造函数+Action方法,已经执行了控制器的构造函数和Action方法--在这里就应该保存缓存
                // 获得结果返回之前,将结果存入缓存
                // ResultModel 统一返回值模型
                ResultModel _result = (ResultModel)resourceExecutedContext.Result;
                // 如果返回值正确,就将其存入缓存
                if (_result is not null && _result.ResCode == 200)
                {
                    _memoryCache.Set(key, _result, new TimeSpan(2, 0, 0));
                }            
            }
            await Task.CompletedTask;
        }
       
    }
}

然后在program.cs中配置使用

// 配置同步读取流数据
builder.Services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true)
                .Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);
//允许body重用
            app.Use((context,next)=>
            {
                context.Request.EnableBuffering();
                return next(context);
            });

然后将Filter标记在Action即可。

注意重点是每次需要重读时,要加以下两句代码

context.HttpContext.Request.EnableBuffering();
            context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);

并且注意,需要将body拷贝到MemoryStream中操作

using (var ms = new MemoryStream())
            {
                request.Body.CopyTo(ms);
                var b = ms.ToArray();
                body = Encoding.UTF8.GetString(b);
            } 

问题解决参考 https://www.cnblogs.com/personblog/p/13259732.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值