自定义HttpFilter模块完善

 

背景

  在12月由于要针对项目做用户操作日志,但不想在每个方法里去增加代码,写入用户日志。因为这样具体的方法违背职责单一的原则,若后期日志内容格式发生变更,或其他什么需求,该方法代码主要一变在变,故使用HttpModule模块来完成此功能,具体请看:由做网站操作日志想到的HttpModule应用

  经过一个月的实际运用与完善,现在可以再次总结下。

拦截时机

  现在的版本中,拦截的依据是,在每次请求发生的过程中,拦截控制器类请求,重定向http输出流,并分析出Controller与Action,接下来查找是否有方法监控了此控制器,若有,则分析出请求输入参数,与此次请求输出内容,存储在FilterContext中,交给该方法,完成相应逻辑。

  由于在最初的写法中,是针对所有的请求进行流的重定向,在asmx下,会遇到问题,只要重定向了,调用服务的客户端会提示400 Http Bad Request 。这个具体的错误原因,还不清楚,但正是由于该错误,让我发现,我之前拦截的时机是错误的,理应放在请求之前,判断是否满足拦截的规则,若满足,则重定向输出流。

读取用户名

  在Module模块中总会出点问题,最后使用了Cookie记住用户名,并直接定义为FilterContext一个属性。解释下这样做的原因:由于记住用户名的方式有很多,如Session、Cookie,即读取用户名的方式是可变的,所以尽可能将变化的内容在前面解决,这样监听控制器的方法,直接根据该属性获取用户名,否则用户名的读取时机,放在每个监听控制器模块之后,读取方式一旦发生变更,所有的模块都要改变,当然也可以通过继承一个base类来避免这么大的改变。

  在这里我想表达的意思是:我们做类似底层库的东西,尽可能稳定,将变化点集中在库本身,这样依赖该库的应用才能稳定。若.net版本更新过程中,API都不稳定,想必我们也不会在去使用它。

应用之写入日志

  典型例子如下:

        [FilterMethod("Login", "Login")]
public void Login(FilterContext context)
{
//解析输出内容,这里针对要监听的控制器和方法来写的
var arr = context.OutputBody.Split('|');
var log = string.Format("userName:{0} password:{1}",arr);
FilterLog.Log.Info(log);
}

  该方法表达的意思是,监控LoginController的Login方法。由于我们需要分析请求输出结果,所以分析的规则,与控制器是强依赖的,控制器的方法是怎么返回数据的,我们此处就要根据规则解析。我在项目中使用的是Json,所以监控的地方都需要Json的反序列化,这里仅仅是一个Demo。

  另外一个方法可以监听一个控制器下的多个方法,或者多个控制器。这样是旨在解决有很多Action,输入参数和输出参数都是相同的,可能由于业务不同,仅仅在方法名和内部实现中有不同。

应用之更新缓存

  首先关于Cache的应用,可以读下此文章,Asp.Net Cache高级用法 。

  由于此处我没有写例子,先描述我在项目中运用的情况。系统有很多数据字典,在请求该数据字典时,程序首先从数据库加载字典数据,并放入缓存,此时放入缓存有个技巧,设置过期时间,并设置移除缓存前的回调,我们来看看具体的方法定义:

        //
// 摘要:
// 将对象与依赖项、到期策略以及可用于在从缓存中移除项之前通知应用程序的委托一起插入到 System.Web.Caching.Cache 对象中。
//
// 参数:
// key:
// 用于引用对象的缓存键。
//
// value:
// 要插入到缓存中的对象。
//
// dependencies:
// 该项的文件依赖项或缓存键依赖项。当任何依赖项更改时,该对象即无效,并从缓存中移除。如果没有依赖项,则此参数包含 null。
//
// absoluteExpiration:
// 所插入对象将到期并被从缓存中移除的时间。要避免可能的本地时间问题(例如从标准时间改为夏时制),请使用 System.DateTime.UtcNow
// 而不是 System.DateTime.Now 作为此参数值。如果使用绝对到期,则 slidingExpiration 参数必须设置为 System.Web.Caching.Cache.NoSlidingExpiration。
//
// slidingExpiration:
// 缓存对象的上次访问时间和对象的到期时间之间的时间间隔。如果该值等效于 20 分钟,则对象在最后一次被访问 20 分钟之后将到期并被从缓存中移除。如果使用可调到期,则
// absoluteExpiration 参数必须设置为 System.Web.Caching.Cache.NoAbsoluteExpiration。
//
// onUpdateCallback:
// 从缓存中移除对象之前将调用的委托。可以使用它来更新缓存项并确保缓存项不会从缓存中移除。
//
// 异常:
// System.ArgumentNullException:
// key、value 或 onUpdateCallback 参数为 null。
//
// System.ArgumentOutOfRangeException:
// 将 slidingExpiration 参数设置为小于 TimeSpan.Zero 或大于一年的等效值。
//
// System.ArgumentException:
// 为要添加到 Cache 中的项设置 absoluteExpiration 和 slidingExpiration 参数。- 或 -dependencies
// 参数为 null,absoluteExpiration 参数设置为 System.Web.Caching.Cache.NoAbsoluteExpiration
// 并且 slidingExpiration 参数设置为 System.Web.Caching.Cache.NoSlidingExpiration。
public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemUpdateCallback onUpdateCallback);

  仔细看看onUpdateCallback参数的描述:从缓存中移除对象之前将调用的委托。可以使用它来更新缓存项并确保缓存项不会从缓存中移除。

  我在把数据字典放入缓存的同时传递读取缓存的委托,这样在主动移除缓存或者缓存过期时都将再次调用此委托,将数据字典再次放入缓存。所以一旦数据字典发生了变更,如增删改,那么就主动将字典缓存移除,它就可以自动更新过来,是不是很方便呢。

  区别于写操作日志,不过是处理逻辑发生了变化,他们都需要请求的输入和输出。

