我在前面的博客【用Asp.net写自己的服务框架】中 发布了一个用ASP.NET写的服务框架那个框架我目前仍在使用中。近来由于时常也会有人问我一些关于ASP.NET MVC的话题 因此就想再写个自己的MVC框架出来一方面可以留给自己使用另外也可以谈谈MVC尤其可以展示一下在ASP.NET下写框架的乐趣。我之所以将写框架看成是件有乐趣的事是因为在写框架的过程中会接触许多的技术细节。比如1. 为了支持Session需要了解管线过程以及支持Session工作的原理。2. 在HttpHandler的映射过程中HttpHandlerFactory的使用会让设计很灵活。3. 反射可以让我们轻松地根据一个URL去寻找匹配的Action以及为Action准备传入参数。4. 为了便于测试Action如何有效的封装框架的功能这里有许多ASP.NET的技术细节。5. 如何设计让框架更灵活更强大。在开始今天的博客之前我想有必要说说我的框架的规模如果说ASP.NET WebForm是个复杂框架ASP.NET MVC是个轻量级框架的话那么我的MVC框架将只是个微量级的框架。但这个微量级的框架却可以提供许多实用的功能因为我没有引入一些与MVC无关的东西而且完全遵守MVC的思想而设计。由于我的框架规模实在太小因此有些地方的确是不够完善但我认为在大多数情况下是够用的。回到顶部ASP.NET程序的几种开发方式时常见到有人在问到底选择WebForm还是MVC 其实我认为最好是二个都选吧。然后你去发现它们的优点与缺点最后当你觉得它们都不爽时还是写自己的框架吧。我来说说我这样选择的理由任何东西都有它们的优点这很正常所以二个都选就能发现更多的优点 发现优点的过程其实也是自己进步的过程。当然它们也有缺点发现那些缺点你自然会想办法去避开 其实这也是个进步的过程。因此在你吸收优点以及避开缺点的过程中会感觉它们不再完美因为自己在进步 再到后来你会怎么选择我就不知道了那就是你自己的事了。 而我选择了另一条路写自己的ASP.NET MVC框架。在比较各类框架之前我想有必要先来总结一下现在能用ASP.NET开发哪些类型的网站 由于ASP.NET与WCF这类纯服务性质的框架不同我们主要还是用它来开发一些可与用户交互的界面程序。 因此今天的分类将重要讨论这些界面UI的开发方式。我认为目前的ASP.NET能支持开发三种类型的网站1.以服务端为中心的网站所有页面的生成以及交互的逻辑全部服务端来完成服务端甚至能生成必要的JS代码。2.门户类网站服务端只负责页面的第一次呈现用户的交互以及表单的提交全部采用AJAX的方式完成。3.纯AJAX网站服务端基本上不参与UI的处理只负责处理数据UI在客户端由JavaScript来生成并控制提交。【以服务端为中心的网站】这类网站有个非常明显的特点至少在开发上表现地非常明显 服务端要做的事情很多HTML的生成, 简单的JS交互客户端验证等等全由服务端来处理。 在开发这类网站时由于工作全在服务端因此如果我们使用ASP.NET开发自然地所有的任务都将由aspx, C#这类技术来实现 采用这种方式开发出来的网站页面与用户的交互性通常不会很友好比如提交数据时经常需要整页刷新。【门户类网站】这类网站与之前的【以服务端为中心的网站】有个重要的差别页面只是呈现数据 表单的提交全采用AJAX方式了。这样做的好处能将显示逻辑与数据更新逻辑有效的分离不必纠缠在一起可认为是二个通道 在这种开发模式下由于页面只负责数据的显示因此只要能将业务逻辑尽可能地与UI分离项目在维护上会容易很多 采用这种方式开发的网站页面与用户交互的友好性会好很多而且也不会影响SEO因此被较多的门户网站采用。【纯AJAX网站】在这类网站中服务端由于不参与UI处理网站可以只是些静态的HTML文件 而且在设计页面时只要留下一些占位符就可以了UI元素全部由JS来生成。 这类网站的客户端通常会选择一个JS的UI框架来支持。这类界面相对于前二种来说会更漂亮用户的操作体验也会更友好。 但由于页面主要由JS来生成对SEO的支持较差因此特别适合一些后台类的网站项目。在前面所列出的三种开发方式中前二种由于界面部分由服务端来实现 因此选择一个合适的框架会对开发工作有着非常重要的影响尤其是第一种。 但是如果选择第三种方式那么选择 WebForm 还是 MVC 真的都是浮云了甚至还可以使用其它的服务框架来支持AJAX的调用。喜欢微软的MVC框架的一些人通常会列举一些WebForm中较为低级的缺点从而把ASP.NET MVC框架说的很完美而且是非它不可。 这里我不想谈论它们的优点与缺点因为我前面已经说过了在我看来它们都有优点也同时有各自的缺点。 今天我只想先暂且忘记它们来实现自己的框架。开始吧看看我的作品。回到顶部介绍我的MVC框架我比较喜欢ASP.NET这个平台因为它们扩展性实在太好了在它上面我可以容易地实现自己所需的东西 包括开发自己所需要的WEB框架。通过微软的ASP.NET MVC框架也让我认识到MVC思想的优点 因此我的WEB框架也将采用MVC思想来开发因此我把自己的这个框架称为【我的MVC框架MyMVC】。 今天的博客也将向您展示这个框架同时我也会与大家一起分享我在开发框架过程中所使用到的一些技术或者称为实现方式。为了让大家对我的MVC框架有个感性的认识我准备了一个示例网站网站提供二种完全不同的风格 分别采用【门户类网站】和【纯AJAX网站】的方式来开发。 在示例网站的首页程序会让您选择喜欢的界面风格来继续后面的操作当然您也可以随时在右上角切换风格。回到顶部我的MVC框架设计架构在我的框架中【页面请求】与【AJAX请求】是分开来实现的。因为我前面以对开发方式做过分类在开发【纯AJAX网站】时 那么就几乎没有页面请求了或许有但可以忽略此时在服务端全是AJAX服务我喜欢将AJAX的服务端实现称为服务。我将AJAX请求分开来处理是因为 我做的网站中AJAX的使用非常多数量都会超过页面请求而且有时甚至没有ASPX页面全是AJAX调用所以我更看重AJAX。二种请求我称为通道大致是这样的处理过程说明示意图中并没有直观地反映出【页面请求】与【AJAX请求】在处理过程中的差别 但这个差别是存在的差别主要在于从URL到Action的映射过程后面会有详细地介绍。以下示意图表示了【我的MVC框架】在处理一个请求时的具体过程今天的博客内容将主要介绍这个框架如何实现AJAX请求处理页面请求的实现过程将在后续的博客中介绍。回到顶部回忆以往AJAX的实现方式我的MVC框架对AJAX的支持来源于我对代码不断重构的过程为了更好地了解我的MVC框架 我认为有必要先来回忆一下以往是如何在服务端实现AJAX的。在ASP.NET中有一种比较原始的实现Ajax的方式那就是创建一个ashx就像下面的代码当然了也有人会选择创建一个空的aspx去代替ashx而且使用aspx还可以只输出一个HTML片段。在这种原始的方式下整个处理过程可以大致分为注释中所标注的三个阶段。 如果使用这种方式去做服务端的AJAX开发当AJAX的数量到达一定规模后可以发现大量的代码是类似。我之所以称为【类似】是因为它们却实有差别差别在于参数的名字不同参数的类型不同参数的个数不同要调用的方法以及返回值不同。说实话这种机械代码我也写过。不过当我发现时这个现象时我就开始想办法去解决这个问题因为我非常不喜欢写这类重复性质的代码。在重构过程中也逐渐形成了我自己的AJAX服务端框架。后来我把它写到我的第一篇博客中了 【晒晒我的Ajax服务端框架】在AJAX的发展过程中微软曾经推出过ASP.NET AJAX框架它可以在服务端生成一些JS的代理类让客户端的JS方便地调用服务端的方法。 虽然那个框架设计地很巧妙并且与WebForm配合地很完美只可惜那个框架不够流行。 后来的WCF通过一些配置也可以让JS去调用不过喜欢的人也不多可能还是因为配置麻烦的缘故吧。 当后来微软推出了ASP.NET MVC框架时一些人开始惊呼AJAX非ASP.NET MVC框架不可。 因为ASP.NET MVC框架可以很容易让JS去调用一个C#方法从此以后再也不用去【读参数调用方法写输出】这些繁琐的事情了 而且没有WCF那么复杂的配置。 的确他们没有解决的问题ASP.NET MVC框架很好地解决了。今天的博客我将向大家介绍我的AJAX解决方案它同样可以很好的解决上面的那些繁琐的过程。回到顶部MyMVC中实现AJAX的方式在我的框架中服务端可以很容易地将一个C#方法公开给客户端的JavaScript来访问比如下面这个C#方法public class AjaxOrder { [Action] public void AddOrder(OrderSubmitForm form) { Order order form.ConvertToOrderItem(); BllFactory.GetOrderBLL().AddOrder(order); }那么客户端就可以通过这个URL地址来调用那个方法/AjaxOrder/AddOrder.cspx URL中的一些名称与C#类名以及方法的名称的对应关系请参考下图。至于C#方法所需的参数你就不用担心了框架会替您准备好你只要访问就可以了。说明这个Action太简单了连返回值也没有。后面会有返回值的示例代码请继续阅读。前面的示例可以用下面的图形来表示C#代码与URL的映射关系补充说明一下按照MVC的标准术语下文将这类用于处理请求的方法将称为【Action】Action所在的类型称为Controller。 不过在我的MVC框架中Action又分【PageAction】和【AjaxAction】。 而且在我的MVC框架中对Controller限制极少不会要求您继承什么类型或者实现什么接口Controller甚至可以是个静态类。唯独只要求1.包含AjaxAction的Controller必须以Ajax开头包含PageAction的Controller必须以Controller结尾照顾喜欢微软MVC框架的用户。 加这个限制仅仅是为了快速定位Action并没有其它原因。 2. 类型与方法的可见性为 public 同样仅仅只是为了快速定位 。所以在我的框架中Controller的意义将只是一个Action的容器。回到顶部如何使用MyMVC框架中的AJAX功能在我的MVC框架中JS几乎可以透明地直接调用C#方法。比如我有这样一个C#方法public class AjaxDemo { [Action] public string GetMd5(string input) { if( input null ) input string.Empty; byte[] bb (new MD5CryptoServiceProvider()).ComputeHash(Encoding.Default.GetBytes(input)); return BitConverter.ToString(bb).Replace(-, ).ToLower(); } }方法很简单可以计算一个字符串的MD5值。下面再来看一下如何在JS中调用$(#btnGetMd5).click(function(){ $.ajax({ // 以下二个URL地址都是有效的。 //url: /AjaxDemo/GetMd5.cspx, url: /AjaxDemo.GetMd5.cspx, data: {input: $(#txtInput).val()}, success: function(responseText){ $(#spanReslt).text(responseText); } }); });说明一下这里我使用JQuery这个JavaScript类库来完成客户端的部分。在JS代码中我通过一个URL地址就可以直接访问到前面所定义的C#方法C#方法所面的参数由$.ajax()的data参数指定。 由于实在过于简单我感觉不需要再对这个示例做更多的解释。唯独我要提醒的是为了安全JS并不能调用任何一个C#方法虽然在技术上没有任何难度。 所以如果您允许一个C#方法公开给JS调用那么方法必须加[Action]这个Attribute 。在前面的示例中方法的传入参数以及返回值的类型都比较简单事实上MyMVC也可以支持复杂的数据类型。 例如以下方法的签名都是有效的有了MyMVC就几乎上不需要再去访问QueryStringForm这些对象了。你需要什么参数只要写在方法的签名中就可以了。参数可以是简单的数据类型也可以是自定义的数据类型参数的个数也没有限制。不过有一点我要提醒您所有的数据来源只有二个地方QueryString和Form框架只读取这二个地方而且直接访问它们的索引器。 由于QueryStringForm这二个类型都是NameValueCollection而NameValueCollection的索引器在实现上有点独特因此请大家注意它们的返回值。 关于NameValueCollection的细节描述可以参考我的博客【细说 Request[]与Request.Params[]】 今天我就不再重谈这个细节话题了。在读取参数时万一出现key重复了怎么办框架还提供另一种解决方案那就是您可以在C#的方法的签名中声明NameValueCollection类型的变量变量名可以从【FormQueryStringHeadersServerVariables】中选择。 注意对于后二者框架本身也是不读取的如果需要读取只能使用这个方法来获取。示例代码如下代码中我同时要求框架给出这四个集合事实上您可以根据实际情况来决定需要多少个参数。注意1. 参数名称是大小写【不敏感】的。2. 类型一定要求是NameValueCollection 。3. 框架会优先读取QueryString如果没有则会查看Form4. 千万不要在Action中使用HttpContext.Current.Request.QueryString[]的方式读取来自客户端的参数。关于参数还有一种特殊的情况我在博客【细说 Form (表单)】中曾提到过 例如我有这样二个类型它们的结构一样如果此时我有这样一个C#方法又该如何处理呢上面的示例也可以理解成一模一样的参数类型就是要出现多次再或者多个不同的自定义类型中有些成员的名称是相同的。此时我的框架在设计时与微软的MVC框架一样要求在HTML中对name做特殊的设置示例代码如下此时要求input标签中的name必须能够反映C#方法的参数名以及类型中所包含的数据成员名称。注意在MyMVC框架中自定义的数据类型所包含的数据成员不要求是属性字段(Field)也是完全受支持的。回到顶部配置MyMVC框架MyMVC框架在使用前必须配置。在前面的示例中/AjaxDemo2/TestCustomerType.cspx 这样的URL地址按照ASP.NET的默认设置它是不能被映射到一个有效的处理器的 那时将出现一个404异常。因此为了使用MyMVC中对AJAX的支持必须做以下配置httpHandlers add path*Ajax*/*.cspx,*Ajax*.*.cspx verb* typeMyMVC.AjaxHandlerFactory, MyMVC validatetrue/ /httpHandlers如果在IIS7的环境中运行还需要以下配置在示例代码中我使用了【cspx】这个扩展名如果您不喜欢也可以选择您所喜欢的扩展名这个不是问题。关于配置参数中的【path】属性请参考我的上篇博客【细说 HttpHandler 的映射过程】这里也不再重新解释。 如果没有看过的建议还是去看一下下面将会用到那些知识因为它非常重要。回到顶部MyMVC框架的实现原理 - 映射处理器入口前面谈到了MyMVC框架的配置通过那个配置相当于在ASP.NET中为MyMVC注册了一个入口点。根据上面的配置符合条件的请求将会被映射给AjaxHandlerFactory。既然是这样我们来看一下这个入口点的实现代码代码中每个步骤做了什么事情注释中有说明不需要再重复说明。最后创建的ActionHandler的实现代码如下整个入口点就是这样的。有没人想过为什么不直接在web.config中映射到这个ActionHandler呢答案在后面请继续阅读。回到顶部MyMVC框架的实现原理 - 对Session的支持前面有一个方法的实现我故意没有贴出那么是ActionHandler.CreateHandler()这个静态方法。现在是时候来贴它了这段代码又涉及另外二个类型它们的实现代码如下internal class RequiresSessionActionHandler : ActionHandler, IRequiresSessionState { } internal class ReadOnlySessionActionHandler : ActionHandler, IRequiresSessionState, IReadOnlySessionState { }不要感到奇怪这二个类型的确没有任何代码。它们除了从ActionHandler继承而来还实现了另外二个接口 那二个接口我在博客【Session有没有必要使用它】中已有详细的解释 不明白的朋友可以去阅读那篇博客。再来回答前面那个问题为什么不直接在web.config中映射到这个ActionHandler呢答如果这样配置那么对Session的支持将只有一种模式在这个框架中我采用HttpHandlerFactory就可以轻松地实现对多种Session模式的支持。说到这里我真的感觉上篇博客【细说 HttpHandler 的映射过程】的研究成果太有意义了 是它给【MyMVC对Session完美的支持】提供了灵感。老实说我是不使用Session的。但看到以前的博客中有些人还是坚持使用Session所以就决定在MyMVC中支持这个功能 毕竟支持Session不是件难事。下面再来说说如何支持Session 。在上面这段代码中我加了一个[SessionMode]的Attribute用它可以指定Action的Session支持模式 SessionMode是个枚举值定义如下MyMVC框架支持以上三种不同的Session模式默认是关闭的如果需要使用请显式指定。 [SessionMode]既可以用于Controller类型也可以用于Action 。注意Session的使用将会给Action的单元测试带来麻烦。
写了几篇细说之后,今天打算换换口味,还是来写代码吧。 所以,这次博客将以实际的代码来展示在ASP.NET平台上开发自己的框架,我希望也能让您发现这并不是件难事。
我在前面的博客【用Asp.net写自己的服务框架】中 发布了一个用ASP.NET写的服务框架那个框架我目前仍在使用中。近来由于时常也会有人问我一些关于ASP.NET MVC的话题 因此就想再写个自己的MVC框架出来一方面可以留给自己使用另外也可以谈谈MVC尤其可以展示一下在ASP.NET下写框架的乐趣。我之所以将写框架看成是件有乐趣的事是因为在写框架的过程中会接触许多的技术细节。比如1. 为了支持Session需要了解管线过程以及支持Session工作的原理。2. 在HttpHandler的映射过程中HttpHandlerFactory的使用会让设计很灵活。3. 反射可以让我们轻松地根据一个URL去寻找匹配的Action以及为Action准备传入参数。4. 为了便于测试Action如何有效的封装框架的功能这里有许多ASP.NET的技术细节。5. 如何设计让框架更灵活更强大。在开始今天的博客之前我想有必要说说我的框架的规模如果说ASP.NET WebForm是个复杂框架ASP.NET MVC是个轻量级框架的话那么我的MVC框架将只是个微量级的框架。但这个微量级的框架却可以提供许多实用的功能因为我没有引入一些与MVC无关的东西而且完全遵守MVC的思想而设计。由于我的框架规模实在太小因此有些地方的确是不够完善但我认为在大多数情况下是够用的。回到顶部ASP.NET程序的几种开发方式时常见到有人在问到底选择WebForm还是MVC 其实我认为最好是二个都选吧。然后你去发现它们的优点与缺点最后当你觉得它们都不爽时还是写自己的框架吧。我来说说我这样选择的理由任何东西都有它们的优点这很正常所以二个都选就能发现更多的优点 发现优点的过程其实也是自己进步的过程。当然它们也有缺点发现那些缺点你自然会想办法去避开 其实这也是个进步的过程。因此在你吸收优点以及避开缺点的过程中会感觉它们不再完美因为自己在进步 再到后来你会怎么选择我就不知道了那就是你自己的事了。 而我选择了另一条路写自己的ASP.NET MVC框架。在比较各类框架之前我想有必要先来总结一下现在能用ASP.NET开发哪些类型的网站 由于ASP.NET与WCF这类纯服务性质的框架不同我们主要还是用它来开发一些可与用户交互的界面程序。 因此今天的分类将重要讨论这些界面UI的开发方式。我认为目前的ASP.NET能支持开发三种类型的网站1.以服务端为中心的网站所有页面的生成以及交互的逻辑全部服务端来完成服务端甚至能生成必要的JS代码。2.门户类网站服务端只负责页面的第一次呈现用户的交互以及表单的提交全部采用AJAX的方式完成。3.纯AJAX网站服务端基本上不参与UI的处理只负责处理数据UI在客户端由JavaScript来生成并控制提交。【以服务端为中心的网站】这类网站有个非常明显的特点至少在开发上表现地非常明显 服务端要做的事情很多HTML的生成, 简单的JS交互客户端验证等等全由服务端来处理。 在开发这类网站时由于工作全在服务端因此如果我们使用ASP.NET开发自然地所有的任务都将由aspx, C#这类技术来实现 采用这种方式开发出来的网站页面与用户的交互性通常不会很友好比如提交数据时经常需要整页刷新。【门户类网站】这类网站与之前的【以服务端为中心的网站】有个重要的差别页面只是呈现数据 表单的提交全采用AJAX方式了。这样做的好处能将显示逻辑与数据更新逻辑有效的分离不必纠缠在一起可认为是二个通道 在这种开发模式下由于页面只负责数据的显示因此只要能将业务逻辑尽可能地与UI分离项目在维护上会容易很多 采用这种方式开发的网站页面与用户交互的友好性会好很多而且也不会影响SEO因此被较多的门户网站采用。【纯AJAX网站】在这类网站中服务端由于不参与UI处理网站可以只是些静态的HTML文件 而且在设计页面时只要留下一些占位符就可以了UI元素全部由JS来生成。 这类网站的客户端通常会选择一个JS的UI框架来支持。这类界面相对于前二种来说会更漂亮用户的操作体验也会更友好。 但由于页面主要由JS来生成对SEO的支持较差因此特别适合一些后台类的网站项目。在前面所列出的三种开发方式中前二种由于界面部分由服务端来实现 因此选择一个合适的框架会对开发工作有着非常重要的影响尤其是第一种。 但是如果选择第三种方式那么选择 WebForm 还是 MVC 真的都是浮云了甚至还可以使用其它的服务框架来支持AJAX的调用。喜欢微软的MVC框架的一些人通常会列举一些WebForm中较为低级的缺点从而把ASP.NET MVC框架说的很完美而且是非它不可。 这里我不想谈论它们的优点与缺点因为我前面已经说过了在我看来它们都有优点也同时有各自的缺点。 今天我只想先暂且忘记它们来实现自己的框架。开始吧看看我的作品。回到顶部介绍我的MVC框架我比较喜欢ASP.NET这个平台因为它们扩展性实在太好了在它上面我可以容易地实现自己所需的东西 包括开发自己所需要的WEB框架。通过微软的ASP.NET MVC框架也让我认识到MVC思想的优点 因此我的WEB框架也将采用MVC思想来开发因此我把自己的这个框架称为【我的MVC框架MyMVC】。 今天的博客也将向您展示这个框架同时我也会与大家一起分享我在开发框架过程中所使用到的一些技术或者称为实现方式。为了让大家对我的MVC框架有个感性的认识我准备了一个示例网站网站提供二种完全不同的风格 分别采用【门户类网站】和【纯AJAX网站】的方式来开发。 在示例网站的首页程序会让您选择喜欢的界面风格来继续后面的操作当然您也可以随时在右上角切换风格。回到顶部我的MVC框架设计架构在我的框架中【页面请求】与【AJAX请求】是分开来实现的。因为我前面以对开发方式做过分类在开发【纯AJAX网站】时 那么就几乎没有页面请求了或许有但可以忽略此时在服务端全是AJAX服务我喜欢将AJAX的服务端实现称为服务。我将AJAX请求分开来处理是因为 我做的网站中AJAX的使用非常多数量都会超过页面请求而且有时甚至没有ASPX页面全是AJAX调用所以我更看重AJAX。二种请求我称为通道大致是这样的处理过程说明示意图中并没有直观地反映出【页面请求】与【AJAX请求】在处理过程中的差别 但这个差别是存在的差别主要在于从URL到Action的映射过程后面会有详细地介绍。以下示意图表示了【我的MVC框架】在处理一个请求时的具体过程今天的博客内容将主要介绍这个框架如何实现AJAX请求处理页面请求的实现过程将在后续的博客中介绍。回到顶部回忆以往AJAX的实现方式我的MVC框架对AJAX的支持来源于我对代码不断重构的过程为了更好地了解我的MVC框架 我认为有必要先来回忆一下以往是如何在服务端实现AJAX的。在ASP.NET中有一种比较原始的实现Ajax的方式那就是创建一个ashx就像下面的代码当然了也有人会选择创建一个空的aspx去代替ashx而且使用aspx还可以只输出一个HTML片段。在这种原始的方式下整个处理过程可以大致分为注释中所标注的三个阶段。 如果使用这种方式去做服务端的AJAX开发当AJAX的数量到达一定规模后可以发现大量的代码是类似。我之所以称为【类似】是因为它们却实有差别差别在于参数的名字不同参数的类型不同参数的个数不同要调用的方法以及返回值不同。说实话这种机械代码我也写过。不过当我发现时这个现象时我就开始想办法去解决这个问题因为我非常不喜欢写这类重复性质的代码。在重构过程中也逐渐形成了我自己的AJAX服务端框架。后来我把它写到我的第一篇博客中了 【晒晒我的Ajax服务端框架】在AJAX的发展过程中微软曾经推出过ASP.NET AJAX框架它可以在服务端生成一些JS的代理类让客户端的JS方便地调用服务端的方法。 虽然那个框架设计地很巧妙并且与WebForm配合地很完美只可惜那个框架不够流行。 后来的WCF通过一些配置也可以让JS去调用不过喜欢的人也不多可能还是因为配置麻烦的缘故吧。 当后来微软推出了ASP.NET MVC框架时一些人开始惊呼AJAX非ASP.NET MVC框架不可。 因为ASP.NET MVC框架可以很容易让JS去调用一个C#方法从此以后再也不用去【读参数调用方法写输出】这些繁琐的事情了 而且没有WCF那么复杂的配置。 的确他们没有解决的问题ASP.NET MVC框架很好地解决了。今天的博客我将向大家介绍我的AJAX解决方案它同样可以很好的解决上面的那些繁琐的过程。回到顶部MyMVC中实现AJAX的方式在我的框架中服务端可以很容易地将一个C#方法公开给客户端的JavaScript来访问比如下面这个C#方法public class AjaxOrder { [Action] public void AddOrder(OrderSubmitForm form) { Order order form.ConvertToOrderItem(); BllFactory.GetOrderBLL().AddOrder(order); }那么客户端就可以通过这个URL地址来调用那个方法/AjaxOrder/AddOrder.cspx URL中的一些名称与C#类名以及方法的名称的对应关系请参考下图。至于C#方法所需的参数你就不用担心了框架会替您准备好你只要访问就可以了。说明这个Action太简单了连返回值也没有。后面会有返回值的示例代码请继续阅读。前面的示例可以用下面的图形来表示C#代码与URL的映射关系补充说明一下按照MVC的标准术语下文将这类用于处理请求的方法将称为【Action】Action所在的类型称为Controller。 不过在我的MVC框架中Action又分【PageAction】和【AjaxAction】。 而且在我的MVC框架中对Controller限制极少不会要求您继承什么类型或者实现什么接口Controller甚至可以是个静态类。唯独只要求1.包含AjaxAction的Controller必须以Ajax开头包含PageAction的Controller必须以Controller结尾照顾喜欢微软MVC框架的用户。 加这个限制仅仅是为了快速定位Action并没有其它原因。 2. 类型与方法的可见性为 public 同样仅仅只是为了快速定位 。所以在我的框架中Controller的意义将只是一个Action的容器。回到顶部如何使用MyMVC框架中的AJAX功能在我的MVC框架中JS几乎可以透明地直接调用C#方法。比如我有这样一个C#方法public class AjaxDemo { [Action] public string GetMd5(string input) { if( input null ) input string.Empty; byte[] bb (new MD5CryptoServiceProvider()).ComputeHash(Encoding.Default.GetBytes(input)); return BitConverter.ToString(bb).Replace(-, ).ToLower(); } }方法很简单可以计算一个字符串的MD5值。下面再来看一下如何在JS中调用$(#btnGetMd5).click(function(){ $.ajax({ // 以下二个URL地址都是有效的。 //url: /AjaxDemo/GetMd5.cspx, url: /AjaxDemo.GetMd5.cspx, data: {input: $(#txtInput).val()}, success: function(responseText){ $(#spanReslt).text(responseText); } }); });说明一下这里我使用JQuery这个JavaScript类库来完成客户端的部分。在JS代码中我通过一个URL地址就可以直接访问到前面所定义的C#方法C#方法所面的参数由$.ajax()的data参数指定。 由于实在过于简单我感觉不需要再对这个示例做更多的解释。唯独我要提醒的是为了安全JS并不能调用任何一个C#方法虽然在技术上没有任何难度。 所以如果您允许一个C#方法公开给JS调用那么方法必须加[Action]这个Attribute 。在前面的示例中方法的传入参数以及返回值的类型都比较简单事实上MyMVC也可以支持复杂的数据类型。 例如以下方法的签名都是有效的有了MyMVC就几乎上不需要再去访问QueryStringForm这些对象了。你需要什么参数只要写在方法的签名中就可以了。参数可以是简单的数据类型也可以是自定义的数据类型参数的个数也没有限制。不过有一点我要提醒您所有的数据来源只有二个地方QueryString和Form框架只读取这二个地方而且直接访问它们的索引器。 由于QueryStringForm这二个类型都是NameValueCollection而NameValueCollection的索引器在实现上有点独特因此请大家注意它们的返回值。 关于NameValueCollection的细节描述可以参考我的博客【细说 Request[]与Request.Params[]】 今天我就不再重谈这个细节话题了。在读取参数时万一出现key重复了怎么办框架还提供另一种解决方案那就是您可以在C#的方法的签名中声明NameValueCollection类型的变量变量名可以从【FormQueryStringHeadersServerVariables】中选择。 注意对于后二者框架本身也是不读取的如果需要读取只能使用这个方法来获取。示例代码如下代码中我同时要求框架给出这四个集合事实上您可以根据实际情况来决定需要多少个参数。注意1. 参数名称是大小写【不敏感】的。2. 类型一定要求是NameValueCollection 。3. 框架会优先读取QueryString如果没有则会查看Form4. 千万不要在Action中使用HttpContext.Current.Request.QueryString[]的方式读取来自客户端的参数。关于参数还有一种特殊的情况我在博客【细说 Form (表单)】中曾提到过 例如我有这样二个类型它们的结构一样如果此时我有这样一个C#方法又该如何处理呢上面的示例也可以理解成一模一样的参数类型就是要出现多次再或者多个不同的自定义类型中有些成员的名称是相同的。此时我的框架在设计时与微软的MVC框架一样要求在HTML中对name做特殊的设置示例代码如下此时要求input标签中的name必须能够反映C#方法的参数名以及类型中所包含的数据成员名称。注意在MyMVC框架中自定义的数据类型所包含的数据成员不要求是属性字段(Field)也是完全受支持的。回到顶部配置MyMVC框架MyMVC框架在使用前必须配置。在前面的示例中/AjaxDemo2/TestCustomerType.cspx 这样的URL地址按照ASP.NET的默认设置它是不能被映射到一个有效的处理器的 那时将出现一个404异常。因此为了使用MyMVC中对AJAX的支持必须做以下配置httpHandlers add path*Ajax*/*.cspx,*Ajax*.*.cspx verb* typeMyMVC.AjaxHandlerFactory, MyMVC validatetrue/ /httpHandlers如果在IIS7的环境中运行还需要以下配置在示例代码中我使用了【cspx】这个扩展名如果您不喜欢也可以选择您所喜欢的扩展名这个不是问题。关于配置参数中的【path】属性请参考我的上篇博客【细说 HttpHandler 的映射过程】这里也不再重新解释。 如果没有看过的建议还是去看一下下面将会用到那些知识因为它非常重要。回到顶部MyMVC框架的实现原理 - 映射处理器入口前面谈到了MyMVC框架的配置通过那个配置相当于在ASP.NET中为MyMVC注册了一个入口点。根据上面的配置符合条件的请求将会被映射给AjaxHandlerFactory。既然是这样我们来看一下这个入口点的实现代码代码中每个步骤做了什么事情注释中有说明不需要再重复说明。最后创建的ActionHandler的实现代码如下整个入口点就是这样的。有没人想过为什么不直接在web.config中映射到这个ActionHandler呢答案在后面请继续阅读。回到顶部MyMVC框架的实现原理 - 对Session的支持前面有一个方法的实现我故意没有贴出那么是ActionHandler.CreateHandler()这个静态方法。现在是时候来贴它了这段代码又涉及另外二个类型它们的实现代码如下internal class RequiresSessionActionHandler : ActionHandler, IRequiresSessionState { } internal class ReadOnlySessionActionHandler : ActionHandler, IRequiresSessionState, IReadOnlySessionState { }不要感到奇怪这二个类型的确没有任何代码。它们除了从ActionHandler继承而来还实现了另外二个接口 那二个接口我在博客【Session有没有必要使用它】中已有详细的解释 不明白的朋友可以去阅读那篇博客。再来回答前面那个问题为什么不直接在web.config中映射到这个ActionHandler呢答如果这样配置那么对Session的支持将只有一种模式在这个框架中我采用HttpHandlerFactory就可以轻松地实现对多种Session模式的支持。说到这里我真的感觉上篇博客【细说 HttpHandler 的映射过程】的研究成果太有意义了 是它给【MyMVC对Session完美的支持】提供了灵感。老实说我是不使用Session的。但看到以前的博客中有些人还是坚持使用Session所以就决定在MyMVC中支持这个功能 毕竟支持Session不是件难事。下面再来说说如何支持Session 。在上面这段代码中我加了一个[SessionMode]的Attribute用它可以指定Action的Session支持模式 SessionMode是个枚举值定义如下MyMVC框架支持以上三种不同的Session模式默认是关闭的如果需要使用请显式指定。 [SessionMode]既可以用于Controller类型也可以用于Action 。注意Session的使用将会给Action的单元测试带来麻烦。