老赵点滴


  先做人,再做技术人员,最后做程序员。
  我的理想:“让外国人看中国人写的技术书籍和文章”。Try as I might
posts - 287, comments - 10549, trackbacks - 137, articles - 6
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

  先来发一通牢骚。

  其实这是一篇迟发布近2个月的文章。事实上在ASP.NET MVC Preview 2发布之前我就已经将这篇文章的所有内容准备完毕了。当时想,就等Preview 2发布吧,而真一旦Preview 2发布之后却又懒得进行移植——移植了之后却又懒得写文章。这一拖就是近2个月,毫无长进。可能工作等其他事情的确多了些,但是扪心自问,也并没有忙到不可开交。时间往往都是在点点滴滴间浪费的。唉,可能是自视太高,越来越不愿意写一些普普通通的介绍性文章,导致可写的东西大大减少。不过话说回来,其实打算写的,甚至多次说过要写得东西也并不少,为什么就就是没有动笔呢?其实还是一个“懒”字——当年的勤奋劲儿到哪里去了呢?

  言归正传。先解释一下标题,什么是“UpdatePanel for ASP.NET MVC”呢?ASP.NET AJAX中的UpdatePanel相信大家都有所了解。可惜的是,ASP.NET MVC框架的诞生“毁灭”了大量基于PostBack的控件,首当其冲地可能就是UpdatePanel了。如果没有PostBack,UpdatePanel就失去了全部作用,甚至不如一些绑定控件,至少它们还能够用于展示。为UpdatePanel长吁短叹之后,我们不禁又开始怀念UpdatePanel的优势:“透明”。在UpdatePanel的帮助下,实现AJAX操作对于开发人员几乎完全透明。我们要做的仅仅是将需要AJAX更新的内容用UpdatePanel包装起来,一切都是那么优雅。

  我们能否在ASP.NET MVC中拯救UpdatePanel呢?也许是可以的吧,但这更像是一个“不可能完成的任务”。我不是传说中的阿汤哥,因此重新为ASP.NET MVC量身定制一个AJAX解决方案似乎更为可行。虽然我们不会苛求一个新生事物从诞生开始就趋向完美,但即使只是一个原型,它也必须严格遵守的一些原则:

  • 不得破坏MVC中的协议(协作,职责等等)
  • 对开发人员尽可能地透明

  Nikhil Kothari曾经提出了他在ASP.NET MVC框架下的AJAX解决方案。如果您还不了解他的做法,那么我先在这里进行一点概括。Nikhil扩展了Controller使之支持一种Ajax操作,于是我们在代码中就可以写如下代码:

public class TaskListController : AjaxController {
    ...
    public void CompleteTask(int taskID) {
        if (String.IsNullOrEmpty(Request.Form["deleteTask"]) == false) {
            InvokeAction("DeleteTask");
            return;
        }
 
        Task task = _taskDB.GetTask(taskID);
        if (task != null) {
            _taskDB.CompleteTask(task);
        }
 
        if (IsAjaxRequest) {
            if (task != null) {
                RenderPartial("TaskView", task);
            }
        }
        else {
            RedirectToAction("List");
        }
    }
    ...
}

  与AjaxController类似,Nikhil也为ViewPage和ViewControl提供了一些扩展方法,因此目前在View(List.aspx)中我们就能看到如下的代码:

<div id="taskList">
    <% foreach (Task task in Tasks) { %>
        <div>
          <% this.RenderPartial("TaskView", task); %>
        </div>
    <% } %>
</div>

  在View和Controller中都存在对于RenderPartiel方法的调用,它们的作用就是向客户端输出一个“Partial Template”生成的HTML代码。而在ASP.NET MVC的默认配置中,Partial Template即为User Control。而在TaskView这个Partial Template中可以看到一些辅助方法:

<div id="taskItem<%= Task.ID %>" class="taskPanel">
<% Ajax.Initialize(); %>
<% this.RenderBeginAjaxForm(
      Url.Action("CompleteTask"),
      new {
          Update = "taskItem" + Task.ID,
          UpdateType = "replace",
          Completed = "endUpdateTask"}); %>

    <input type="hidden" name="taskID" value="<%= Task.ID %>" />
    <input type="submit" class="completeButton" name="completeTask" value="Done!" />
    <input type="submit" class="deleteButton" name="deleteTask" value="Delete" />
    <span><%= Html.Encode(Task.Name) %></span>
 
