编写轻量ajax组件第三篇实现
通过之前的介绍,我们知道要执行页面对象的方法,核心就是反射,是从请求获取参数并执行指定方法的过程。实际上这和asp.netmvc框架的核心思想很类似,它会解析url,从中获取controller和action名称,然后激活controller对象,从请求获取action参数并执action。在webform平台上,我们把方法写在.aspx.cs中,要实现的就是在页面对象还未生成的情况下,执行指定的方法,然后返回结果。
我们先看实现后几个调用例子,这些功能也可以组合使用:
[AjaxMethod]
publicvoidTest1(intindex)
{
//简单调用
}
[AjaxMethod]
publicstringTest2(Testtest)
{
return"参数为一个Test实例";
}
[AjaxMethod(OutputCache=20)]
publicstringTest3(intindex)
{
return"输出结果缓存20秒";
}
[AjaxMethod(ServerCache=20)]
publicstringTest4()
{
return"在服务端缓存20秒";
}
[AjaxMethod(SessionState=SessionState.None)]
publicvoidTest5()
{
//Session未被加载
}
[AjaxMethod(SessionState=SessionState.ReadOnly)]
publicvoidTest6()
{
//Session只能读不能写
}
[AjaxMethod(SessionState=SessionState.ReadWrite)]
publicvoidTest7()
{
//Session可以读写
}
[AjaxMethod(IsAsync=true)]
publicvoidTest8()
{
//异步调用
}
前面我们已经熟悉基本的执行流程,现在直接进入主题。
Ajax约定
通常现在主流浏览器在使用ajax发送异步请求时,请求头都会带上一个:X-Requested-With:XMLHttpRequest的标记。我们也可以直接通过这个标记来判断是不是ajax请求,不过项目中可能有用其它的组件,为了不相互影响,我们加入一个自定义的请求头。这里为:
internalstaticclassAjaxConfig
{
///<summary>
///请求头Ajax标记键
///</summary>
publicconststringKey="AjaxFlag";
///<summary>
///请求头Ajax标记值
///</summary>
publicconststringValue="XHR";
///<summary>
///请求头Ajax方法标记
///</summary>
publicconststringMethodName="";
}
意思是如果http的请求头包含一个AjaxFlag:XHR,就是我们要处理的。另外httpheader的MethodName就表示我们要执行的方法的名称。
AjaxMethodAttribute标记属性
标记属性是给反射用的,在这里定义我们需要的一些功能。我们希望有:
1.可以配置Session状态
2.支持异步Handler
3.支持Get缓存
4.支持服务端缓存
定义如下,用AttributeUsag标记该标记只能用于方法上。
///<summary>
///ajax方法标记属性
///</summary>
[AttributeUsage(AttributeTargets.Method,AllowMultiple=false,Inherited=false)]
publicclassAjaxMethodAttribute:Attribute
{
publicAjaxMethodAttribute()
{
}
privateSessionState_sessionState=SessionState.None;
privateint_outputCache=0;
privateint_serverCache=0;
privateContentType_contentType=ContentType.Plain;
privatebool_isUseAsync=false;
///<summary>
///session状态
///</summary>
publicSessionStateSessionState
{
get{return_sessionState;}
set{_sessionState=value;}
}
///<summary>
///客户端缓存时间,以秒为单位。该标记只对get请求有效
///</summary>
publicintOutputCache
{
get{return_outputCache;}
set{_outputCache=value;}
}
///<summary>
///服务端缓存时间,以秒为单位
///</summary>
publicintServerCache
{
get{return_serverCache;}
set{_serverCache=value;}
}
///<summary>
///输出类型(默认为text/plain)
///</summary>
publicContentTypeContentType
{
get{return_contentType;}
set{_contentType=value;}
}
///<summary>
///使用启用异步处理
///</summary>
publicboolIsAsync
{
get{return_isUseAsync;}
set{_isUseAsync=value;}
}
}
///<summary>
///Session状态
///</summary>
publicenumSessionState
{
None,
ReadOnly,
ReadWrite
}
///<summary>
///输出内容类型
///</summary>
publicenumContentType
{
Plain,
Html,
XML,
Javascript,
JSON
}
各种处理程序和AjaxHandlerFactory
按照上一篇的说法,具体的Handler主要分为两类,异步和非异步;这两类下,对于Session的状态又有3三种,不支持、只支持读(实现IReadOnlySessionState接口)、支持读写(实现IRequiresSessionState接口)。IReadOnlySessionState和IRequiresSessionState都只是标记接口(无任何方法,其实应该用标记属性实现比较合理)。异步的Handler需要实现IHttpAsyncHandler接口,该接口又实现了IHttpHandler。Handler的ProcessRequest方法(或BeginProcessRequest)就是我们要执行方法的地方。定义如下:
非异步状态的Handler:
//不支持Session
internalclassSyncAjaxHandler:IHttpHandler
{
privatePage_page;
privateCacheMethodInfo_cacheMethodInfo;
internalSyncAjaxHandler(Pagepage,CacheMethodInfocacheMethodInfo)
{
_page=page;
_cacheMethodInfo=cacheMethodInfo;
}
publicvoidProcessRequest(HttpContextcontext)
{
//执行方法(下面详细介绍)
Executor.Execute(_page,context,_cacheMethodInfo);
}
publicboolIsReusable
{
get{returnfalse;}
}
publicstaticSyncAjaxHandlerCreateHandler(Pagepage,CacheMethodInfocacheMethodInfo,SessionStatestate)
{
switch(state)
{
caseSessionState.ReadOnly:
returnnewSyncAjaxSessionReadOnlyHandler(page,cacheMethodInfo);
caseSessionState.ReadWrite:
returnnewSyncAjaxSessionHandler(page,cacheMethodInfo);
default:
returnnewSyncAjaxHandler(page,cacheMethodInfo);
}
}
}
//支持只读Session
internalclassSyncAjaxSessionReadOnlyHandler:SyncAjaxHandler,IReadOnlySessionState
{
internalSyncAjaxSessionReadOnlyHandler(Pagepage,CacheMethodInfocacheMethodInfo)
:base(page,cacheMethodInfo)
{
}
}
//支持读写Session
internalclassSyncAjaxSessionHandler:SyncAjaxHandler,IRequiresSessionState
{
internalSyncAjaxSessionHandler(Pagepage,CacheMethodInfocacheMethodInfo)
:base(page,cacheMethodInfo)
{
}
}
异步状态的Handler:
//不支持Session
internalclassASyncAjaxHandler:IHttpAsyncHandler,IHttpHandler
{
privatePage_page;
privateCacheMethodInfo_cacheMethodInfo;
internalASyncAjaxHandler(Pagepage,CacheMethodInfocacheMethodInfo)
{
_page=page;
_cacheMethodInfo=cacheMethodInfo;
}
publicIAsyncResultBeginProcessRequest(HttpContextcontext,AsyncCallbackcb,objectextraData)
{
//执行方法(下面详细介绍)
Action<Page,HttpContext,CacheMethodInfo>action=newAction<Page,HttpContext,CacheMethodInfo>(Executor.Execute);
IAsyncResultresult=action.BeginInvoke(_page,context,_cacheMethodInfo,cb,action);
returnresult;
}
publicvoidEndProcessRequest(IAsyncResultresult)
{
Action<Page,HttpContext,CacheMethodInfo>action=result.AsyncStateasAction<Page,HttpContext,CacheMethodInfo>;
action.EndInvoke(result);
}
publicvoidProcessRequest(HttpContextcontext)
{
thrownewNotImplementedException();
}
publicboolIsReusable
{
get{returnfalse;}
}
publicstaticASyncAjaxHandlerCreateHandler(Pagepage,CacheMethodInfocacheMethodInfo,SessionStatestate)
{
switch(state)
{
caseSessionState.ReadOnly:
returnnewASyncAjaxSessionReadOnlyHandler(page,cacheMethodInfo);
caseSessionState.ReadWrite:
returnnewASyncAjaxSessionHandler(page,cacheMethodInfo);
default:
returnnewASyncAjaxHandler(page,cacheMethodInfo);
}
}
}
//支持只读Session
internalclassASyncAjaxSessionReadOnlyHandler:ASyncAjaxHandler,IReadOnlySessionState
{
internalASyncAjaxSessionReadOnlyHandler(Pagepage,CacheMethodInfocacheMethodInfo)
:base(page,cacheMethodInfo)
{
}
}
//支持读写Session
internalclassASyncAjaxSessionHandler:ASyncAjaxHandler,IRequiresSessionState
{
internalASyncAjaxSessionHandler(Pagepage,CacheMethodInfocacheMethodInfo)
:base(page,cacheMethodInfo)
{
}
}
AjaxHandlerFactory实现了IHandlerFactory接口,用来根据请求生成具体的Handler,它需要在web.config进行注册使用。AjaxHandlerFactory的GetHandler是我们拦截请求的第一步。通过请求头的AjaxFlag:XHR来判断是否需要我们处理,如果是,则创建一个Handler,否则按照普通的方式进行。由于我们的方法是写在.aspx.cs内的,我们的请求是.aspx后缀的,也就是页面(Page,实现了IHttpHandler)类型,Page是通过PageHandlerFactory创建的,PageHandlerFactory也实现了IHandlerFactory接口,表示它是用来创建处理程序的。所以我们需要用PageHandlerFactory来创建一个IHttpHandler,不过PageHandlerFactory的构造函数是protectedinternal类型的,我们无法直接new一个,所以需要通过一个CommonPageHandlerFactory继承它来实现。
通过PageHandlerFactory获得Page后,结合方法名称,我们就可以反射获取AjaxMethodAttribute标记属性了。然后根据它的相关属性生成具体的Handler。具体代码如下:
internalclassCommonPageHandlerFactory:PageHandlerFactory{}
internalclassAjaxHandlerFactory:IHttpHandlerFactory
{
publicvoidReleaseHandler(IHttpHandlerhandler)
{
}
publicIHttpHandlerGetHandler(HttpContextcontext,stringrequestType,stringurl,stringpathTranslated)
{
HttpRequestrequest=context.Request;
if(string.Compare(request.Headers[AjaxConfig.Key],AjaxConfig.Value,true)==0)
{
//检查函数标记
stringmethodName=request.Headers[AjaxConfig.MethodName];
if(methodName.IsNullOrEmpty())
{
Executor.EndCurrentRequest(context,"方法名称未正确指定!");
returnnull;
}
try
{
CommonPageHandlerFactoryajaxPageHandler=newCommonPageHandlerFactory();
IHttpHandlerhandler=ajaxPageHandler.GetHandler(context,requestType,url,pathTranslated);
Pagepage=handlerasPage;
if(page==null)
{
Executor.EndCurrentRequest(context,"处理程序类型必须是aspx页面!");
returnnull;
}
returnGetHandler(page,methodName,context);
}
catch
{
Executor.EndCurrentRequest(context,url+"不存在!");
returnnull;
}
}
if(url.EndsWith(".aspx",StringComparison.CurrentCultureIgnoreCase))
{
CommonPageHandlerFactoryorgPageHandler=newCommonPageHandlerFactory();
returnorgPageHandler.GetHandler(context,requestType,url,pathTranslated);
}
returnnull;
}
///<summary>
///获取自定义处理程序
///</summary>
///<paramname="page">处理页面</param>
///<paramname="methodName">处理方法</param>
///<paramname="context">当前请求</param>
privateIHttpHandlerGetHandler(Pagepage,stringmethodName,HttpContextcontext)
{
//根据Page和MethodName进行反射,获取标记属性(下面详细介绍)
CacheMethodInfomethodInfo=Executor.GetDelegateInfo(page,methodName);
if(methodInfo==null)
{
Executor.EndCurrentRequest(context,"找不到指定的Ajax方法!");
returnnull;
}
AjaxMethodAttributeattribute=methodInfo.AjaxMethodAttribute;
if(attribute.ServerCache>0)
{
//先查找缓存
objectdata=CacheHelper.TryGetCache(context);
if(data!=null)
{
Executor.EndCurrentRequest(context,data);
returnnull;
}
}
if(attribute.IsAsync)
{
//异步处理程序
returnASyncAjaxHandler.CreateHandler(page,methodInfo,attribute.SessionState);
}
returnSyncAjaxHandler.CreateHandler(page,methodInfo,attribute.SessionState);
}
}
上面的CacheMethodInfo是用于缓存调用方法的相关信息的,第一篇我们有提到过优化缓存的一些方法,其中就包括缓存+委托。但这里我们并不直接缓存方法的MethodInfo,因为缓存MethodInfo的话,需要通过Invoke去执行,这样的效率比较低。这里我缓存的是方法的委托,该委托的签名为:Func<object,object[],object>,该委托的返回值为object类型,表示可以返回任意的类型(我们可以在组件内部进行处理,例如如果是引用类型(非string),就将其序列化为json,但这里并没有实现)。该委托接收两个参数,第一个参数是方法所属的对象,如果是静态方法就是null;第二个参数是方法的参数,定义为object[]表示可以接收任意类型的参数。通过委托执行方法,与直接调用方法的效率差别就不是很大(对委托不熟悉的朋友可以参见:委托)。CacheMethodInfo的定义如下:
///<summary>
///缓存方法信息
///</summary>
sealedclassCacheMethodInfo
{
///<summary>
///方法名称
///</summary>
publicstringMethodName{get;set;}
///<summary>
///方法委托
///</summary>
publicFunc<object,object[],object>Func{get;set;}
///<summary>
///方法参数
///</summary>
publicParameterInfo[]Parameters{get;set;}
///<summary>
///Ajax标记属性
///</summary>
publicAjaxMethodAttributeAjaxMethodAttribute{get;set;}
}
核心方法
1.Eexcutor.GetDelegateInfo获取方法相关信息
该方法用于遍历页面类,获取所有AjaxMethodAttribute标记的方法信息,生成一个CacheMethodInfo对象,包括标记信息、方法名称、参数信息,以及最重要的方法委托。该对象会缓存在一个哈希表中,下次获取时,直接从内存获得。
///<summary>
///获取页面标记方法信息
///</summary>
///<paramname="page">页面对象</param>
///<paramname="methodName">方法名称</param>
internalstaticCacheMethodInfoGetDelegateInfo(Pagepage,stringmethodName)
{
if(page==null)
{
thrownewArgumentNullException("page");
}
Typetype=page.GetType();
//ajaxDelegateTable是一个Hashtable
Dictionary<string,CacheMethodInfo>dic=ajaxDelegateTable[type.AssemblyQualifiedName]asDictionary<string,CacheMethodInfo>;
if(dic==null)
{
dic=newDictionary<string,CacheMethodInfo>();
//遍历页面的所有MethodInfo
IEnumerable<CacheMethodInfo>infos=(frommintype.GetMethods(BindingFlags.Public|BindingFlags.Instance|BindingFlags.Static)
letca=m.GetCustomAttributes(typeof(AjaxMethodAttribute),false).FirstOrDefault()
whereca!=null
selectnewCacheMethodInfo
{
//方法标记属性
AjaxMethodAttribute=caasAjaxMethodAttribute,
//方法名称
MethodName=m.Name,
//方法参数信息
Parameters=m.GetParameters()
});
if(infos.IsNullOrEmpty())
{
returnnull;
}
for(inti=0,length=infos.Count();i<length;i++)
{
CacheMethodInfocacheMethodInfo=infos.ElementAt(i);
stringname=cacheMethodInfo.MethodName;
MethodInfomethodInfo=type.GetMethod(name);
if(!dic.ContainsKey(name))
{
//根据MethodInfo获取方法委托
cacheMethodInfo.Func=ReflectionUtil.GetMethodDelegate(methodInfo);
dic.Add(name,cacheMethodInfo);
}
}
ajaxDelegateTable[type.AssemblyQualifiedName]=dic;
}
CacheMethodInfocurrentMethodInfo=null;
dic.TryGetValue(methodName,outcurrentMethodInfo);
returncurrentMethodInfo;
}
获取方法的委托的是通过一个ReflectionUtil获得的,该类主要用来优化反射,它通过Expression,可以将MethodInfo编译成Func<object,object[],object>委托,为Type编译一个Func<object>委托,用于创建实例对象。
通过Expression优化反射
Expression(表达式树)允许我们将代码逻辑以表达式的形式存储在树状结构里,然后在运行时去动态解析,实现动态编辑和执行代码。熟悉ORM框架的朋友对Expression肯定很熟悉,因为大部分方法都有一个Expression<TDelegate>类型的参数。访问关系型数据库的本质还是sql语句,orm的工作就是为开发人员屏蔽这个过程,以面向对象的方式去读写数据库,而不是自己编写sql语句。例如,Users.Where(u=>u.Age>18)就可查询年龄大于18的用户。这里不对应用在orm的过程进行详解,下面我们介绍如何用Expression并利用它来生成委托。
.net定义了许多表达式类型,这些类型都派生自Expression,Expression是一个抽象类,而且是一个工厂类,所有类型的表达式都通过它来创建。如图:
先看一个1*2+2例子,我们用表达树来描述来描述它:
/*
*a*b+2
*/
/*
直接操作
inta=1,b=2;
intresult=a*2+2;
*/
/*
通过委托调用
Func<int,int,int>func=newFunc<int,int,int>((a,b)=>{returna*b+2;});
func(1,2);
*/
/*通过Expression调用*/
//定义两个参数
ParameterExpressionpe1=Expression.Parameter(typeof(int),"a");
ParameterExpressionpe2=Expression.Parameter(typeof(int),"b");
//定义一个常量
ConstantExpressionconstExpression=Expression.Constant(2);
//参数数组
ParameterExpression[]parametersExpression=newParameterExpression[]{pe1,pe2};
//一个乘法运算
BinaryExpressionmultiplyExpression=Expression.Multiply(pe1,pe2);
//一个加法运算
BinaryExpressionunaryExpression=Expression.Add(multiplyExpression,constExpression);
//将上面的表达式转换为一个委托表达式
LambdaExpressionlambdaExpression=Expression.Lambda<Func<int,int,int>>(unaryExpression,parametersExpression);
//将委托编译成可执行代码
Func<int,int,int>func=lambdaExpression.Compile()asFunc<int,int,int>;
Console.WriteLine(func(1,2));
可以看到我们最终将其编译为一个具体类型的委托了。下面看我们真正用到的方法是如何实现的,代码如下:
publicstaticFunc<object,object[],object>GetMethodDelegate(MethodInfomethodInfo)
{
if(methodInfo==null)
{
thrownewArgumentNullException("methodInfo");
}
//定义参数表达式,它表示委托的第一个参数
ParameterExpressioninstanceExp=Expression.Parameter(typeof(object),"instance");
//定义参数表达式,它表示委托的第二个参数
ParameterExpressionparamExp=Expression.Parameter(typeof(object[]),"parameters");
//获取方法的参数信息数组
ParameterInfo[]paramInfos=methodInfo.GetParameters();
//参数表达式集合
List<Expression>paramExpList=newList<Expression>();
intlength=paramInfos.Length;
for(inti=0;i<length;i++)
{
//获取paramExp参数数组的第i个元素
BinaryExpressionvalueObj=Expression.ArrayIndex(paramExp,Expression.Constant(i));
//将其转换为与参数类型一致的类型
UnaryExpressionvalueCast=Expression.Convert(valueObj,paramInfos[i].ParameterType);
//添加到参数集合
paramExpList.Add(valueCast);
}
//方法所属的实例的表达式,如果为静态则为null
UnaryExpressioninstanceCast=methodInfo.IsStatic?null:Expression.Convert(instanceExp,methodInfo.ReflectedType);
//表示调用方法的表达式
MethodCallExpressionmethodCall=Expression.Call(instanceCast,methodInfo,paramExpList);
//将表达式目录描述的lambda编译为可执行代码(委托)
if(methodCall.Type==typeof(void))
{
Expression<Action<object,object[]>>lambda=Expression.Lambda<Action<object,object[]>>(methodCall,instanceExp,paramExp);
Action<object,object[]>action=lambda.Compile();
return(instance,parameters)=>
{
action(instance,parameters);
returnnull;
};
}
else
{
UnaryExpressioncastMethodCall=Expression.Convert(methodCall,typeof(object));
Expression<Func<object,object[],object>>lambda=Expression.Lambda<Func<object,object[],object>>(castMethodCall,instanceExp,paramExp);
returnlambda.Compile();
}
}
具体代码都有注释解释,最终我们获得了一个Func<object,object[],object>类型的委托,它会作为CacheMethodInfo的属性进行缓存。有兴趣测试反射性能的朋友,也不妨去测试对比一下这几种方式执行的效率差别:1.直接执行方法2.Emit3.缓存+委托4.Delegate.DynamicInvoke。
2.Executor.Execute执行委托
在执行委托前,我们需要先从请求获取参数,映射到方法。参数可以是简单的类型,如stringTest(inti,intj);也可以是一个对象,如stringTest(Useruser);如果是stringTest(Useruser1,Useruser2)也行,提交参数时只需要加上user1或user2前缀即可,例如user1.Name,user2.Name。这里没有支持更多的匹配方式,像mvc,它还支持嵌套类型等等,这些可以自己去实现。如果参数是一个对象,我们可能需要为它的字段进行赋值,也可能为它的属性进行赋值。这里我们定义一个DataMember,用来表示字段或属性的父类。如:
internalabstractclassDataMember
{
publicabstractstringName{get;}
publicabstractTypeMemberType{get;}
publicabstractvoidSetValue(objectinstance,objectvalue);
publicabstractobjectGetValue(objectinstance);
}
接着定义属性类型PropertyMember和字段类型FieldMember,分别继承了DataMember。
PropertyMember定义:
internalclassPropertyMember:DataMember
{
privatePropertyInfoproperty;
publicPropertyMember(PropertyInfoproperty)
{
if(property==null)
{
thrownewArgumentNullException("property");
}
this.property=property;
}
publicoverridevoidSetValue(objectinstance,objectvalue)
{
if(instance==null)
{
thrownewArgumentNullException("instance");
}
this.property.SetValue(instance,value,null);
}
publicoverrideobjectGetValue(objectinstance)
{
if(instance==null)
{
thrownewArgumentNullException("instance");
}
returnthis.property.GetValue(instance,null);
}
publicoverridestringName
{
get{returnthis.property.Name;}
}
publicoverrideTypeMemberType
{
get{returnthis.property.PropertyType;}
}
}
FieldMember定义:
internalclassFieldMember:DataMember
{
privateFieldInfofield;
publicFieldMember(FieldInfofield)
{
if(field==null)
{
thrownewArgumentNullException("field");
}
this.field=field;
}
publicoverridevoidSetValue(objectinstance,objectvalue)
{
if(instance==null)
{
thrownewArgumentNullException("instance");
}
this.field.SetValue(instance,value);
}
publicoverrideobjectGetValue(objectinstance)
{
if(instance==null)
{
thrownewArgumentNullException("instance");
}
returnthis.field.GetValue(instance);
}
publicoverridestringName
{
get{returnthis.field.Name;}
}
publicoverrideTypeMemberType
{
get{returnthis.field.FieldType;}
}
}
定义一个DataMemberManager,用来遍历Type,获取所有字段和属性的,实现如下:
internalstaticclassDataMemberManager
{
///<summary>
///获取实例字段/属性集合
///</summary>
///<paramname="type">类型</param>
///<returns></returns>
publicstaticList<DataMember>GetDataMember(Typetype)
{
if(type==null)
{
thrownewArgumentNullException("type");
}
IEnumerable<PropertyMember>propertyMembers=frompropertyintype.GetProperties(BindingFlags.Instance|BindingFlags.Public)
selectnewPropertyMember(property);
IEnumerable<FieldMember>fieldMembers=fromfieldintype.GetFields(BindingFlags.Instance|BindingFlags.Public)
selectnewFieldMember(field);
List<DataMember>members=newList<DataMember>();
foreach(varpropertyinpropertyMembers)
{
members.Add(property);
}
foreach(varfieldinfieldMembers)
{
members.Add(field);
}
returnmembers;
}
}
在前面我们定义的Handler的ProcessRequest方法中,我们调用了Executor.Execute,该方法用于执行委托,实现如下:
///<summary>
///核心函数,执行Handler的方法
///</summary>
///<paramname="page">页面对象</param>
///<paramname="context">请求上下文</param>
///<paramname="cacheMethodInfo">缓存方法原数据</param>
internalstaticvoidExecute(Pagepage,HttpContextcontext,CacheMethodInfomethodInfo)
{
if(page==null)
{
thrownewArgumentNullException("page");
}
try
{
if(methodInfo!=null)
{
HttpRequestrequest=context.Request;
object[]parameters=GetParametersFromRequest(request,methodInfo.Parameters);
objectdata=methodInfo.Func(page,parameters);
intserverCache=methodInfo.AjaxMethodAttribute.ServerCache;
if(serverCache>0)
{
CacheHelper.Insert(context,methodInfo.AjaxMethodAttribute.ServerCache,data);
}
EndCurrentRequest(context,data,methodInfo.AjaxMethodAttribute.OutputCache);
}
else
{
EndCurrentRequest(context,"找不到合适的Ajax方法!");
}
}
catch(FormatException)
{
EndCurrentRequest(context,"调用方法匹配到无效的参数!");
}
catch(InvalidCastException)
{
EndCurrentRequest(context,"参数转换出错!");
}
catch(System.Threading.ThreadAbortException)
{
//donothing
}
catch(Exceptionex)
{
EndCurrentRequest(context,ex.Message);
}
}
CacheMethodInfo我们已经获得了,现在只要获得参数我们就可以执行方法。
GetParameterFromRequest用于从请求获取object[]参数数组。根据上面所说的,如果参数是一个简单类型,那么直接进行转换;如果是实例对象,那么我们先要创建new一个实例对象,然后为其字段或属性赋值。实现如下:
///<summary>
///从请求获取参参数
///</summary>
///<paramname="request">HttpRequest</param>
///<paramname="parameters">参数信息</param>
///<returns>参数数组</returns>
privatestaticobject[]GetParametersFromRequest(HttpRequestrequest,ParameterInfo[]parameters)
{
if(parameters.IsNullOrEmpty())
{
returnnull;
}
intlength=parameters.Length;
object[]realParameters=newobject[length];
for(inti=0;i<length;i++)
{
ParameterInfopi=parameters[i];
TypepiType=pi.ParameterType.GetRealType();
objectvalue=null;
if(piType.IsValueType())
{
//值类型
value=ModelUtil.GetValue(request,pi.Name,piType);
value=value??Activator.CreateInstance(piType);
}
elseif(piType.IsClass)
{
//引用类型
objectmodel=ModelUtil.CreateModel(piType);
ModelUtil.FillModelByRequest(request,pi.Name,piType,model);
value=model;
}
else
{
thrownewNotSupportedException(pi.Name+"参数不被支持");
}
realParameters[i]=value;
}
returnrealParameters;
}
ModelUtil会从HttpRequest获取参数,并进行类型转换处理:
internalstaticclassModelUtil
{
///<summary>
///缓存构造函数
///</summary>
privatestaticHashtableconstructorTable=Hashtable.Synchronized(newHashtable());
///<summary>
///根据名称从HttpRequest获取值
///</summary>
///<paramname="request">HttpRequest</param>
///<paramname="name">键名称</param>
///<paramname="type">参数类型</param>
///<returns></returns>
publicstaticobjectGetValue(HttpRequestrequest,stringname,Typetype)
{
string[]values=null;
if(string.Compare(request.RequestType,"POST",true)==0)
{
values=request.Form.GetValues(name);
}
else
{
values=request.QueryString.GetValues(name);
}
if(values.IsNullOrEmpty())
{
returnnull;
}
stringdata=values.Length==1?values[0]:string.Join(",",values);
returnConvert.ChangeType(data,type);
}
///<summary>
///创建实例对象
///</summary>
///<paramname="type">实例类型</param>
///<returns></returns>
publicstaticobjectCreateModel(Typetype)
{
if(type==null)
{
thrownewArgumentNullException("type");
}
Func<object>func=constructorTable[type.AssemblyQualifiedName]asFunc<object>;
if(func==null)
{
func=ReflectionUtil.GetConstructorDelegate(type);
constructorTable[type.AssemblyQualifiedName]=func;
}
if(func!=null)
{
returnfunc();
}
returnnull;
}
///<summary>
///填充模型
///</summary>
///<paramname="request">HttpRequest</param>
///<paramname="name">键名称</param>
///<paramname="prefix">参数类型</param>
///<parparamname="model">实例对象</parparam>
publicstaticvoidFillModelByRequest(HttpRequestrequest,stringname,Typetype,objectmodel)
{
if(model==null)
{
return;
}
IEnumerable<DataMember>members=DataMemberManager.GetDataMember(type);
if(members.IsNullOrEmpty())
{
return;
}
objectvalue=null;
foreach(DataMembermemberinmembers)
{
value=GetValue(request,string.Format("{0}.{1}",name,member.Name),member.MemberType);
value=value??GetValue(request,member.Name,member.MemberType);
member.SetValue(model,value);
}
}
}
如果是引用类型,需要通过构造函数创建对象,像前面用于,这里我们也用Expression来构建一个Func<object>类型的委托来优化,它调用了ReflectionUtil.GetConstructorDelegate方法。实现如下:
///<summary>
///获取构造函数委托
///</summary>
///<paramname="type">实例类型</param>
///<returns></returns>
publicstaticFunc<object>GetConstructorDelegate(Typetype)
{
if(type==null)
{
thrownewArgumentNullException("type");
}
ConstructorInfoci=type.GetConstructor(Type.EmptyTypes);
if(ci==null)
{
thrownewMissingMemberException("类型必须有一个无参public构造函数!");
}
NewExpressionnewExp=Expression.New(type);
Expression<Func<object>>lambda=Expression.Lambda<Func<object>>(newExp);
returnlambda.Compile();
}
最后再输出结果时,如果是Get请求,并且需要缓存,我们还需要设置一下Response.Cache。如下:
privatestaticvoidEndRequest(HttpContextcontext,objectdata,intoutPutCache,ContentTypecontentType)
{
HttpResponseresponse=context.Response;
if(outPutCache!=0)
{
if(string.Compare(context.Request.HttpMethod,"GET",true)==0)
{
if(outPutCache>0)
{
response.Cache.SetCacheability(HttpCacheability.Public);
response.Cache.SetMaxAge(newTimeSpan(0,0,outPutCache));
response.Cache.SetExpires(DateTime.Now.AddSeconds(outPutCache));
}
else
{
response.Cache.SetCacheability(HttpCacheability.NoCache);
response.Cache.SetNoStore();
}
}
}
response.ContentType=GetContentType(contentType);
response.ContentEncoding=System.Text.Encoding.UTF8;
if(data!=null)
{
response.Write(data);
}
response.End();
}
总结
现在不管我们前台用什么脚本库,只要按照约定就可以调用标记方法。上面已经介绍了组件的核心部分,您也可以按照自己的想法进行扩展,也欢迎共同学习交流。