系列导航及源代码

需求

Filter在.NET Web API项目开发中也是很重要的一个概念,它运行在执行MVC响应的Pipeline中执行,允许我们将一些可以在多个Action之间重用的逻辑抽取出来集中管理。虽然我们在上一篇使用.NET 6开发TodoList应用(11)——使用FluentValidation和MediatR实现接口请求验证中演示了如何通过使用MediatR提供的IPipelineBehavior接口在CQRS的Handle方法执行前后插入可重用代码,而本文所演示的Filters作用在Controller的Action执行或Action返回结果前后。

可以创建自定义Filters,用于处理应用程序中的横切片关注点。 横切片关注点的包括错误处理、缓存、配置、授权和日志记录。 Filters可以避免重复代码。

Filter的类型分为以下几种:

  • Authorization Filters:最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,Authorization Filters可以让管道短路。

  • Resource Filters:授权后运行。OnResourceExecuting在Filter Pipeline的其余阶段之前运行代码。OnResourceExecuted在管道的其余阶段完成之后运行代码。可以用这个类型的Filter进行缓存和性能统计。

  • Action Filters:在调用操作方法之前和之后立即运行代码。它可以更改传递到操作中的参数,也可以更改从操作返回的结果,当然如果什么都不更改仅作记录也是可以的。

  • Exception Filters:在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

  • Result Filters:在执行操作结果返回之前和之后运行代码。 仅当操作方法成功执行时,它们才会被运行。

这五种Filters在Filter Pipeline中直观的展现是这样的:

而整个FIlter Pipeline在完整的Middleware Pipeline中的阶段是这样的:

在本文中,我们将演示Action Filters是如何在Controller的Action执行前后记录请求和响应日志的。

目标

使用Action Filters进行接口日志记录。

原理与思路

创建一个自定义的Action Filter,用于实现Controller的接口日志逻辑。

实现

Api新建文件夹Filters并创建类LogFilterAttribute:

  • LogFilterAttribute.cs
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; namespace TodoList.Api.Filters; public class LogFilterAttribute : IActionFilter
{
private readonly ILogger<LogFilterAttribute> _logger; public LogFilterAttribute(ILogger<LogFilterAttribute> logger) => _logger = logger; public void OnActionExecuting(ActionExecutingContext context)
{
var action = context.RouteData.Values["action"];
var controller = context.RouteData.Values["controller"];
// 获取名称包含Command的参数值
var param = context.ActionArguments.SingleOrDefault(x => x.Value.ToString().Contains("Command")).Value; _logger.LogInformation($"Controller:{controller}, action: {action}, Incoming request: {JsonSerializer.Serialize(param)}");
} public void OnActionExecuted(ActionExecutedContext context)
{
var action = context.RouteData.Values["action"];
var controller = context.RouteData.Values["controller"];
// 需要先将Result转换为ObjectResult类型才能拿到Value值
var result = (ObjectResult)context.Result!; _logger.LogInformation($"Controller:{controller}, action: {action}, Executing response: {JsonSerializer.Serialize(result.Value)}");
}
}

依赖注入:

  • Program.cs
builder.Services.AddScoped<LogFilterAttribute>();

在需要应用该Filter的Controller Action上添加属性:

  • TodoListController.cs
[HttpPost]
[ServiceFilter(typeof(LogFilterAttribute))]
public async Task<ApiResponse<Domain.Entities.TodoList>> Create([FromBody] CreateTodoListCommand command)
{
return ApiResponse<Domain.Entities.TodoList>.Success(await _mediator.Send(command));
}

验证

启动Api项目,执行创建TodoList的请求:

  • 请求

  • 响应

    • 来自于OnActionExecuting的请求数据日志:

      注意在我们上一篇文章中的Handling CreateTodoListCommand之前输出的内容。

    • 以及来自于OnActionExecuted输出的返回数据日志:

一点扩展

关于Filter的主题还包含关于其作用域,优先级顺序以及如何调整优先级等,其他主题像如何改写响应,如何进行性能统计和缓存,我在这里暂时不做演示,可以参考微软官方文档:ASP.NET Core 中的筛选器,进行更多了解。

总结

在本文中我们通过一个很简单的例子,演示了Action Filter的基本用法。至此我们关于请求中间件管道的讨论先告一个段落,后面说到认证鉴权的时候我们还会回来讨论这个主题。

从下一篇开始,我们集中来讨论查询操作中涉及的一些典型场景:包括分页、排序、过滤、搜索等。

参考资料

  1. ASP.NET Core 中的筛选器

