怎么样编写轻量ajax组件
1个回答
展开全部
一、ajaxpro的使用
我们先来看这个组件如何使用。
1. 注册AjaxHandlerFactory
在web.config里进行如下配置:
<httpHandlers>
<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/>
</httpHandlers>
简单的说,请求的url符合 ajaxpro/*.ashx 格式的,都会被AjaxHandlerFactory处理,这是一个实现IHandlerFactory接口的工厂类,用来获取IHandler处理程序。其中type的格式是:"名称控件.类名称,程序集名称"。
2. 在页面类Page_Load事件进行注册
protected void Page_Load(object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));
}
3.用AjaxMethod标记方法
[AjaxMethod]
public List<string> GetList(string input1,string input2)
{
return new List<string> { input1, input2 };
}
方法的返回值可以是简单的类型,也可以是复杂的类型;例如集合类型在前台获得就是一个数组。
4.前台调用
后台的配置和使用都非常简单,接下来我们看前台如何发起请求。
function GetList() {
//var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value;
//console.log(result);
AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) {
console.log(result);
});
}
这里AjaxProNamespace 是页面类所在的名称空间,AjaxProPage 就是页面类的名称,GetList是标记的方法。为什么可以这样写呢?前面说到,ajaxpro会在前台注册脚本,它会根据我们页面对象的相关信息生成如下脚本,所以我们才可以这样调用,而完全不用自己写js或者用jquery库的方法。
if(typeof AjaxProNamespace == "undefined") AjaxProNamespace={};
if(typeof AjaxProNamespace.AjaxProPage_class == "undefined") AjaxProNamespace.AjaxProPage_class={};
AjaxProNamespace.AjaxProPage_class = function() {};
Object.extend(AjaxProNamespace.AjaxProPage_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
GetList: function(input1, input2) {
return this.invoke("GetList", {"input1":input1, "input2":input2}, this.GetList.getArguments().slice(2));
},
url: '/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx'
}));
AjaxProNamespace.AjaxProPage = new AjaxProNamespace.AjaxProPage_class();
GetList的参数对应后台方法的参数,类型必须可以转换,否则调用会失败。最后一个参数为回调函数,回调函数的参数是对返回结果进行封装的对象,其value属性就是执行成功返回的值,如上面返回的就是一个数组对象。其error包括了失败的信息。
注意,上面注释掉的部分是同步请求的做法,这往往不是我们想要的,我曾经就见过有人这样错误的使用。
二、ajaxpro处理请求原理
这里主要关注组件处理ajax请求的过程,其它辅助功能不做介绍。
1.生成辅助脚本
在Page_Load事件里我们调用了AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage)); 用来注册所需要的脚本。我们注意到在前台页面引入了如下脚本:
也就是每个页面都会都会发起这几个请求。这几个都是.ashx结尾的文件,但实际里面都是js代码;这些js有的是作为资源嵌套在dll内部,有的是自动生成的,主要是封装了ajax请求相关方法,以及让我们可以用:名称空间.页面类名称.标记方法名称 这样去调用方法。为什么要用.ashx而不是用.js呢?因为作为组件内部的资源文件,外部无法直接请求.js文件,而.ashx可以被拦截,然后用Response.Write将内容输出。
如果每次都生成和发送这些脚本的效率是很低的,ajaxpro内部的处理是判断请求头的If-None-Math和If-Modified-Since,如果两个都和缓存的一样,就返回一个304状态码。所以,客户端只有首次请求服务端会返回文件的内容,后续的都只返回304表示使用本地缓存。我们刷新页面可以验证这个过程:
2. 拦截请求
public interface IHttpHandlerFactory
{
IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
void ReleaseHandler(IHttpHandler handler);
}
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string filename = Path.GetFileNameWithoutExtension(context.Request.Path);
Type t = null;
Exception typeException = null;
bool isInTypesList = false;
switch (requestType)
{
//Get请求,获取前面的那4个脚本
case "GET":
switch (filename.ToLower())
{
case "prototype":
return new EmbeddedJavaScriptHandler("prototype");
case "core":
return new EmbeddedJavaScriptHandler("core");
case "ms":
return new EmbeddedJavaScriptHandler("ms");
case "prototype-core":
case "core-prototype":
return new EmbeddedJavaScriptHandler("prototype,core");
case "converter":
return new ConverterJavaScriptHandler();
default:
return new TypeJavaScriptHandler(t);
}
case "POST":
IAjaxProcessor[] p = new IAjaxProcessor[2];
p[0] = new XmlHttpRequestProcessor(context, t);
p[1] = new IFrameProcessor(context, t);
for (int i = 0; i < p.Length; i++)
{
if (p[i].CanHandleRequest)
{
//获取标记方法的AjaxMethod属性
AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])p[i].AjaxMethod.GetCustomAttributes(typeof(AjaxMethodAttribute), true);
bool useAsync = false;
HttpSessionStateRequirement sessionReq = HttpSessionStateRequirement.ReadWrite;
if (ma.Length > 0)
{
useAsync = ma[0].UseAsyncProcessing;
if (ma[0].RequireSessionState != HttpSessionStateRequirement.UseDefault)
sessionReq = ma[0].RequireSessionState;
}
//6种Handler,根据是否异步,session状态返回指定的Handler
switch (sessionReq)
{
case HttpSessionStateRequirement.Read:
if (!useAsync)
return new AjaxSyncHttpHandlerSessionReadOnly(p[i]);
else
return new AjaxAsyncHttpHandlerSessionReadOnly(p[i]);
case HttpSessionStateRequirement.ReadWrite:
if (!useAsync)
return new AjaxSyncHttpHandlerSession(p[i]);
else
return new AjaxAsyncHttpHandlerSession(p[i]);
case HttpSessionStateRequirement.None:
if (!useAsync)
return new AjaxSyncHttpHandler(p[i]);
else
return new AjaxAsyncHttpHandler(p[i]);
default:
if (!useAsync)
return new AjaxSyncHttpHandlerSession(p[i]);
else
return new AjaxAsyncHttpHandlerSession(p[i]);
}
}
}
break;
}
return null;
}
3. 反射执行方法
internal void Run()
{
try
{
//设置输出结果不缓存(这不一定是我们想要的)
p.Context.Response.Expires = 0;
p.Context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
p.Context.Response.ContentType = p.ContentType;
p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
//验证ajax请求
if (!p.IsValidAjaxToken())
{
p.SerializeObject(new System.Security.SecurityException("The AjaxPro-Token is not valid."));
return;
}
//方法参数对象数组
object[] po = null;
//请求处理结果
object res = null;
try
{
//获取参数
po = p.RetreiveParameters();
}
catch (Exception ex){}
//获取缓存的Key
string cacheKey = p.Type.FullName + "|" + p.GetType().Name + "|" + p.AjaxMethod.Name + "|" + p.GetHashCode();
if (p.Context.Cache[cacheKey] != null)
{
//如果缓存存在,则直接使用缓存
p.Context.Response.AddHeader("X-" + Constant.AjaxID + "-Cache", "server");
p.Context.Response.Write(p.Context.Cache[cacheKey]);
return;
}
try
{
if (p.AjaxMethod.IsStatic)
{
//使用反射调用静态方法
try
{
res = p.Type.InvokeMember(
p.AjaxMethod.Name,
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.InvokeMethod,
null, null, po);
}
catch (Exception ex){}
}
else
{
try
{
//创建实例对象,反射调用实例方法
object c = (object)Activator.CreateInstance(p.Type, new object[] { });
if (c != null)
{
res = p.AjaxMethod.Invoke(c, po);
}
}
catch (Exception ex){}
}
}
catch (Exception ex){}
try
{
//判断结果是不是xml,如是设置ContentType
if (res != null && res.GetType() == typeof(System.Xml.XmlDocument))
{
p.Context.Response.ContentType = "text/xml";
p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
((System.Xml.XmlDocument)res).Save(p.Context.Response.OutputStream);
return;
}
string result = null; ;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
result = p.SerializeObject(res);
}
catch (Exception ex){}
//如果需要缓存,则将结果写入缓存
if (p.ServerCacheAttributes.Length > 0)
{
if (p.ServerCacheAttributes[0].IsCacheEnabled)
{
p.Context.Cache.Add(cacheKey, result, null, DateTime.Now.Add(p.ServerCacheAttributes[0].CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
}
catch (Exception ex){}
}
catch (Exception ex){}
}
我们先来看这个组件如何使用。
1. 注册AjaxHandlerFactory
在web.config里进行如下配置:
<httpHandlers>
<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/>
</httpHandlers>
简单的说,请求的url符合 ajaxpro/*.ashx 格式的,都会被AjaxHandlerFactory处理,这是一个实现IHandlerFactory接口的工厂类,用来获取IHandler处理程序。其中type的格式是:"名称控件.类名称,程序集名称"。
2. 在页面类Page_Load事件进行注册
protected void Page_Load(object sender, EventArgs e)
{
AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage));
}
3.用AjaxMethod标记方法
[AjaxMethod]
public List<string> GetList(string input1,string input2)
{
return new List<string> { input1, input2 };
}
方法的返回值可以是简单的类型,也可以是复杂的类型;例如集合类型在前台获得就是一个数组。
4.前台调用
后台的配置和使用都非常简单,接下来我们看前台如何发起请求。
function GetList() {
//var result = AjaxProNamespace.AjaxProPage.GetList("a", "b").value;
//console.log(result);
AjaxProNamespace.AjaxProPage.GetList("a", "b", function (result) {
console.log(result);
});
}
这里AjaxProNamespace 是页面类所在的名称空间,AjaxProPage 就是页面类的名称,GetList是标记的方法。为什么可以这样写呢?前面说到,ajaxpro会在前台注册脚本,它会根据我们页面对象的相关信息生成如下脚本,所以我们才可以这样调用,而完全不用自己写js或者用jquery库的方法。
if(typeof AjaxProNamespace == "undefined") AjaxProNamespace={};
if(typeof AjaxProNamespace.AjaxProPage_class == "undefined") AjaxProNamespace.AjaxProPage_class={};
AjaxProNamespace.AjaxProPage_class = function() {};
Object.extend(AjaxProNamespace.AjaxProPage_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
GetList: function(input1, input2) {
return this.invoke("GetList", {"input1":input1, "input2":input2}, this.GetList.getArguments().slice(2));
},
url: '/ajaxpro/AjaxProNamespace.AjaxProPage,TestAjaxProSourceCode.ashx'
}));
AjaxProNamespace.AjaxProPage = new AjaxProNamespace.AjaxProPage_class();
GetList的参数对应后台方法的参数,类型必须可以转换,否则调用会失败。最后一个参数为回调函数,回调函数的参数是对返回结果进行封装的对象,其value属性就是执行成功返回的值,如上面返回的就是一个数组对象。其error包括了失败的信息。
注意,上面注释掉的部分是同步请求的做法,这往往不是我们想要的,我曾经就见过有人这样错误的使用。
二、ajaxpro处理请求原理
这里主要关注组件处理ajax请求的过程,其它辅助功能不做介绍。
1.生成辅助脚本
在Page_Load事件里我们调用了AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProPage)); 用来注册所需要的脚本。我们注意到在前台页面引入了如下脚本:
也就是每个页面都会都会发起这几个请求。这几个都是.ashx结尾的文件,但实际里面都是js代码;这些js有的是作为资源嵌套在dll内部,有的是自动生成的,主要是封装了ajax请求相关方法,以及让我们可以用:名称空间.页面类名称.标记方法名称 这样去调用方法。为什么要用.ashx而不是用.js呢?因为作为组件内部的资源文件,外部无法直接请求.js文件,而.ashx可以被拦截,然后用Response.Write将内容输出。
如果每次都生成和发送这些脚本的效率是很低的,ajaxpro内部的处理是判断请求头的If-None-Math和If-Modified-Since,如果两个都和缓存的一样,就返回一个304状态码。所以,客户端只有首次请求服务端会返回文件的内容,后续的都只返回304表示使用本地缓存。我们刷新页面可以验证这个过程:
2. 拦截请求
public interface IHttpHandlerFactory
{
IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
void ReleaseHandler(IHttpHandler handler);
}
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string filename = Path.GetFileNameWithoutExtension(context.Request.Path);
Type t = null;
Exception typeException = null;
bool isInTypesList = false;
switch (requestType)
{
//Get请求,获取前面的那4个脚本
case "GET":
switch (filename.ToLower())
{
case "prototype":
return new EmbeddedJavaScriptHandler("prototype");
case "core":
return new EmbeddedJavaScriptHandler("core");
case "ms":
return new EmbeddedJavaScriptHandler("ms");
case "prototype-core":
case "core-prototype":
return new EmbeddedJavaScriptHandler("prototype,core");
case "converter":
return new ConverterJavaScriptHandler();
default:
return new TypeJavaScriptHandler(t);
}
case "POST":
IAjaxProcessor[] p = new IAjaxProcessor[2];
p[0] = new XmlHttpRequestProcessor(context, t);
p[1] = new IFrameProcessor(context, t);
for (int i = 0; i < p.Length; i++)
{
if (p[i].CanHandleRequest)
{
//获取标记方法的AjaxMethod属性
AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])p[i].AjaxMethod.GetCustomAttributes(typeof(AjaxMethodAttribute), true);
bool useAsync = false;
HttpSessionStateRequirement sessionReq = HttpSessionStateRequirement.ReadWrite;
if (ma.Length > 0)
{
useAsync = ma[0].UseAsyncProcessing;
if (ma[0].RequireSessionState != HttpSessionStateRequirement.UseDefault)
sessionReq = ma[0].RequireSessionState;
}
//6种Handler,根据是否异步,session状态返回指定的Handler
switch (sessionReq)
{
case HttpSessionStateRequirement.Read:
if (!useAsync)
return new AjaxSyncHttpHandlerSessionReadOnly(p[i]);
else
return new AjaxAsyncHttpHandlerSessionReadOnly(p[i]);
case HttpSessionStateRequirement.ReadWrite:
if (!useAsync)
return new AjaxSyncHttpHandlerSession(p[i]);
else
return new AjaxAsyncHttpHandlerSession(p[i]);
case HttpSessionStateRequirement.None:
if (!useAsync)
return new AjaxSyncHttpHandler(p[i]);
else
return new AjaxAsyncHttpHandler(p[i]);
default:
if (!useAsync)
return new AjaxSyncHttpHandlerSession(p[i]);
else
return new AjaxAsyncHttpHandlerSession(p[i]);
}
}
}
break;
}
return null;
}
3. 反射执行方法
internal void Run()
{
try
{
//设置输出结果不缓存(这不一定是我们想要的)
p.Context.Response.Expires = 0;
p.Context.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
p.Context.Response.ContentType = p.ContentType;
p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
//验证ajax请求
if (!p.IsValidAjaxToken())
{
p.SerializeObject(new System.Security.SecurityException("The AjaxPro-Token is not valid."));
return;
}
//方法参数对象数组
object[] po = null;
//请求处理结果
object res = null;
try
{
//获取参数
po = p.RetreiveParameters();
}
catch (Exception ex){}
//获取缓存的Key
string cacheKey = p.Type.FullName + "|" + p.GetType().Name + "|" + p.AjaxMethod.Name + "|" + p.GetHashCode();
if (p.Context.Cache[cacheKey] != null)
{
//如果缓存存在,则直接使用缓存
p.Context.Response.AddHeader("X-" + Constant.AjaxID + "-Cache", "server");
p.Context.Response.Write(p.Context.Cache[cacheKey]);
return;
}
try
{
if (p.AjaxMethod.IsStatic)
{
//使用反射调用静态方法
try
{
res = p.Type.InvokeMember(
p.AjaxMethod.Name,
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.InvokeMethod,
null, null, po);
}
catch (Exception ex){}
}
else
{
try
{
//创建实例对象,反射调用实例方法
object c = (object)Activator.CreateInstance(p.Type, new object[] { });
if (c != null)
{
res = p.AjaxMethod.Invoke(c, po);
}
}
catch (Exception ex){}
}
}
catch (Exception ex){}
try
{
//判断结果是不是xml,如是设置ContentType
if (res != null && res.GetType() == typeof(System.Xml.XmlDocument))
{
p.Context.Response.ContentType = "text/xml";
p.Context.Response.ContentEncoding = System.Text.Encoding.UTF8;
((System.Xml.XmlDocument)res).Save(p.Context.Response.OutputStream);
return;
}
string result = null; ;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
result = p.SerializeObject(res);
}
catch (Exception ex){}
//如果需要缓存,则将结果写入缓存
if (p.ServerCacheAttributes.Length > 0)
{
if (p.ServerCacheAttributes[0].IsCacheEnabled)
{
p.Context.Cache.Add(cacheKey, result, null, DateTime.Now.Add(p.ServerCacheAttributes[0].CacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
}
catch (Exception ex){}
}
catch (Exception ex){}
}
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询