.NetCore6.0实现ActionFilter过滤器记录接口请求日志
文章目录
- 目的
- 实现案例:
- 一.首先我们新建一个WebApi项目
- 二.配置 appsettings.json 文件,配置日志存放路径
- 三.创建 Model 文件夹,创建AppConfig类和ErrorLog类
- 1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值
- 2.在ErrorLog类中,实现往日志文件中书写接口日志的操作
- 四.创建Filter文件夹,创建ActionFilter实现行动过滤器,实现记录接口日志
- 五.在 Program 中配置行动过滤器 ActionFilter
- 六.创建一个接口,调试执行一下
- 结果
目的
使用ActionFilter记录接口请求日志
实现案例:
https://gitee.com/hgcjd/WebApiFilter
一.首先我们新建一个WebApi项目


开发环境,我们去掉HTTPS配置

二.配置 appsettings.json 文件,配置日志存放路径

{"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AppConfig": {"DirectoryPath": "项目本地日志存放路径"},"AllowedHosts": "*"
}
三.创建 Model 文件夹,创建AppConfig类和ErrorLog类

1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值
/// <summary>
/// 获取.NetCore配置文件信息
/// </summary>
public class AppConfig
{public static string GetConfigInfo(string Key){var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");IConfigurationRoot configuration = builder.Build();string configStr = configuration.GetSection($"{Key}").Value;if (!string.IsNullOrWhiteSpace(configStr)){return configStr;}return null;}}
2.在ErrorLog类中,实现往日志文件中书写接口日志的操作
public class ErrorLog
{//获取日志文件路径private static string DirectoryPath = AppConfig.GetConfigInfo("AppConfig:DirectoryPath");/// <summary>/// 写入操作日志到文件中/// </summary>/// <param name="moduleName">模块名字</param>/// <param name="message">错误文本信息</param>/// <param name="ex">异常</param>public static void Write(string message, Exception ex){string directoryPath = $@"{DirectoryPath}{DateTime.Now.ToString("yyyyMMdd")}"; // 目标目录路径if (!Directory.Exists(directoryPath)){// 如果目录不存在,则新建文件夹Directory.CreateDirectory(directoryPath);}string filePath = directoryPath + $@"\{DateTime.Now.ToString("yyyyMMddHH")}.log"; // 目标文件路径if (!File.Exists(filePath)){// 如果文件不存在,则创建文件using (File.Create(filePath)){//Console.WriteLine("文件已创建");}}LogToFile(filePath, message);}/// <summary>/// 写入操作日志到文件中/// </summary>/// <param name="moduleName">模块名字</param>/// <param name="ex">异常</param>public static void Write(string moduleName, Exception ex){Write(moduleName, moduleName, ex);}/// <summary>/// 写入过程数据或说明到文件中,以便跟踪/// </summary>/// <param name="moduleName">模块名字</param>/// <param name="ex">异常</param>public static void Write(string message){Write(String.Empty, message, null);}/// <summary>/// 文本写入/// </summary>/// <param name="logMessage"></param>private static void LogToFile(string logFilePath, string logMessage){using (StreamWriter sw = File.AppendText(logFilePath)){sw.WriteLine($"{logMessage}");}}
}
四.创建Filter文件夹,创建ActionFilter实现行动过滤器,实现记录接口日志

这里我们引入Newtonsoft.Json包

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Text.Json;
using Microsoft.Extensions.Primitives;
using WebApiTest.Model;
using System.Net.Http.Json;namespace WebApiTest.Filter
{/// <summary>/// 方法过滤器/// </summary>public class ActionFilter : IActionFilter{/// <summary>/// 监控日志/// </summary>public static ILogger LoggerMonitor { get; set; }/// <summary>/// 错误日志/// </summary>public static ILogger LoggerError { get; set; }private Stopwatch _stopwatch;/// <summary>/// 创建请求日志文本/// </summary>/// <param name="method"></param>/// <param name="controllerName"></param>/// <param name="actionName"></param>/// <param name="actionArgs"></param>/// <returns></returns>private static string CreateRequestLogText(string method, string controllerName, string actionName, string requestHead, string requestBody){StringBuilder sb = new StringBuilder();sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 请求{method}/{controllerName}/{actionName}接口,请求Head:{requestHead}\n");sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 请求{method}/{controllerName}/{actionName}接口,请求Body:{requestBody}\n");return sb.ToString();} /// <summary>/// 创建响应日志文本/// </summary>/// <param name="method"></param>/// <param name="controllerName"></param>/// <param name="actionName"></param>/// <param name="result"></param>/// <returns></returns>private static string CreateResponseLogText(string method, string controllerName, string actionName, object result){StringBuilder sb = new StringBuilder();sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 完成请求{method}/{controllerName}/{actionName}接口,返回结果:");if (result != null){sb.Append($"{JsonConvert.SerializeObject(result)}");}else{sb.Append($"无");}return sb.ToString();}/// <summary>/// 方法执行前,记录接口请求参数/// </summary>/// <param name="context"></param>/// <exception cref="NotImplementedException"></exception>public async void OnActionExecuting(ActionExecutingContext context){ErrorLog.Write("==================================================================================================================================");_stopwatch = new Stopwatch();_stopwatch.Start();//throw new NotImplementedException();if (LoggerMonitor != null){//记录请求参数日志ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;if (desc != null){Dictionary<string, object> headers = new Dictionary<string, object>();var requestHeaders = context.HttpContext.Request.Headers;// 访问请求中的 header 信息foreach (var header in requestHeaders){headers.Add(header.Key, header.Value);}var requestHead = JsonConvert.SerializeObject(headers);Dictionary<string, object> bodys = new Dictionary<string, object>();var actionArguments = context.ActionArguments;// 访问请求中的参数foreach (var argument in actionArguments){//dic.Add(argument.Key, argument.Value);var parameter = JsonConvert.DeserializeObject<Dictionary<string, object>>(argument.Value.ToString());foreach (var item in parameter){bodys.Add(item.Key, item.Value);}}var requestBody = JsonConvert.SerializeObject(bodys);var logText = CreateRequestLogText(context.HttpContext.Request.Method, desc.ControllerName, desc.ActionName, requestHead, requestBody);LoggerMonitor.LogDebug(logText);ErrorLog.Write(logText);}}}//方法执行后,记录接口请求结果public void OnActionExecuted(ActionExecutedContext context){//throw new NotImplementedException();_stopwatch.Stop();long elaspsedMillisedconds = _stopwatch.ElapsedMilliseconds;string msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 接口执行时间:{elaspsedMillisedconds}毫秒";//ErrorLog.Write(msg);if (context.Exception != null){// 记录异常日志if (LoggerError != null){LoggerError.LogError(context.Exception, context.Exception.Message);ErrorLog.Write($@"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 接口异常:{JsonConvert.SerializeObject(context.Exception)}");ErrorLog.Write($@"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 异常提示信息:{JsonConvert.SerializeObject(context.Exception.Message)}");}}if (LoggerMonitor != null){// 记录请求结果日志ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;if (desc != null){ObjectResult rst = context.Result as ObjectResult;object rstValue = rst != null ? rst.Value : null;var logText = CreateResponseLogText(context.HttpContext.Request.Method,desc.ControllerName,desc.ActionName,rstValue);LoggerMonitor.LogDebug(logText);ErrorLog.Write(logText);}}ErrorLog.Write(msg);ErrorLog.Write("==================================================================================================================================");}}
}
五.在 Program 中配置行动过滤器 ActionFilter

using Microsoft.AspNetCore.Mvc.Filters;
using WebApiTest.Filter;var builder = WebApplication.CreateBuilder(args);var configuration = builder.Configuration;
// Add services to the container.builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();#region 接口行动过滤器
// Add services to the container.
builder.Services.AddSingleton<IActionFilter>(new ActionFilter()); // 初始化 LoggerMonitor
builder.Services.AddSingleton<IActionFilter>(new ActionFilter()); // 初始化 LoggerError
builder.Services.AddScoped<ActionFilter>(); // 注册 ActionFilterbuilder.Services.AddControllers(options => {options.Filters.Add(new ActionFilter());
});var serviceProvider = builder.Services.BuildServiceProvider();
ActionFilter.LoggerError = serviceProvider.GetRequiredService<ILogger<ActionFilter>>();
ActionFilter.LoggerMonitor = serviceProvider.GetRequiredService<ILogger<ActionFilter>>();
#endregionvar app = builder.Build();// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseAuthorization();app.MapControllers();app.Run();
六.创建一个接口,调试执行一下

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;namespace WebApiTest.Controllers
{[ApiController][Route("Home")]public class HomeController : ControllerBase{[HttpGet][Route("Index")]public string Index(){return JsonConvert.SerializeObject(new { code = 0,data=true,msg="成功"});}}
}

