这次给大家带来编写轻量级ajax组件(图文详解),编写轻量级ajax组件的注意事项有哪些,下面就是实战案例,一起来看一下。
通过之前的介绍,我们知道要执行页面对象的方法,核心就是反射,是从请求获取参数并执行指定方法的过程。实际上这和asp.net mvc框架的核心思想很类似,它会解析url,从中获取controller和action名称,然后激活controller对象,从请求获取action参数并执action。在web form平台上,我们把方法写在.aspx.cs中,要实现的就是在页面对象还未生成的情况下,执行指定的方法,然后返回结果。
我们先看实现后几个调用例子,这些功能也可以组合使用:
[AjaxMethod] public void Test1(int index) { //简单调用 } [AjaxMethod] public string Test2(Test test) { return "参数为一个Test实例"; } [AjaxMethod(OutputCache = 20)] public string Test3(int index) { return "输出结果缓存20秒"; } [AjaxMethod(ServerCache = 20)] public string Test4() { return "在服务端缓存20秒"; } [AjaxMethod(SessionState=SessionState.None)] public void Test5() { //Session未被加载 } [AjaxMethod(SessionState = SessionState.ReadOnly)] public void Test6() { //Session只能读不能写 } [AjaxMethod(SessionState = SessionState.ReadWrite)] public void Test7() { //Session可以读写 } [AjaxMethod(IsAsync = true)] public void Test8() { //异步调用 }
前面我们已经熟悉基本的执行流程,现在直接进入主题。
Ajax约定
通常现在主流浏览器在使用ajax发送异步请求时,请求头都会带上一个:X-Requested-With:XMLHttpRequest 的标记。我们也可以直接通过这个标记来判断是不是ajax请求,不过项目中可能有用其它的组件,为了不相互影响,我们加入一个自定义的请求头。这里为:
internal static class AjaxConfig { /// <summary> /// 请求头Ajax标记键 /// </summary> public const string Key = "AjaxFlag"; /// <summary> /// 请求头Ajax标记值 /// </summary> public const string Value = "XHR"; /// <summary> /// 请求头Ajax方法标记 /// </summary> public const string MethodName = ""; }
意思是如果http 的请求头包含一个 AjaxFlag : XHR,就是我们要处理的。另外http header的MethodName就表示我们要执行的方法的名称。
AjaxMethodAttribute标记属性
标记属性是给反射用的,在这里定义我们需要的一些功能。我们希望有:
1. 可以配置Session状态
2. 支持异步Handler
3. 支持Get缓存
4. 支持服务端缓存
定义如下,用AttributeUsag标记该标记只能用于方法上。
/// <summary> /// ajax方法标记属性 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class AjaxMethodAttribute : Attribute { public AjaxMethodAttribute() { } private SessionState _sessionState = SessionState.None; private int _outputCache = 0; private int _serverCache = 0; private ContentType _contentType = ContentType.Plain; private bool _isUseAsync = false; /// <summary> /// session状态 /// </summary> public SessionState SessionState { get { return _sessionState; } set { _sessionState = value; } } /// <summary> /// 客户端缓存时间,以秒为单位。该标记只对get请求有效 /// </summary> public int OutputCache { get { return _outputCache; } set { _outputCache = value; } } /// <summary> /// 服务端缓存时间,以秒为单位 /// </summary> public int ServerCache { get { return _serverCache; } set { _serverCache = value; } } /// <summary> /// 输出类型(默认为text/plain) /// </summary> public ContentType ContentType { get { return _contentType; } set { _contentType = value; } } /// <summary> /// 使用启用异步处理 /// </summary> public bool IsAsync { get { return _isUseAsync; } set { _isUseAsync = value; } } } /// <summary> /// Session状态 /// </summary> public enum SessionState { None, ReadOnly, ReadWrite } /// <summary> /// 输出内容类型 /// </summary> public enum ContentType { Plain, Html, XML, Javascript, JSON }
各种处理程序和AjaxHandlerFactory
按照上一篇的说法,具体的Handler主要分为两类,异步和非异步;这两类下,对于Session的状态又有3三种,不支持、只支持读(实现IReadOnlySessionState接口)、支持读写(实现IRequiresSessionState接口)。IReadOnlySessionState和IRequiresSessionState都只是标记接口(无任何方法,其实应该用标记属性实现比较合理)。异步的Handler需要实现IHttpAsyncHandler接口,该接口又实现了IHttpHandler。Handler的ProcessRequest方法(或BeginProcessRequest)就是我们要执行方法的地方。定义如下:
非异步状态的Handler:
//不支持Session internal class SyncAjaxHandler : IHttpHandler { private Page _page; private CacheMethodInfo _cacheMethodInfo; internal SyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo) { _page = page; _cacheMethodInfo = cacheMethodInfo; } public void ProcessRequest(HttpContext context) { //执行方法(下面详细介绍) Executor.Execute(_page, context, _cacheMethodInfo); } public bool IsReusable { get { return false; } } public static SyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state) { switch (state) { case SessionState.ReadOnly: return new SyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo); case SessionState.ReadWrite: return new SyncAjaxSessionHandler(page, cacheMethodInfo); default: return new SyncAjaxHandler(page, cacheMethodInfo); } } } //支持只读Session internal class SyncAjaxSessionReadOnlyHandler : SyncAjaxHandler, IReadOnlySessionState { internal SyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo) : base(page, cacheMethodInfo) { } } //支持读写Session internal class SyncAjaxSessionHandler : SyncAjaxHandler, IRequiresSessionState { internal SyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo) : base(page, cacheMethodInfo) { } }
异步状态的Handler:
//不支持Session internal class ASyncAjaxHandler : IHttpAsyncHandler, IHttpHandler { private Page _page; private CacheMethodInfo _cacheMethodInfo; internal ASyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo) { _page = page; _cacheMethodInfo = cacheMethodInfo; } public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { //执行方法(下面详细介绍) Action<Page, HttpContext, CacheMethodInfo> action = new Action<Page, HttpContext, CacheMethodInfo>(Executor.Execute); IAsyncResult result = action.BeginInvoke(_page, context, _cacheMethodInfo, cb, action); return result; } public void EndProcessRequest(IAsyncResult result) { Action<Page, HttpContext, CacheMethodInfo> action = result.AsyncState as Action<Page, HttpContext, CacheMethodInfo>; action.EndInvoke(result); } public void ProcessRequest(HttpContext context) { throw new NotImplementedException(); } public bool IsReusable { get { return false; } } public static ASyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state) { switch (state) { case SessionState.ReadOnly: return new ASyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo); case SessionState.ReadWrite: return new ASyncAjaxSessionHandler(page, cacheMethodInfo); default: return new ASyncAjaxHandler(page, cacheMethodInfo); } } } //支持只读Session internal class ASyncAjaxSessionReadOnlyHandler : ASyncAjaxHandler, IReadOnlySessionState { internal ASyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo) : base(page, cacheMethodInfo) { } } //支持读写Session internal class ASyncAjaxSessionHandler : ASyncAjaxHandler, IRequiresSessionState { internal ASyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo) : 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的构造函数是protected internal类型的,我们无法直接new一个,所以需要通过一个CommonPageHandlerFactory继承它来实现。
通过PageHandlerFactory获得Page后,结合方法名称,我们就可以反射获取AjaxMethodAttribute标记属性了。然后根据它的相关属性生成具体的Handler。具体代码如下:
internal class CommonPageHandlerFactory : PageHandlerFactory { } internal class AjaxHandlerFactory : IHttpHandlerFactory { public void ReleaseHandler(IHttpHandler handler) { } public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) { HttpRequest request = context.Request; if (string.Compare(request.Headers[AjaxConfig.Key], AjaxConfig.Value, true) == 0) { //检查函数标记 string methodName = request.Headers[AjaxConfig.MethodName]; if (methodName.IsNullOrEmpty()) { Executor.EndCurrentRequest(context, "方法名称未正确指定!"); return null; } try { CommonPageHandlerFactory ajaxPageHandler = new CommonPageHandlerFactory(); IHttpHandler handler = ajaxPageHandler.GetHandler(context, requestType, url, pathTranslated); Page page = handler as Page; if (page == null) { Executor.EndCurrentRequest(context, "处理程序类型必须是aspx页面!"); return null; } return GetHandler(page, methodName, context); } catch { Executor.EndCurrentRequest(context, url + " 不存在!"); return null; } } if (url.EndsWith(".aspx", StringComparison.CurrentCultureIgnoreCase)) { CommonPageHandlerFactory orgPageHandler = new CommonPageHandlerFactory(); return orgPageHandler.GetHandler(context, requestType, url, pathTranslated); } return null; } /// <summary> /// 获取自定义处理程序 /// </summary> /// <param name="page">处理页面</param> /// <param name="methodName">处理方法</param> /// <param name="context">当前请求</param> private IHttpHandler GetHandler(Page page, string methodName, HttpContext context) { //根据Page和MethodName进行反射,获取标记属性(下面详细介绍) CacheMethodInfo methodInfo = Executor.GetDelegateInfo(page, methodName); if (methodInfo == null) { Executor.EndCurrentRequest(context, "找不到指定的Ajax方法!"); return null; } AjaxMethodAttribute attribute = methodInfo.AjaxMethodAttribute; if (attribute.ServerCache > 0) { //先查找缓存 object data = CacheHelper.TryGetCache(context); if (data != null) { Executor.EndCurrentRequest(context, data); return null; } } if (attribute.IsAsync) { //异步处理程序 return ASyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState); } return SyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState); } }
上面的CacheMethodInfo是用于缓存调用方法的相关信息的,第一篇我们有提到过优化缓存的一些方法,其中就包括缓存+委托。但这里我们并不直接缓存方法的MethodInfo,因为缓存MethodInfo的话,需要通过Invoke去执行,这样的效率比较低。这里我缓存的是方法的委托,该委托的签名为:Func
/// <summary> /// 缓存方法信息 /// </summary> sealed class CacheMethodInfo { /// <summary> /// 方法名称 /// </summary> public string MethodName { get; set; } /// <summary> /// 方法委托 /// </summary> public Func<object, object[], object> Func { get; set; } /// <summary> /// 方法参数 /// </summary> public ParameterInfo[] Parameters { get; set; } /// <summary> /// Ajax标记属性 /// </summary> public AjaxMethodAttribute AjaxMethodAttribute { get; set; } }
核心方法
1. Eexcutor.GetDelegateInfo 获取方法相关信息
该方法用于遍历页面类,获取所有AjaxMethodAttribute标记的方法信息,生成一个CacheMethodInfo对象,包括标记信息、方法名称、参数信息,以及最重要的方法委托。该对象会缓存在一个哈希表中,下次获取时,直接从内存获得。
/// <summary> /// 获取页面标记方法信息 /// </summary> /// <param name="page">页面对象</param> /// <param name="methodName">方法名称</param> internal static CacheMethodInfo GetDelegateInfo(Page page, string methodName) { if (page == null) { throw new ArgumentNullException("page"); } Type type = page.GetType(); //ajaxDelegateTable是一个Hashtable Dictionary<string, CacheMethodInfo> dic = ajaxDelegateTable[type.AssemblyQualifiedName] as Dictionary<string, CacheMethodInfo>; if (dic == null) { dic = new Dictionary<string, CacheMethodInfo>(); //遍历页面的所有MethodInfo IEnumerable<CacheMethodInfo> infos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) let ca = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false).FirstOrDefault() where ca != null select new CacheMethodInfo { //方法标记属性 AjaxMethodAttribute = ca as AjaxMethodAttribute, //方法名称 MethodName = m.Name, //方法参数信息 Parameters = m.GetParameters() }); if (infos.IsNullOrEmpty()) { return null; } for (int i = 0, length = infos.Count(); i < length; i++) { CacheMethodInfo cacheMethodInfo = infos.ElementAt(i); string name = cacheMethodInfo.MethodName; MethodInfo methodInfo = type.GetMethod(name); if (!dic.ContainsKey(name)) { //根据MethodInfo获取方法委托 cacheMethodInfo.Func = ReflectionUtil.GetMethodDelegate(methodInfo); dic.Add(name, cacheMethodInfo); } } ajaxDelegateTable[type.AssemblyQualifiedName] = dic; } CacheMethodInfo currentMethodInfo = null; dic.TryGetValue(methodName, out currentMethodInfo); return currentMethodInfo; }
获取方法的委托的是通过一个ReflectionUtil获得的,该类主要用来优化反射,它通过Expression,可以将MethodInfo编译成Func

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Dreamweaver Mac版
视觉化网页开发工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Dreamweaver CS6
视觉化网页开发工具