<% this.RenderEndForm(); %>
<% Ajax.RenderScripts(); %>
</div>

  这些辅助方法的作用是生成一些触发AJAX更新的标签及脚本,当用户点击RenderBeginAjaxForm与RenderEndForm方法生成的tag之间的提交按钮时,网页将会向服务器端发出一个AJAX请求,而服务器端的Action并最终会通过RenderPartial方法输出一个Partial Template生成的HTML。服务器端最终输出的HTML将会被替换或添加到页面的某个元素内。这就形成了一个AJAX效果。这个解决方案从某些方面看上去很酷,尤其是生成的代码可以添加到某个元素中,而不单单是如同UpdatePanel的替换,例如Nikhil在他的例子中就使用了这个特性实现了一个添加功能。不过如果使用之前提出的原则来衡量的话,似乎这个解决方案并不十分理想。

  原因很简单,因为不够透明。

  也有评论认为,Controller中的逻辑不该根据一个请求AJAX与否而进行不同处理(Nikhil的解决方案使用RenderPartial来替代RenderView为AJAX操作进行输出),因此这个解决方案破坏了MVC的职责。我不这么认为,但是我希望能做到这一点,因为做到这一点即意味着绝对的透明。绝对透明则意味着Controller将一个应用程序是否AJAX的决定权完全交给了客户端,这点非常理想,因为AJAX完全是一个表现层的概念。ASP.NET AJAX中的UpdatePanel在这方面的表现可圈可点(虽然还远不够完美),因此我最后决定也为ASP.NET MVC开发一款类似UpdatePanel的组件。值得庆幸的是,ASP.NET MVC默认使用WebForm页面作为视图模板,在这个强大的模型之下,构建出这样一个AJAX解决方案(的原形)似乎并不十分困难。

  我将这个控件命名为MvcAjaxPanel。MvcAjaxPanel与UpdatePanel最大的区别在于后者接收的是PostBack,而前者接收的只是普通的HTTP请求。Post“Back”意味着Post过后回到了原来的Page,而ASP.NET MVC的请求往往会被引导至不同的页面。因此如何跨页面进行内容更新是MvcAjaxPanel首要解决的问题。最终我选择了为每个MvcAjaxPanel指定一个UpdateAreaID的做法。

<mvc:MvcAjaxPanel runat="server" ID="mvcAjaxPanel" UpdateAreaID="Header">
    ...
</mvc:MvcAjaxPanel>

  当页面向服务器端发出一个AJAX请求时将会附带页面中的UpdateAreaID信息,而服务器端的Action并不会意识到这一点,因此依旧按照寻常逻辑指定一个视图模版并输出HTML。不过,如果视图模板中的MvcAjaxPanel发现这个请求实际上是一个符合约定的AJAX请求(请注意,只有View组件意识到这是个请求的性质),则会使用新的方法来替换标准的输出。这时候模板就会根据客户端传递过来的UpdateAreaID,寻找页面上具有同样属性值的MvcAjaxPanel,有选择性地输出内容。在客户端就会有对应的JavaScript代码接收服务器端的数据,并且更新页面中的相应区域。

  很明显,MvcAjaxPanel的工作原理与UpdatePanel有颇多相似之处,也做到了一定程度上的透明。而且与Nikhil的解决方案相比,一个非常重要的优势就是可以一次更新页面中的多个区域——其实这也就是UpdatePanel的特性之一。而且这种对Controller透明的做法又有一个天然的特点,那就是能够轻松地在不支持AJAX的浏览器中使用传统的方式切换页面。

  服务器端的实现原理并不复杂,不过作为解决方案的另一个关键部分,如何在客户端触发一个AJAX提交也是一个值得思考的话题。UpdatePanel的方式可谓“全自动”:页面加载时将会把服务器端的Trigger信息输出至客户端,然后在客户端截获form的提交事件,并通过UniqueID或DOM结构等方式来判断这次提交是否该转化为AJAX方式。不过在一个ASP.NET MVC页面中几乎不会出现产生PostBack的元素,相反会有大量的普通链接,它们才是AJAX更新的主要截获目标。

  为此我提供了一些JavaScript代码,截获一个链接原本的目标地址并将其转化为一个AJAX请求。我在这里通过示例中的代码来展示这种使用方式(这个示例源于Brad Abrams提供的ASP.NET MVC示例,不过我舍弃了Northwind数据库与Entity Framework,取而代之的是XML数据以及自定义的简单Model。此外,我也将其移植到ASP.NET MVC框架的0416 Build中):

<%  foreach (var category in this.ProductCategories)
    { %>
        <li>
            <%= Html.ActionLink<ProductsController>(
                    c => c.List(category, 1),
                    category,
                    new { onclick = "mvcAjax.get(this, event)" })%>
        </li><%
    } %>

  这段代码来自分类列表页。与AJAX改进之前的代码相比,唯一的区别就是额外指定了元素的onclick事件(加粗部分)。在onclick事件执行中,这个链接默认的跳转行为将被取消,取而代之的是一个AJAX请求,请求的目标便是ProductsController中名为List的Action。

  我们可以使用上面的方式应对普通链接,那么又该如何将一个客户端from的提交行为也变成AJAX操作呢?以下依旧是示例中的代码:

<form method="post"
    action="<%= Url.Action("Update", new { id = this.Product.ProductID }) %>"
    onsubmit="mvcAjax.submit(this, event);">
 
    <table>
        <tr>
            <td>Name:</td>
            <td><%= Html.TextBox("Name", this.Product.Name) %></td>
        </tr>
        ...
    </table>
 
    <input type="submit" value="Save" />
</form>

  在截获了form的submit事件之后,客户端将会收集该form中的所有input、select等值,组成一个请求的body,并且以HTTP POST的方式发出一个AJAX请求。余下的事情和之前就没有什么区别了。

  与UpdatePanel相比,MvcAjaxPanel的客户端截获方式可谓“纯手工”,但是我并不认为这会造成什么问题。ASP.NET MVC强调的就是职责分离,而这种分离并不仅仅体现在代码上,也体现在开发人员的职责上。在开发ASP.NET MVC应用程序时,负责View的是前端开发工程师,对他们来说JavaScript与AJAX可谓是再熟悉不过的技术。在合时的地方手动编写一些JavaScript调用反而会让他们得到无比的自由性。例如在之前的代码示例中,调用mvcAjax.get或mvcAjax.submit方法时完全可以在前后自由地加入额外操作或者条件判断。这就不会像使用UpdatePanel时,如果需要使用JavaScript提交一个AJAX更新,还需要借助不登大雅之堂的trick

  也正因为如此,Nikhil提出的解决方案非常不错,它能够和前台开发人员的自定义逻辑进行灵活地结合。此外,通过阅读ASP.NET MVC框架0416 Build的代码,我发现在新版本的ASP.NET MVC中似乎将会内置这种AJAX解决方案了——不过这也的确符合微软的一贯做法,不是吗?:)

  这个AJAX解决方案原型的使用方式和工作原理已经描述完了,如果您对其具体实现感兴趣,或者想亲自尝试一下,可以下载文章末尾的附件。附件中的解决方案包含三个项目,MvcAjax为提供MvcAjaxPanel的项目,而MvcWebApp是一个普通的ASP.NET MVC示例程序,而MvcAjaxWebApp自然就是添加AJAX效果之后的结果了。在示例中,我还在Master Page中定义的菜单(即页面左侧的菜单)里显示了一块当前时间,这是为了体现MvcAjaxPanel的“一次提交,多处更新”的特点。

  不过需要强调的是,这仅仅是个原型。或者说这只是一种实现上尝试,在很多细节方面并没有作太多追求。如果要成为一个完善的AJAX解决方案,还需要作大量的改进。例如:

  • 提供一些客户端的hook供前台开发人员使用(如提交前、接受后、或者处理一个提交还没有返回,客户端就发起另一个请求的情况等等)。
  • 更强大的功能,更好的开发体验(如客户端触发机制)
  • 异常处理
  • 支持脚本
  • 支持跳转(Redirection)
  • ...

  此外,作为面向ASP.NET MVC特有的AJAX解决方案,也有一些额外的问题需要考虑。最典型的问题之一就是在使用ASP.NET MVC时很少使用模板控件,而更多的使用页面中的循环,那么如何让MvcAjaxPanel在循环内容生效?我也产生过一些想法,但是如果要真正确定下来最终的实现方式,很多东西还需要进一步思考。如果您对于这个AJAX解决方案有什么建议或其他任何想法,也请尽快告诉我。

  最后再说一件有趣的事情:在我实现了这个原型之后的某一天,忽然意识到这个控件似乎不光可以为ASP.NET MVC使用,也能够用于普通的WebForms应用程序。这真是一个令人意外的发现。

 

附件:源代码及使用示例

Feedback

#1楼    回复  引用  查看    

2008-04-26 01:55 by marcll JJ      
受教了.

#2楼    回复  引用  查看    

2008-04-26 02:24 by 任力      
老赵,注意身体哦~~

#3楼    回复  引用  查看    

2008-04-26 02:44 by 笨→鸟(Bird)      
老赵。为把身体保养好,零点以前睡觉觉。

#4楼 [楼主]   回复  引用  查看    

2008-04-26 03:18 by Jeffrey Zhao      
@笨→鸟(Bird)
0点睡觉太奢侈,3点,3点,呵呵。

#5楼    回复  引用  查看    

2008-04-26 04:26 by TT.Net      
学习中~~感谢老赵

#6楼    回复  引用  查看    

2008-04-26 04:45 by TT.Net      
很完美,最好能加上游览器的history,那就更完美了

#7楼    回复  引用  查看    