接口请求成功,接着我们查看appsettings.json中配置的路径文件中,日志的记录情况

结果
.NetCore6.0项目,ActionFilter行动过滤器搭建成功,之后这个框架的接口请求就都会携带日志了
相关文章:
.NetCore6.0实现ActionFilter过滤器记录接口请求日志
文章目录 目的实现案例:一.首先我们新建一个WebApi项目二.配置 appsettings.json 文件,配置日志存放路径三.创建 Model 文件夹,创建AppConfig类和ErrorLog类1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值2.在ErrorLog类中&a…...
代码详解:2024美团春招实习笔试第一场0309,是难还是简单?
前言: 1.第一题(模拟) 2.第二题(模拟) 3.第三题(二维前缀和) 4.第四题的思维(双指针) 5.第五题难度比较大(并查集删边离散化) 一.小美的MT MT 是美团的…...
平衡二叉树
前言 在关键字排列随机的情况下,二叉排序树的平均查找长度和 l o g n log n logn是等数量级的。在某些情况下,尚需在构成二叉排序树的过程中进行“平衡化”处理,使其成为平衡二叉树。 如果任何初始化序列构成的二叉排序树都是平衡二叉树&…...
脚本自动化 设置快捷方式并设置为管理员运行
自动化创建快捷方式并设置为始终以管理员权限运行,可以通过编写批处理脚本来实现。以下是一个创建.bat批处理文件快捷方式并设置为管理员运行的示例脚本: batch echo off set SCRIPT_PATH"C:\Scripts\myScript.bat" set SHORTCUT_PATH"%…...
TypeScript学习笔记(上):TypeScript的介绍、安装及常用类型
我对TypeScript的理解就是,TypeScript是增加了类型校验的JavaScript,能够把运行期错误提升至编译期 目录 TypeScript是什么? 安装编译 TS 的工具包 运行 TS 的步骤 TypeScript 常用类型 JS 已有类型 TS 新增类型 简单数据类型 数组类…...
Vue3学习记录(六)--- 组合式API之依赖注入和异步组件
一、依赖注入 1、简介 在前面的笔记中,我们学习过父组件向子组件传递数据时,需要借助props来实现。但如果父组件想要向孙子组件传递数据,那就要连续使用两层props逐级向下传递,如果要接收数据的是更深层的后代组件࿰…...
JZ76 删除链表中重复的结点
/*public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} } */import java.util.*; public class Solution {public ListNode deleteDuplication(ListNode pHead) {//初步想想法: 弄一个hashmap 然后进行key存储起来。然后 如果存…...
20.2 nginx
20.2 nginx 1. 学习目标2. 介绍2.1 正向代理2.2 反向代理2.3 动态静态资源分离2.4 nginx优缺点3. 安装3.1 Linux安装****************************************************************************************************************************************************…...
MySQL学习Day26——事务基础知识
一、数据库事务概述: 事务是数据库区别于文件系统的重要特性之一,事务会让数据始终保持一致性,能通过事务机制恢复到某个时间点,可以保证提交到数据库的修改不会因为系统崩溃而丢失 1.查看引擎支持事务的情况:只有InnoDB存储引擎支持事务 SHOW ENGINES; 2.基本概念: 事…...
three.js 射线Ray,三维空间中绘制线框
效果: 代码: <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div> <div>{{ res1 }}</div> <div>{{ res2 }}</div><…...
【Demo】游戏小地图
简介 该Demo基于2D关卡随机生成项目进行实现,旨在初步探索游戏小地图的制作。 演示 MiniMapDemo 资源下载 百度网盘(提取码:1314) 如果这篇文章对你有帮助,请给作者点个赞吧!...
代码随想录算法训练营Day39 || leetCode 762.不同路径 || 63. 不同路径 II
62.不同路径 每一位的结果等于上方与左侧结果和 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m,vector(n,0));for (int i 0; i < m; i) dp[i][0] 1;for (int j 0; j < n; j) dp[0][j] 1;for (int i 1; i < m; …...
Qt中parent()函数的使用
情景(需求)抽象: A类对象是B类对象的成员变量。 B类对象是A类对象的父亲。 A类对象中包含按钮,点击按钮,调用B类的成员函数。 示例: A类: #pragma once#include <QWidget> #include "ui_QtWidgetsCla…...
Python基础学习(5)流程控制
文章目录 一. 程序三大执行流程二. 分支结构1.单分支结构(if)2.双分支结构(if..else)3.多分支结构(if..elif..else) 二,缩进(tab键)三,循环结构1.while循环2.for循环①遍历字典 五.break,continue和pass语句1.break,continue2.pass Python基础学习(1)基本…...
代码随想录刷题笔记 DAY 42 | 最后一块石头的重量 II No.1049 | 目标和 No.494 | 一和零 No.474
文章目录 Day 4301. 最后一块石头的重量 II(No. 1049)<1> 题目<2> 笔记<3> 代码 02. 目标和(No. 494)<1> 题目<2> 笔记<3> 代码 03. 一和零(No. 474)<1> 题目&l…...
793.高精度乘法(acwing)
文章目录 793.高精度乘法题目描述高精度乘法 793.高精度乘法 题目描述 给定两个正整数A和B,请你计算A * B的值。 输入格式 共两行,第一行包含整数A,第二行包含整数B。 输出格式 共一行,包含A * B的值。 数据范围 1≤A的长度≤…...
考研经验|如何从考研失败中走出来?
对我来说,太丢人了 其实我在本科的时候在同学眼中,一直很优秀,每年奖学金必有我的,国家励志奖学金,国家奖学金,这种非常难拿的奖学金,我也拿过,本科期间学校有一个公费去新西兰留学的…...
SpringBoot如何修改pom依赖的默认版本号
1、找到SpringBoot父工程依赖。 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version> </parent>2、ctrl 鼠标左键点击<artifact…...
UDP与TCP:了解这两种网络协议的不同之处
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
String、StringBuffer基本用法
一、StringBuffer基本用法 1.append():字符串连接操作 StringBuffer sb new StringBuffer();sb.append("a");sb.append("b");sb.append("c");sb.append("哈哈").append("d");System.out.println(sb);2.insert():在任意位…...
【AIAgent架构核心机密】:基于237个真实项目验证的目标分解熵值模型(附可运行评估脚本)
第一章:目标分解在AIAgent架构中的战略定位 2026奇点智能技术大会(https://ml-summit.org) 目标分解并非AI Agent开发中的辅助技巧,而是其认知架构的底层编排范式。它将高层任务语义(如“策划一场低碳主题的社区科普活动”)系统性…...
零碳入门:碳核算的三大范围
在企业推进碳中和的过程中,碳排放核算是最基础的管理工作。目前,GHG核算体系是全球通用的标准,也是国内外碳披露、碳交易以及ESG评价的底层依据。注意,GHG不止包含二氧化碳,也包含其他温室气体。谈到企业碳核算&#x…...
推荐一些可以用于论文降重的软件:2026年实测TOP5功能对比,AIGC率最低降至5%!
【博主避坑前言】 “知网文字复制比查重4.5%,妥妥过关。但右边赫然写着:AIGC疑似率 89%,导师直接把初稿扔回给我,让我重写!” 类似这样的粉丝私信,在2026年的毕业季已经成了重灾区。很多同学为了降重&#…...
基于领航者人工势场法的队形变化避障控制及Matlab代码仿真:路径规划、拓扑结构与集群编队控制算法
基于领航者人工势场法的队形变化避障控制matlab代码仿真,路径规划,改进人工势场法,拓扑结构,集群,变换队形,基于领航者与人工势场法相结合的编队控制算法,可随意变换队形 增加机器人个数一、代码…...
决策树核心算法详解与应用,机器学习数据挖掘核心知识点
决策树是一种树形结构的机器学习模型,它通过一系列“是/否”问题(基于数据特征)对实例进行分类或回归预测,最终形成一个类似流程图的结构。 其核心思想是递归地将数据集分割成更纯的子集。 在数据挖掘中,决策树因其直…...
避坑指南:STM32CUBEMX串口配置常见问题及解决方案(USART/printf重定向)
STM32CubeMX串口开发实战:从原理到调试的完整避坑手册 第一次在STM32CubeMX里配置串口时,我盯着那个115200的波特率数值发呆了十分钟——这个看似简单的数字背后,隐藏着多少新手会踩的坑?从时钟树配置到DMA缓冲区,从p…...
计算机视觉需要哪些数学基础?如何高效学习线性代数和概率论?
计算机视觉需要哪些数学基础?如何高效学习线性代数和概率论? 标签:#计算机视觉、#线性代数、#人工智能、#深度学习、#自然语言处理、#神经网络、#机器学习 ### 一、痛点引入:为什么很多人怕CV数学?真相是什么…...
三相PFC控制固件代码功能解析
概述 本文档详细分析了一个用于三相功率因数校正(PFC)控制系统的嵌入式固件代码。该代码基于特定的处理器架构,实现了复杂的电力电子控制算法,主要用于车载充电系统等高性能电源应用场景。 系统架构 硬件抽象层 代码通过硬件抽象层…...
generators-with-stylegan2高级使用技巧:如何生成定制化人脸素材
generators-with-stylegan2高级使用技巧:如何生成定制化人脸素材 【免费下载链接】generators-with-stylegan2 Here is a series of face generators based on StyleGAN2 项目地址: https://gitcode.com/gh_mirrors/ge/generators-with-stylegan2 generators…...
STM32F4 HAL库串口+DMA接收数据,为啥第一次总是收不到?一个配置顺序的坑
STM32F4 HAL库串口DMA接收异常解析:从第一次失败到稳定运行的深度优化 最近在调试STM32F407的串口DMA接收功能时,遇到了一个典型问题——系统上电后的第一次数据接收总是失败,而后续通信却完全正常。这个现象在嵌入式开发中并不罕见ÿ…...
