zoukankan      html  css  js  c++  java
  • 重新整理 .net core 实践篇——— filter[四十四]

    前言

    简单介绍一下filter

    正文

    filter 的种类,微软文档中写道:

    每种筛选器类型都在筛选器管道中的不同阶段执行:
    
    授权筛选器最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,授权筛选器可以让管道短路。
    
    资源筛选器:授权后运行。
    
    OnResourceExecuting 在筛选器管道的其余阶段之前运行代码。 例如,OnResourceExecuting 在模型绑定之前运行代码。
    
    OnResourceExecuted 在管道的其余阶段完成之后运行代码。
    
    操作筛选器:
    
    在调用操作方法之前和之后立即运行代码。
    
    可以更改传递到操作中的参数。
    
    可以更改从操作返回的结果。
    
    Pages 中 Razor 不支持 。
    
    异常筛选器在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。
    
    结果筛选器在执行操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。
    

    交互方式:

    一般我们每个项目(单体或者微服务)都会用到授权筛选器和异常筛选器。

    授权筛选器 是因为我们要保证用户访问的资源是其能够访问的。

    异常筛选器 一般处理一些未捕获的异常进行处理,当然现在为了代码的简洁性性,一般我们在代码里面一般情况下不进行try catch。

    如果是某段代码是已知的异常,比如说知道某个参数不符合规则,那么会主动抛出自定义的异常,然后在异常筛选器统一处理。

    举个例子:

    如果我们要进行错误码处理。

    那么代码中会直接抛出。

    throw CustomException("errorCode");
    

    然后再在异常筛选器中:

    if(Exception is CustomException customException)
    {
       // 进行错误码处理 获取其错误message
    }
    

    当然我们也可以使用异常处理中间件:

    public class CustomExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly CustomExceptionHandlerOptions _options;
    
        public CustomExceptionHandlerMiddleware(RequestDelegate next, IOptions<CustomExceptionHandlerOptions> options)
        {
            _next = next;
            _options = options.Value;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (System.Exception ex)
            {
                var logger = context.RequestServices.GetRequiredService<ILoggerFactory>()
                    .CreateLogger<CustomExceptionHandlerMiddleware>();
                if (context.RequestAborted.IsCancellationRequested && (ex is TaskCanceledException || ex is OperationCanceledException))
                {
                    _options.OnRequestAborted?.Invoke(context, logger);
                }
                else
                {
                    _options.OnException?.Invoke(context, logger, ex);
                }
            }
        }
    }
    

    异常处理中间件的好处也是不言而喻的,公司封装自己的异常中间件,那么每个服务使用该中间件处理方式会得到统一风格的log,且处理方式相同,那么各服务的整体风格也差不多。

    这些想必非常多的开发都是比较熟悉的,然后在网关中一般会使用结果筛选器。

    如果我们需要加入一个参数是requestId进行追踪的话,方便查询日志的话,那么可以这样:

    public class AddRequestIdResultServiceFilter : IResultFilter
    {
        private ILogger _logger;
        public AddRequestIdResultServiceFilter(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<AddRequestIdResultServiceFilter>();
        }
    
        public void OnResultExecuting(ResultExecutingContext context)
        {
    		if (context.Result is ObjectResult objectResult)
    		{
    			if (objectResult.Value is DtoBaseModel dtoModel)
    			{
    				dtoModel.RequestId = context.HttpContext.TraceIdentifier;
    			}
    		}else if (context.Result is EmptyResult emptyResult)
    		{
    			context.Result = new ObjectResult(new DtoBaseModel{RequestId = context.HttpContext.TraceIdentifier });
    		}
        }
    
        public void OnResultExecuted(ResultExecutedContext context)
        {
            // Can't add to headers here because response has started.
            _logger.LogInformation("AddRequestIdResultServiceFilter.OnResultExecuted");
        }
    }
    

    当然这里并不是真正的requestId,而是日志追踪的id,而是TraceId。

    一般会在网关中增加一个requestId是为了日志查询相关的,一般仅在网关中添加这个requestId,因为一般会做分布式追踪,也就是说网关后面的访问链的traceId都会一样,这个后面单独总结一下。

    仅当操作或操作筛选器生成操作结果时,才会执行结果筛选器。 不会在以下情况下执行结果筛选器:

    授权筛选器或资源筛选器使管道短路。

    异常筛选器通过生成操作结果来处理异常。

    Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 方法可以将 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel 设置为 true,使操作结果和后续结果筛选器的执行短路。

    设置短路时写入响应对象,以免生成空响应。 如果在 IResultFilter.OnResultExecuting 中引发异常,则会导致:

    阻止操作结果和后续筛选器的执行。

    结果被视为失败而不是成功。

    有一个值得注意的地方:

    当 Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 方法运行时,响应可能已发送到客户端。 如果响应已发送到客户端,则无法更改。
    

    如果操作结果执行已被另一个筛选器设置短路,则 ResultExecutedContext.Canceled 设置为 true。

    如果操作结果或后续结果筛选器引发了异常,则 ResultExecutedContext.Exception 设置为非 NULL 值。

    将 Exception 设置为 NULL 可有效地处理异常,并防止在管道的后续阶段引发该异常。 处理结果筛选器中出现的异常时,没有可靠的方法来将数据写入响应。 如果在操作结果引发异常时标头已刷新到客户端,则没有任何可靠的机制可用于发送失败代码。

    对于 IAsyncResultFilter,通过调用 ResultExecutionDelegate 上的 await next 可执行所有后续结果筛选器和操作结果。 若要短路,请设置为 ResultExecutingContext.Cancel true ,不调用 ResultExecutionDelegate :

    public class MyAsyncResponseFilter : IAsyncResultFilter
    {
        public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                                 ResultExecutionDelegate next)
        {
            if (!(context.Result is EmptyResult))
            {
                await next();
            }
            else
            {
                context.Cancel = true;
            }
    
        }
    }
    

    操作筛选器

    实现 IActionFilter 或 IAsyncActionFilter 接口。

    它们的执行围绕着操作方法的执行。

    public class MySampleActionFilter : IActionFilter 
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // Do something before the action executes.
            MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            // Do something after the action executes.
            MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
        }
    }
    

    ActionExecutingContext 提供以下属性:

    ActionArguments - 用于读取操作方法的输入。

    Controller - 用于处理控制器实例。

    Result - 设置 Result 会使操作方法和后续操作筛选器的执行短路。

    在操作方法中引发异常:

    防止运行后续筛选器。

    与设置 Result 不同,结果被视为失败而不是成功。

    例子:

    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext 
                                               context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(
                                                    context.ModelState);
            }
        }
    
    
        public override void OnActionExecuted(ActionExecutedContext 
                                              context)
        {
            var result = context.Result;
            // Do something with Result.
            if (context.Canceled == true)
            {
                // Action execution was short-circuited by another filter.
            }
    
            if(context.Exception != null)
            {
                // Exception thrown by action or action filter.
                // Set to null to handle the exception.
                context.Exception = null;
            }
            base.OnActionExecuted(context);
        }
    }
    

    这个一般记录用户的访问情况居多,比如说全局注册,统计action的访问次数等。

    资源筛选器

    资源筛选器:

    实现 IResourceFilter 或 IAsyncResourceFilter 接口。
    执行会覆盖筛选器管道的绝大部分。
    只有 授权筛选器 才会在资源筛选器之前运行。
    如果要使大部分管道短路,资源筛选器会很有用。 例如,如果缓存命中,则缓存筛选器可以绕开管道的其余阶段。

    资源筛选器示例:

    之前显示的短路资源筛选器。

    DisableFormValueModelBindingAttribute:

    可以防止模型绑定访问表单数据。

    用于上传大型文件,以防止表单数据被读入内存。

    如:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
    {
    	public void OnResourceExecuting(ResourceExecutingContext context)
    	{
    		var formValueProviderFactory = context.ValueProviderFactories
    				.OfType<FormValueProviderFactory>()
    				.FirstOrDefault();
    		if (formValueProviderFactory != null)
    		{
    			context.ValueProviderFactories.Remove(formValueProviderFactory);
    		}
    
    		var jqueryFormValueProviderFactory = context.ValueProviderFactories
    			.OfType<JQueryFormValueProviderFactory>()
    			.FirstOrDefault();
    		if (jqueryFormValueProviderFactory != null)
    		{
    			context.ValueProviderFactories.Remove(jqueryFormValueProviderFactory);
    		}
    	}
    
    	public void OnResourceExecuted(ResourceExecutedContext context)
    	{
    	}
    }
    

    下一节简单整理一下分布式日志链。

  • 相关阅读:
    Linux下SVN(Subversion)自动启动脚本
    Linux安装SVN
    【转】utf-8的中文是一个汉字占三个字节长度
    24-《分布式系统架构的本质》系列04——分布式系统关键技术:全栈监控
    23-《分布式系统架构的本质》系列03——分布式系统的技术栈
    22-《分布式系统架构的本质》系列02——从亚马逊的实践,谈分布式系统的难点
    由 leetcode 136. Single Number 引出的异或总结
    【工具软件】-Beyond Compare4 试用到期
    01-更新软件源
    01-程序员也要会项目管理
  • 原文地址:https://www.cnblogs.com/aoximin/p/15507689.html
Copyright © 2011-2022 走看看