前几天整理了一下开发框架的内容,记录一下我们的项目中,对 System.Web.PreApplicationStartMethodAttribute这个特性的应用场景:
首先,介绍一下这个属性,参考:https://haacked.com/archive/2010/05/16/three-hidden-extensibility-gems-in-asp-net-4.aspx/
这个特性是Asp.net4新增的一个功能,可以让你的代码在站点的Application_Start方法之前运行,可以方便的注入或进行一些初始化工作,而不需要在每个项目去重复写这些代码。
以下是应用场景:
一、对线上运行中的项目,希望统一配置HttpWebRequest的UserAgent,并添加一些诊断信息和链路信息,
同时把请求的头、请求Body和响应的头和Body数据完整记录日志。
如果按常规作法,一般是封装一个dll类库,要求所有成员,使用这个类库里封装的方法,但是对旧项目,需要进行改造,有些新成员的新项目也可能忘记引用这个类库,或者引用了,但是没有使用它。
我们的作法,封装一个类库,加上PreApplicationStartMethodAttribute属性,然后编译成dll,在Jenkins推送线上站点时,把这个dll也推送到站点的bin目录下,整个过程对业务项目是透明的,RD们只需要按自己的做法去开发和部署即可。
实现步骤:
1、新建一个类库,打开项目的Properties/AssemblyInfo.cs,在里面添加一行代码:
[assembly: PreApplicationStartMethod(typeof(CustomWebRequest), "Patch")]
2、完成方法 CustomWebRequest.Patch() 的实现,对HttpWebRequest进行扩展实现,并增加2个Filter:
UserAgentFilter 和 NLogFilter,实现UserAgent的统一和请求日志的输出。
这整个类库的整体实现和测试代码,我扔到Github上了:
https://github.com/youbl/DemoCode/tree/master/src/Library/Beinet.Request
可以下载这个项目,编译后,随便扔到你的某个站点bin目录下,可以看到有日志输出,并且UserAgent也变了。
二、接入Pinpont,实现项目的整个请求链路跟踪、Sql执行情况记录、Redis访问情况记录等;
三、接入注册服务发现,启动时进行服务注册,退出时进行服务卸载。
注:Nuget上有个WebActivatorEx库,它也定义了相同的一个类:PreApplicationStartMethodAttribute,简单来说:WebActivatorEx.PreApplicationStartMethodAttribute 和 System.Web.PreApplicationStartMethodAttribute 实现的功能是一样的,通过反编译WebActivatorEx组件的源码,可以看到有如下定义:
[assembly: System.Web.PreApplicationStartMethod(typeof (ActivationManager), "Run")]
说明 WebActivatorEx 也是基于System.Web.PreApplicationStartMethodAttribute实现,但是:
1、WebActivatorEx.PreApplicationStartMethodAttribute 支持 Order属性,可以指定多个方法的执行顺序:
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Starter), "Start", Order = 12)]
2、WebActivatorEx 支持额外的2个属性:
WebActivatorEx.PostApplicationStartMethodAttribute
WebActivatorEx.ApplicationShutdownMethodAttribute
// 在Application_Start运行结束后执行这个方法: Starter.End()
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(Starter), "End", Order = 12)]
// 在应用程序退出时执行这个方法: Starter.Quit()
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(Starter), "Quit", Order = 12)]
通过ActivationManager.Run()方法的源码可以看到,这2个属性是通过注册一个IHttpModule来实现,
在Module的Init里调用PostApplicationStartMethod,
在Module的Dispose里调用ApplicationShutdownMethod:
if (HostingEnvironment.IsHosted)
{
Type moduleType = typeof (ActivationManager.StartMethodCallingModule);
if (flag)
(WebConfigurationManager.GetWebApplicationSection("system.web/httpModules") as HttpModulesSection).
Modules.Add(new HttpModuleAction(moduleType.FullName, moduleType.AssemblyQualifiedName));
else
DynamicModuleUtility.RegisterModule(moduleType);
}
private class StartMethodCallingModule : IHttpModule
{
private static object _lock = new object();
private static int _initializedModuleCount;
public void Init(HttpApplication context)
{
lock (ActivationManager.StartMethodCallingModule._lock)
{
if (ActivationManager.StartMethodCallingModule._initializedModuleCount++ != 0)
return;
ActivationManager.RunPostStartMethods();
}
}
public void Dispose()
{
lock (ActivationManager.StartMethodCallingModule._lock)
{
if (--ActivationManager.StartMethodCallingModule._initializedModuleCount != 0)
return;
ActivationManager.RunShutdownMethods();
}
}
}