2008-04-26 05:01 by TT.Net      
相对于Nikhil的MVC ajax解决方案。我更喜欢老赵的,开发人员不太需要去定义那些AJAX的controller,只要在view上用上updatepanel,就像webform中一样,如果和老赵说的那样微软要把 Nikhil的方法集成到下一版本MVC中,感觉没有老赵这个方案更适合MVC的推广应用~

#8楼    回复  引用  查看    

2008-04-26 08:04 by LanYo      
3点??强人就是强人!

#9楼    回复  引用  查看    

2008-04-26 09:20 by wuhang      
支持,最近好像要出MVC的Webcast了?

#10楼    回复  引用  查看    

2008-04-26 09:26 by 秸杆-老赵粉丝的昵称      
好,学习了!老赵的东西肯定有巨大价值,先收着慢慢品。

其实,俺一直没用过UpdatePanel,也不知ASP.NET MVC是啥,自己汗一个

#11楼    回复  引用    

2008-04-26 09:31 by 黑色幽默 [未注册用户]
有时间就用用看!

#12楼    回复  引用  查看    

2008-04-26 10:14 by Indigo Dai      
是不是Nikhil提到的位于ASP.NET MVC上的 AJAX解决方案最终会进入到官方的发布中呢?好像他专门搞这方面的,好像ASP.NET AJAX框架就是他架构的。如果是这样,最终版本肯定是有所改进的,现在我估计是放出来听听“风声”,微软现在知道社区的重要性了。看看那些开源的开发框架多符合口味,要啥有啥!

#13楼    回复  引用  查看    

2008-04-26 11:49 by SZW      
我也正好准备在http://www.cnblogs.com/szw/archive/2008/04/12/1150353.html之后继续写一些UpdatePanel、Repeater之类的“控件”,已经有了个大致路,和MvcAjaxPanel不同的是尽量不去用runat=server,全部靠自身的js库(以及约定)去实现,老赵最后提出的“如何让MvcAjaxPanel在循环内容生效”的问题在我这个模型里面应该不难实现。只是现在还在考虑是否使用josn+模板的形式还是这样直接输出html的形式,所以迟迟没有开始写,目前我更倾向使用前者,但是这样做还有一些细节方面的特殊情况需要针对处理。

现在这里说下我的思路:
对于用json方式:View第一次通过Get请求输出数据,同时这个数据(当然包含html标签)构成了一个现成的"模板",第二次通过ajax接受json的时候直接利用这个现成的“模板”进行填充(当然我前面已经说了还有些细节需要处理,但总体实现是没有问题的)。(PS:这种方法效率很高,但并非“万能”,如果有些UpdatePanel传回的内容形式是极为不确定的(完全颠覆之前格式的,其实总体来看还只是少数),可以考虑同时或单独使用以下这种html方式。)

对于html方式:和老赵的最终实现方式类似,其中一种方式是真个页面重新生成,然后到客户端替换,但是我觉得这样做如果只为一小块区域(比如一个<span>的信息)的话,效率不够高(老赵的源代码我还没有细细看,不知是否是这样?)。所以又想到了第二种方法:让UpdatePanel中的代码单独运行,然后传回html,直接替换客户端标签内容。目前我能想到的综合考虑最“实惠”的方法是使用mvc的ascx,好处是可以单独呈现,并且实现相对json方法更简单,而且可以在不同场合复用(特别是浮动层,我为此做了一个简单的demo,用起来太爽了),可谓一举多得。用ascx有一点不太好的地方是有时候编辑的时候不如在一个页面中直观。

老赵对此是否有什么建议?
以上两个方法也同样适合在ASP.NET MVC/WebForms中使用。

#14楼    回复  引用  查看    

2008-04-26 12:08 by xjb      
楼主强人呀,3点才睡觉,难道早上10点上班?

#15楼    回复  引用  查看    

2008-04-26 13:11 by Q.Lee.lulu      
呵呵,老赵果然爱学习啊,0416的代码都已经研究了.....
SZW说的尽量不要去使用runat="server"这个想法不错,对于ASP.NET MVC来说确实应该如此,期待你的文章.

另问一个问题:一般的web form如果用JS库(例如jQuery)来实现Ajax,如何来使用postback呢 ?望解答偶的菜鸟问题

#16楼    回复  引用  查看    

2008-04-26 14:05 by 笨→鸟(Bird)      
@Jeffrey Zhao
呵呵.我是新兵.学习中.以后还望多指点.

#17楼 [楼主]   回复  引用  查看    