其他

  1.由于使用HttpModule来完成此功能,如需正常运行,需要在WebConfig中注册该模块。详见Demo。

  2.项目中使用了Log4Net记录文本日志,并可以根据功能分类。详见:Log4Net日志分类维护

测试代码下载

自定义HttpFilter模块完善的更多相关文章

  1. 重新想象 Windows 8.1 Store Apps (90) - 通信的新特性: 通过 HttpBaseProtocolFilter 实现 http 请求的缓存控制,以及 cookie 读写; 自定义 HttpFilter; 其他

    [源码下载] 重新想象 Windows 8.1 Store Apps (90) - 通信的新特性: 通过 HttpBaseProtocolFilter 实现 http 请求的缓存控制,以及 cooki ...

  2. 创建自定义 HTTP 模块

    本主题中描述的自定义 HTTP 模块阐释了 HTTP 模块的基本功能.在响应下面两个事件时调用该模块:BeginRequest 事件和 EndRequest 事件.这使该模块可以在处理页请求之前和之后 ...

  3. WPF 自定义 MessageBox (相对完善版)

    WPF 自定义 MessageBox (相对完善版)     基于WPF的自定义 MessageBox. 众所周知WPF界面美观.大多数WPF元素都可以简单的修改其样式,从而达到程序的风格统一.可是当 ...

  4. Dojo初探之2:设置dojoConfig详解,dojoConfig参数详解+Dojo中预置自定义AMD模块的四种方式(基于dojo1.11.2)

    Dojo中想要加载自定义的AMD模块,需要先设置好这个模块对应的路径,模块的路径就是这个模块的唯一标识符. 一.dojoConfig参数设置详解 var dojoConfig = { baseUrl: ...

  5. 自定义Func模块

    自定义Func模块 (1)自定义模块步骤 (2)生成模块 [root@controller modules]# cd /usr/lib/python2.7/site-packages/func/min ...

  6. 创建和注册自定义 HTTP 模块

    本演练演示自定义 HTTP 模块的基本功能. 对于每个请求,都需要调用 HTTP 模块以响应 BeginRequest 和 EndRequest 事件. 因此,该模块在处理请求之前和之后运行. 如果 ...

  7. linux环境下 python环境import找不到自定义的模块

    linux环境下 python环境import找不到自定义的模块 问题现象: Linux环境中自定义的模块swport,import swport 出错.swport模块在/root/sw/目录下. ...

  8. 演练:创建和注册自定义 HTTP 模块

    本演练演示自定义 HTTP 模块的基本功能. 对于每个请求,都需要调用 HTTP 模块以响应 BeginRequest 和 EndRequest 事件. 因此,该模块在处理请求之前和之后运行. 如果 ...

  9. JS高阶---闭包应用(自定义JS模块)

    [自定义JS模块] [闭包案例] (1)案例1 对应的模块文件 (2)案例2---使用匿名函数 对应的模块文件 案例2分析:因为内部函数引用了外部函数的变量,且存在嵌套关系,所以是闭包,分析结构图如下 ...

随机推荐

  1. View中选择的数据行中的部分数据传入到Controller中

    将View中选择的数据行中的部分数据传入到Controller中   ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NE ...

  2. Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization

    跑 service network restart  NIC出现无法启动,他说,没有发现 Bringing up interface eth0: Device eth0 does not seem t ...

  3. Babelfish (STL)

    题目描述 You have just moved from Waterloo to a big city. The people here speak an incomprehensible dial ...

  4. 【高德地图API】从零开始学高德JS API(五)路线规划——驾车|公交|步行

    原文:[高德地图API]从零开始学高德JS API(五)路线规划——驾车|公交|步行 先来看两个问题:路线规划与导航有什么区别?步行导航与驾车导航有什么区别? 回答: 1.路线规划,指的是为用户提供3 ...

  5. 实战parse_ini_file()及扩展函数解析ini文件完整版

    文章来源:PHP开发学习门户 地址:http://www.phpthinking.com/archives/587 在PHP站点开发的过程中,往往会用到读取ini參数配置文件,比方须要訪问一些复杂的借 ...

  6. ajax form表单提交 input file中的文件

    ajax form表单提交 input file中的文件 现今的主流浏览器由于ajax提交form表单无法把文件类型数据提交到后台,供后台处理,可是开发中由于某些原因又不得不用ajax提交文件, 为了 ...

  7. ASP.NET学习笔记2--自己写代码绑定Gridview

    像以前一样,先写好自己的样式布局, 第二步,在数据库建立一个商品表 代码如下: CREATE TABLE [SHANGPING_INFO] ( [Shangping_Id] INT PRIMARY K ...

  8. SSAS系列——【01】准备知识

    原文:SSAS系列--[01]准备知识 关于SQL Server 产品,我从2004年就开始使用了,SQL Server 2K,2K5,2K8,到如今已经准6年了,说来惭愧,这六年来所涉及的内容都是在 ...

  9. SqlDataReader的关闭问题

    原文:SqlDataReader的关闭问题 昨天一个朋友使用Repeater绑定数据源时,老是出现"阅读器关闭时尝试调用 FieldCount 无效."错误. 我看了他的代码,使用 ...

  10. 数据结构--画画--最小生成树(Prim算法)

    通信网络的最小生成树配置,它是使右侧的生成树值并最小化.经常使用Prim和Kruskal算法.看Prim算法:以防万一N={V,{E}}它是在通信网络,TE它是N设置边的最小生成树.从算法U={u0} ...