系列导航及源代码

需求

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. nuxt使用图片懒加载vue-lazyload

    对于nuxt使用第三方插件的方式大体都是都是一致的,就是在plugins文件夹中新增插件对应的js文件进行配置与操作,然后在nuxt.config.js文件的plugins配置项中引入新建的js文件就 ...

  2. adverb

    An adverb is a word or an expression that modifies a verb, adjective, another adverb, determiner [限定 ...

  3. 容器之分类与各种测试(四)——unordered_set和unordered_map

    关于set和map的区别前面已经说过,这里仅是用hashtable将其实现,所以不做过多说明,直接看程序 unordered_set #include<stdexcept> #includ ...

  4. 用oracle中的Row_Number实现分页

    Row_Number实现分页   1:首先是 select ROW_NUMBER() over(order by id asc) as 'rowNumber', * from table1 生成带序号 ...

  5. 【JAVA今法修真】 第四章 redis特性 击穿雪崩!

    感谢这段时间大家的支持,关注我的微信号:南橘ryc ,回复云小霄,就可以获取到最新的福利靓照一张,还等什么,赶快来加入我们吧~ "明日便是决赛了,咋只会用法器没练过法术呢.". 选 ...

  6. shell脚本 用户登录服务器发送钉钉提醒

    一.企业微信配置 1.获取AgentId(AppID).Secret .CropID.部门ID 创建一个企业微信应用获取到AgentId(AppID).Secret 2.获取CropID,点击 &qu ...

  7. HGAME2021 week4 pwn writeup

    第四周只放出两道题,也不是很难. house_of_cosmos 没开pie,并且可以打got表. 在自写的输入函数存在漏洞.当a2==0时,因为时int类型,这里就会存在溢出.菜单题,但是没有输出功 ...

  8. 10 - Vue3 UI Framework - Tabs 组件

    标签页是非常常用的组件,接下来我们来制作一个简单的 Tabs 组件 返回阅读列表点击 这里 需求分析 我们先做一个简单的需求分析 可以选择标签页排列的方向 选中的标签页应当有下划线高亮显示 切换选中时 ...

  9. CF70B Text Messaging 题解

    Content 有一个短信软件最多只能够上传长度为 \(n\) 的消息.现在你有一段话,但不一定能够一次发出.这段话由若干句话组成,以 ..? 或者 ! 为结尾.你不能够将一句话拆开来发,但是如果容量 ...

  10. CF572B Order Book 题解

    Content 账单里面有 \(n\) 条记录,只有卖出记录和买入记录两种,并且都包含两个信息 \(p_i,q_i\),现在根据这些记录,请执行如下操作: 将所有 \(p_i\) 相等的同种记录合并( ...