2008-04-26 14:16 by Jeffrey Zhao      
@SZW
你说的这个方式不是和Nikhil的方式差不多吗?他没有用JSON,用的是直接输出HTML,你可以看看他的做法,他在设计上还考虑了一些使用时的变化。其实一般项目中我也基本上是用的你说的第一种做法,不过没有封装成控件而已。我以前写过用ascx作为模板生成HTML的文章,就是为了这么做。
我目前没有整页Render,只输出需要的部分,这就好像你说的UpdatePanel内部的代码单独运行,生成HTML。但是我认为如果可用性要更高的话很可能就需要这样做,因为一个MvcAjaxPanel所处的位置往往和页面上下文息息相关。最典型的例子,就是我说的一个MvcAjaxPanel放在一个循环内部。
至于性能其实没有什么差别。这些方面所谓的性能在实际应用中都基本上可以忽略不计。而且即使用UserControl生成HTML,还记得怎么做吗?不就是把它放入一个Page,然后Execute一下就获得代码吗?还是在执行一个完整的页面,只是页面内容少了点,相差的只是一些method call,没有数据访问,没有复杂计算。
而且我思考过,如果真care这点的话,也只要用一个新的View来代替一个复杂无比的View。因为MvcAjaxPanel是可以跨页Rendering的,这也就是和UpdatePanel最大的区别。

#18楼 [楼主]   回复  引用  查看    

2008-04-26 14:20 by Jeffrey Zhao      
@Q.Lee.lulu
我不觉得ASP.NET MVC就应该尽量不使用runat=server。runat=server是模板引擎的特性,使用它也不会有任何缺陷。
你问的如何进行postback是什么意思啊?是指什么呢?

#19楼 [楼主]   回复  引用  查看    

2008-04-26 14:22 by Jeffrey Zhao      
@Indigo Dai
Nikhil是ASP.NET团队的架构师,但ASP.NET AJAX不知道是不是他设计的,至少所谓的“UpdatePanel之父”不是他,呵呵。

#20楼    回复  引用    

2008-04-26 14:22 by asp.net CMS [未注册用户]
学习ing

厉害

#21楼 [楼主]   回复  引用  查看    

2008-04-26 14:24 by Jeffrey Zhao      
@TT.Net
我的设计目标和Nikhil的方式在理念上是不同的,目标是共存,而不是替换。
这点不像ASP.NET AJAX与AJAX.NET,前者一出现后者就差不多被完全替换掉了……

#22楼    回复  引用  查看    

2008-04-26 14:40 by Areyan      
受教了,继续关注。

#23楼 [楼主]   回复  引用  查看    

2008-04-26 15:07 by Jeffrey Zhao      
@SZW
其实你说的和Nikhil的方法都和RoR比较接近,那就是基于Partial Template(在ASP.NET MVC里自然就是User Control)的AJAX解决方案。不过既然是ASP.NET MVC,我就想多利用点现有模型,搞出一点不同于其他技术的东西。

#24楼    回复  引用  查看    

2008-04-26 15:59 by SZW      
@Jeffrey Zhao
大概看了下你的Nikhil的代码,我觉得你的方法比Nikhil的更“实用”一些。赞:)
我的想法和Nikhil还是有区别的,虽然在Views里面看上去有些相似,我觉得在Controller里面的Ajax和non-Ajax差别操作少一点更好。

依靠HTML实现“刷新”这条路子上面,需要单独运行一段html的方法其实还能想到不少,之所以我觉得ascx(我上面强调的是mvc中的ascx)挺好,也是出于你上面提到的一种事实——Nikhil那样或者近似的方法使得Ajax请求在整个Controller中显得过于“特殊”,而ASP.NET MVC恰巧在这个环节上为我们提供了一个很大的便利——RenderView可以同时返回aspx或者ascx(本质也都是Execute,aspx优先于ascx),而不需要任何特殊处理——这种便利归功于MVC结构本身。同时aspx和ascx也都接受ViewData(ascx在aspx中使用的时候自动传承aspx的ViewData,“兼容性”很好)的模型,从这点上来说,acsx成为一个单独“页面”或者HTML段更为便捷和“无差异化”。从使用、调用上来看,也更加灵活。

#25楼 [楼主]   回复  引用  查看    

2008-04-26 16:06 by Jeffrey Zhao      
@SZW
不觉得有什么差别,呵呵。Nikhil的Controller代码把AJAX操作和非AJAX操作混在一起了,这主要是因为他是将一个普通应用程序给“AJAX化”的。他也完全可以只为AJAX输出,这样就不用判断什么IsAjaxRequest了,而且其实他现在的代码也执行不到IsAjaxRequest等于false的情况。不过他判断IsAjaxRequest也有一个好处,那就是“能够轻松地在不支持AJAX的浏览器中使用传统的方式切换页面”。

#26楼    回复  引用  查看    

2008-04-26 16:08 by SZW      
@Jeffrey Zhao
写完上面这段话突然想起以前想到的一个问题,jsp之所以在MVC框架的发展上面相对asp.net如此“兴旺”,应该也和其本身的架构有很大的关系,jsp“天生”就更加容易去实现这一系列C-V分离的需求,不知是否有同感?