使用.NET 6开发TodoList应用(12)——实现ActionFilter的更多相关文章

  1. 使用.NET 6开发TodoList应用(3)——引入第三方日志库

    需求 在我们项目开发的过程中,使用.NET 6自带的日志系统有时是不能满足实际需求的,比如有的时候我们需要将日志输出到第三方平台上,最典型的应用就是在各种云平台上,为了集中管理日志和查询日志,通常会选 ...

  2. 使用.NET 6开发TodoList应用(1)——系列背景

    前言 想到要写这样一个系列博客,初衷有两个:一是希望通过一个实践项目,将.NET 6 WebAPI开发的基础知识串联起来,帮助那些想要入门.NET 6服务端开发的朋友们快速上手,对使用.NET 6开发 ...

  3. 使用.NET 6开发TodoList应用(2)——项目结构搭建

    为了不影响阅读的体验,我把系列导航放到文章最后了,有需要的小伙伴可以直接通过导航跳转到对应的文章 : P TodoList需求简介 首先明确一下我们即将开发的这个TodoList应用都需要完成什么功能 ...

  4. 使用.NET 6开发TodoList应用(4)——引入数据存储

    需求 作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件.对我们的TodoList项目来说,自然也需要配置数据存储.目前的需求很简单: 需要能持久化TodoList对象并对其 ...

  5. 使用.NET 6开发TodoList应用(5)——领域实体创建

    需求 上一篇文章中我们完成了数据存储服务的接入,从这一篇开始将正式进入业务逻辑部分的开发. 首先要定义和解决的问题是,根据TodoList项目的需求,我们应该设计怎样的数据实体,如何去进行操作? 长文 ...

  6. 使用.NET 6开发TodoList应用(5.1)——实现Repository模式

    需求 经常写CRUD程序的小伙伴们可能都经历过定义很多Repository接口,分别做对应的实现,依赖注入并使用的场景.有的时候会发现,很多分散的XXXXRepository的逻辑都是基本一致的,于是 ...

  7. 使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求

    需求 需求很简单:如何创建新的TodoList和TodoItem并持久化. 初学者按照教程去实现的话,应该分成以下几步:创建Controller并实现POST方法:实用传入的请求参数new一个数据库实 ...

  8. 使用.NET 6开发TodoList应用文章索引

    系列导航 使用.NET 6开发TodoList应用(1)--系列背景 使用.NET 6开发TodoList应用(2)--项目结构搭建 使用.NET 6开发TodoList应用(3)--引入第三方日志 ...

  9. 开发错误日记 12: Unsupported major.minor version 52.0

    开发错误日记 12: Unsupported major.minor version 52.0 在编译时出现如下错误: java.lang.UnsupportedClassVersionError: ...

随机推荐

  1. day07 MySQL索引事务

    day07 MySQL索引事务 昨日内容回顾 pymysql模块 # 链接数据库都是使用这个模块的 # 创建链接 import pymysql conn = pymysql.connect( host ...

  2. WebService学习总览

    [1]WebService简介 https://blog.csdn.net/xtayfjpk/article/details/12256663 [2]CXF中Web服务请求处理流程 https://b ...

  3. Docker学习(五)——Docker仓库管理

    Docker仓库管理     仓库(Repository)是集中存放镜像的地方. 1.Docker Hub       目前Docker官方维护了一个公共仓库Docker Hub.大部分需求都可以通过 ...

  4. JavaIO——File类

    1.File文件类 File类(描述具体文件或文件夹的类):是唯一一个与文件本身操作有关的程序类,可完成文件的创建.删除.取得文件信息等操作.但不能对文件的内容进行修改. (1)File类的基本使用 ...

  5. Mysql中replace与replace into的用法讲解

    Mysql replace与replace into都是经常会用到的功能:replace其实是做了一次update操作,而不是先delete再insert:而replace into其实与insert ...

  6. Spring Cloud中,如何解决Feign整合Hystrix第一次请求失败的问题

    Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间 ...

  7. Mysql B-Tree和B+Tree索引

    Mysql B-Tree和B+树索引 Mysql加快数据查找使用B-Tree数据结构存储索引数据,InnoDB存储引擎实际使用B+Tree.下面首先介绍下B-Tree和B+Tree的区别: 一.B树和 ...

  8. Linux中的正则

    目录 一.匹配规则 二.举例 一.匹配规则 * 匹配 0 或多个字符 ? 匹配任意一个字符 [list] 匹配 list 中的任意单一字符 [^list] 匹配 除list 中的任意单一字符以外的字符 ...

  9. AD小白如何发板厂制板--导出gerber文件和钻孔文件+嘉立创下单教程

    AD如何发工程制板子? 方式1,发PCB源文件给板厂 方式2,发一些工艺文件给板厂,这样就无须泄漏你的PCB源文件了,一个硬件工程师必须要掌握方式2. 方式2要做的就是导出gerber文件和钻孔文件, ...

  10. HBuilderX无法启动微信小程序?仅三步

    1.复制微信开发者工具启动路径 : "C:\Program Files (x86)\Tencent\微信web开发者工具\微信web开发者工具.exe" 不要后面的 "微 ...