#27楼 [楼主]   回复  引用  查看    

2008-04-26 16:15 by Jeffrey Zhao      
@SZW
我也不知道是不是“天生”,不过这的确应该涉及到一个演变规律的问题。
不管WebForm和MVC孰优孰劣,WebForm模型的复杂度远甚于MVC应该是一件大家都承认的事情。从当年诞生开始WebForms就很强大,就走上了组件化,事件模型等等这一条路。
而JSP它和ASP.NET WebForms相比我觉得真也只能算是一个“HTML显示引擎”,功能远不及后者,而把逻辑混在这个引擎里使用本来就属于一种滥用。为了解决滥用问题,于是有人发展出了MVC这种协作方式,把JSP单纯作为一个模板引擎,逻辑由另外组件来承受。
所以我觉得要说JSP是“天生容易实现MVC”(你看在ASP.NET里实现MVC也不困难啊),还不如说是天生有缺陷所以被强迫着改变。。

#28楼    回复  引用  查看    

2008-04-26 16:36 by SZW      
@Jeffrey Zhao
首先我觉得ASP.NET(WebForms)里实现MVC很简单还不如说是实现MVP比较简单,WebForms(使用其功能、特性的前提)框架下面就决定了哪怕实现了所谓“MVC结构”,也不会在实质上达到“MVC架构”的要求,这一点在开发环节上就已经很明显了。
其次ASP也有同样的诟病(当然和jsp架构上面引发的缺点是不同方面的),为什么ASP的MVC相对jsp没那么兴盛和完善呢?这个一直是我以前开发ASP的时候对ASP十分疑虑的问题,我个人的感觉是“架构”这个“天生”的方面发展了也同时制约了很多东西。现在回过头来看ASP我都想笑……

#29楼 [楼主]   回复  引用  查看    

2008-04-26 17:13 by Jeffrey Zhao      
@SZW
技术架构决定容不容易,有了这个保证之后实现逻辑架构其实只要有聪明人就能实现格框架。剩下的就是特定框架在设计上的优劣,这个容易取长补短。
还有我是说ASP.NET实现MVC简单,而不是说WebForms。ASP.NET的技术架构很好,以前只是往WebForms发展了,现在出一个MVC也非常简单。现在ASP.NET MVC是一个例子,以前Monorail也是一个例子。
至于说ASP为什么没有发展MVC,是因为它被抛弃了,人们停止思考了。而微软选择了走WebForms这条路,Java选择了走MVC。当然ASP的技术架构的确不完善,要搞(现在这种)MVC估计不容易。
至于你说得在WebForms里实现MVC不容易,是因为你一定要保持所有的WebForms特性,还要求是目前ASP.NET MVC这种MVC方式。如果能保留一部分,舍弃一部分,实现一个可用的MVC我觉得也不是什么大问题。当然MVP诞生起就是面向事件驱动界面使用的模式,当然非常适合WebForms。
前一段时间太懒,希望接下来有动力可以写写这方面东西……

#30楼    回复  引用  查看    

2008-04-26 18:11 by 狼Robot      
学习

#31楼    回复  引用  查看    

2008-04-26 22:20 by Indigo Dai      
@Jeffrey Zhao

我感觉Nikhil对AJAX颇有研究。

#32楼    回复  引用  查看    

2008-04-26 22:59 by 王孟军!      
学习吧

#33楼    回复  引用  查看    

2008-04-27 00:13 by Q.Lee.lulu      
@Jeffrey Zhao
就是在Ajax进行postback的时候,如何直接使用页面的viewstate来进行postback

#34楼    回复  引用  查看    

2008-04-27 00:46 by Shiny Zhu      
这个扩展得不错~
比我用mootools自己写script来处理request方便得多了,但页面里还是有不好看的源代码。厄,为什么我总是看不习惯那些WebResoure.axd什么什么的呢?

#35楼 [楼主]   回复  引用  查看    

2008-04-27 00:51 by Jeffrey Zhao      
@Shiny Zhu
追求WebResource.axd好看不好看实在没有必要了吧,把脚本嵌在程序集里可是个good practice,要保证组件的内聚性这点必不可少。

#36楼 [楼主]   回复  引用  查看    

2008-04-27 00:51 by Jeffrey Zhao      
@Q.Lee.lulu
自己收集页面上所有该提交的数据组成个body就可以了。

#37楼    回复  引用    

2008-04-27 04:18 by 帅帅001 [未注册用户]
支持楼主,
收藏慢慢看,
一如既往支持老赵,

#38楼    回复  引用  查看    

2008-04-27 09:18 by 留恋星空      
虽然不全明白,但还是尝试着看。

#39楼    回复  引用    

2008-04-27 10:39 by 1111111195ee [未注册用户]
已经拜读
互联网创业下载中心:
http://down.163k.com
还有这个:
http://biz.163k.com

#40楼    回复  引用  查看    

2008-04-27 11:42 by Zhuang miao      
我现在基本不看你的文章了,看不懂。。。。

#41楼    回复  引用  查看    

2008-04-27 11:53 by SZW      
@Jeffrey Zhao
是的,我说的“简单”也就是这个意思,并不是单纯纵向看有多少潜力和发挥余地(这点上我对ASP.NET MVC还是抱有信心的)。

@Q.Lee.lulu
ViewState被编码后的内容都被存在input:hidden里面,用jquery只要对整个form的内容进行序列化(或者手动也可以,获取name="__VIEWSTATE"等),然后再使用jquery的ajax方式的form提交就可以了,我以前用这个方法做过一个只靠jquery的无刷新上传(WebForms下),在服务器端感觉不到有什么区别(连Files也能获取,这个通过普通的post好像是取不到的)。

#42楼    回复  引用  查看    

2008-04-27 14:24 by Shiny Zhu      
--引用--------------------------------------------------
Jeffrey Zhao: @Shiny Zhu
追求WebResource.axd好看不好看实在没有必要了吧,把脚本嵌在程序集里可是个good practice,要保证组件的内聚性这点必不可少。
--------------------------------------------------------

的确没必要的,这一点我现在已经接受了。把脚本嵌在程序集里的确很大程度上提高了编程的容易程度~

#43楼 [楼主]   回复  引用  查看    

2008-04-27 14:27 by Jeffrey Zhao      
@SZW
AJAX的Post是做不到上传文件的,但是jquery能够实现无刷新上传?我对这个很感兴趣,它是怎么做到的阿?应该是iframe但是我对与iframe接受服务器端信息总是做不好,主要是跨浏览器的问题……

#44楼    回复  引用  查看    

2008-04-27 15:00 by SZW      
@Jeffrey Zhao
那里面没有用iframe,我对ajax里面还要靠iframe实现“ajax效果”的做法还是比较回避的(除了为了实现“后退”等功能,也只能这样),要么干脆全部用iframe,这里面很多缺点(以及很多功能其实完全可以不用iframe)这里就不展开了。

用jquery+webforms实现上传是我考虑到了这么几个因素:
其一,webforms依靠postback的一系列操作实现文件上传实在是方便,功能也很强大,问题是你必须要postback才能获得Files的Collection。
其二,jquery的ajax功能也强大易用,问题是这个post你就这么直接用的话怎么也不会有postback的效果(以及之后对服务器控件、数据的操作、捕获),因为所有的viewstate都在这里断掉了。

问题如你所说,最简单常用的方法是用iframe,那样其实用ajax与否并不是什么关键问题了,同样,我也没有必要用别的js库去做了。我之所以让这两个搭配,就是要摆脱iframe(webforms和jquery在这里的作用都只是简化操作而已,而且是我觉得比较好的搭配,并不是非要这么用——其实我不应该太强到jquery的,只是Q.Lee.lulu正好提到了,请不要误会)。

为了使这个方法同时兼顾webforms(环境)和MVC中的使用,我最后决定在这个ajax请求没有特殊的对服务器控件的操作的情况下,让一个ashx去完成接受这个post过来的任务(返回当前页面也是可以的——那样是更纯粹的postback),在post之前,还需要对页面form当中的内容加以处理,也就是我上面说的对form中的内容进行序列化(关键是这里,配合jquery的一系列form和ajax功能做可以比较方便),比如达到这样的效果(只是个外观而已):__VIEWSTATE=abcd&File1=C:\x.jpg……。(这里__VIEWSTATE值只是为了说明一系列PostBack的特征参数)
如果不这样做,服务器是不会收集HttpFileCollection的(这个是我反复测试的结果,我对其中种种原理还没有全部了解清楚,如果有什么不对请指出),当然也无法用webforms的方法去实现文件上传。剩下的工作和普通的方法没有什么差别了,最后返回一个success的信息给页面就ok了

#45楼    回复  引用  查看    

2008-04-27 15:58 by 侯垒      
什么都不说了,赞一个,再学习.

#46楼 [楼主]   回复  引用  查看    

2008-04-27 16:06 by Jeffrey Zhao      
@SZW
没听懂。我再问的直接点吧,不说jquery,webform之类的东西了,说这些对于现在的问题只会混淆视听。
现在的结论就是用XMLHttpRequest对象没法上传一个文件。有什么解决方法或者workaround吗?
如果要保持不刷新上传,那么就需要iframe或富客户端(Flash/Silverlight)的支持,有别的方法吗?
或者再纯粹点的问法,如果要支持上传文件的无刷新提交,你会怎么做?

#47楼 [楼主]   回复  引用  查看    

2008-04-27 16:08 by Jeffrey Zhao      
@SZW
其实Anthem.NET的功能非常满足我的要求,但是我一直没有看它的实现方式,还是懒。

#48楼    回复  引用  查看    

2008-04-27 16:18 by Q.Lee.lulu      
@Jeffrey Zhao
@SZW
谢谢两位的解答,我再捕获下ASP.NET AJAX用UudatePanle进行Ajax请求所Post给服务器的参数(__VIEWSTATE等等),再研究下。我是想直接利用页面的ViewState来Postback回去给服务器,然后在后台就可以直接取页面上控件的值(不用自己手动在后台对Post回来的值进行处理,就跟同步提交时一样的取值,如 name=txtName.Text ).

#49楼    回复  引用  查看    

2008-04-27 16:27 by SZW      
@Jeffrey Zhao
你说的这些问题我上面都有答案阿:
====================
如果要保持不刷新上传,那么就需要iframe或富客户端(Flash/Silverlight)的支持,有别的方法吗?
====================
我上面这样的方法就不需要任何别的iframe或者flash,sl的支持。
当然我个人认为这个方法在效率上是比较高的,但是不一定是所有方法中最好的方式,只是我对于使用jquery的ajax和webforms配合上面的一些尝试。

这么说确实太抽象了,我找时间做一个实例发上来吧。

#50楼 [楼主]   回复  引用  查看    

2008-04-27 16:53 by Jeffrey Zhao      
@SZW
在body里拼接一个File1=C:\x.jpg就可以了?这个不可能上传内容的,上传的文件是需要被序列化至数据流的,而且这时候body的enocde也是multipart/form-data,而不是默认的application/x-url-encoded……

#51楼    回复  引用  查看    

2008-04-27 17:03 by SZW      
@Jeffrey Zhao
具体在文件传输这个单个的环节上面,jquery是否使用了别的方式暂时我还不太清楚,但是肯定是用到了multipart/form-data、form序列化以及服务器端对File的正常操作,当然还有jquery的form方式提交ajax请求,这个我已经实现了,没有发现有什么问题啊。
也许这个单个的环节上面jquery使用了别的什么方式,这个我当时也没有太过关心,但是实现的效果确实是通过ajax请求,不依靠iframe无刷新的,并且也能完成postback一样的上传操作的。我当时想的重点就是在实现上面,具体里面的流程是怎么样的,可以等我发上来了之后好好研究研究,我也想弄个究竟。

#52楼 [楼主]   回复  引用  查看    

2008-04-27 17:14 by Jeffrey Zhao      
@SZW
哈,不过我还是不信。JQuery也不是神也不能违反JavaScript限制的……AJAX的一个重要特点就是无法上传文件,如果你真能让XMLHttpRequest上传,那真是一个重大突破了。话说这个特点有没有官方文档可以看看?

#53楼    回复  引用  查看    

2008-04-27 17:24 by SZW      
@Jeffrey Zhao
上传环节它内部是怎么弄的,是不是XMLHttpRequest,如何XMLHttpRequest,或者还是服务器本身就是通过别的什么渠道获取的,目前还不清楚,亦或这个jquery.ajax后面还隐藏了什么(不会直接就不用XMLHttpRequest吧,那jquery让我太伤心了)。这个“搭配”是我自己想出来的,我没有看到过任何jquery官方说明说XMLHttpRequest可以实现文件上传。当时我也只是尝试了一下,没想到倒也就成了,你说的那些问题当时我也很纳闷阿:)

#54楼    回复  引用  查看    

2008-04-27 17:35 by SZW      
@Jeffrey Zhao
我当时的感觉是jquery.ajax是提交了一个请求,这个请求几乎、一定是ajax方式的,到了服务器端后,可能还是通过普通的(原本PostBack的)方式获取了HttpFileCollection(只是猜测,里面到底怎么做的我还没去深入了解),而接下去的操作反正就和ajax没有关系了,等到完成后,jquery.ajax还是获取返回的信息,至少整个表面上还是如一个普通的Ajax过程一样平静,也没有用到iframe。
或者说得简单一点,是不是有这种可能:这个"Ajax"请求里面包含了一个普通的Post请求,而这个请求从一开始到结束,都被jquery监视着,这种“无刷新”的效果并非来自XMLHttpRequest,而是因为这个请求本身就被强制控制着?

#55楼 [楼主]   回复  引用  查看    

2008-04-27 17:44 by Jeffrey Zhao      
@SZW
还是快给个例子看吧,最简单的就可以,呵呵……

#56楼    回复  引用  查看