MVC 的优雅

用过 MVC 中局部更新的同学肯定会觉得其中的写法真的是非常的优雅:

public ActionResult Index()
{
    var data = UserService.GetUserList();
    if (Request.IsAjaxRequest())
    {
        return PartialView("UserList", data);
    }
    else
    {
        return View(data);
    }
}

如果在页面上加上一个刷新按钮后,第一次显示就能读取到这个 PartialView 的内容,点即刷新后就可以刷新页面。

这里的读取逻辑本应该是一样的,应该是写在同一个地方的,MVC 做到了这一点。

WebForm 中就不行了吗?

不是说 WebForm 中就不能实现类似的功能了,其实同样的功能完全可以通过 Ajax 或 UpdatePannel 来实现,并且也可以公用逻辑代码。

但是不这么做当然是因为它们有一定的缺陷。

 

Ajax+json:

这个写过的人肯定都明白它有多痛苦,特别是但更新的内容是一个复杂表格的时候… 我觉得没人会这么做吧?

Ajax+后台拼接 Html:

这种写法也大有人在,但是这实在是太不雅了!直接 PASS!

UpdatePannel:

UpdatePannel 对懒人和新手来说真是一个好东西啊。不用懂任何原理就可以实现 Ajax 无刷新更新数据。

但是它有一个很大的缺点,每次请求都会传送 ViewState,特别是当一个页面的 ViewState 很多的时候:

updatepannel

是不是很恐怖… 这还叫 Ajax 吗?还不如直接刷新页面呢!

另外,有很多人写 WebForm 完全不用控件,当然也包括 UpdatePannel。

MVC风格:

MVC 中的 Ajax 写法可以说是吸取了以上几种方法的有点,摒弃了它们的缺点。

首先它是纯粹的 Ajax 请求,不包含任何多余的数据(没有 ViewState)。

另外它把生成 Html 的工作交给了 UserControl(MVC 中称为 PartialView)。

最后,它也实现了代码的重用。

 

 

如何实现 MVC 中的 Ajax 用法

想在 WebForm 中使用类似 MVC 中的 Ajax 写法,难点主要就是两个:后端输出 UserControl + 前端代码

 

后端输出 UserControl 是为了把重复的代码写在 UserControl 中,并且可以被直接访问到。所以这里的难题是,如何让 UserControl 能够被直接访问。

默认 UserControl 是禁止访问的,因为它本身并不是继承与 IHttpHandler 所以无法直接输出,这里不懂的可以去搜索一下 IHttpHandler

 

后台能输出 UserControl 后其实已经非常简单了,前端部分自己写 js 就可以实现了,点击后 Ajax 请求 UserControl 的地址,得到数据后填充即可。

但是体验过 MVC 中 Ajax 的人都知道,这部分在 MVC 中也是不用写任何代码的,特别是 MVC 中的 Unobtrusive JavaScript,让代码更优美了。

所以,这里的难点并不是前端代码,而是如何使用  Unobtrusive JavaScript

 

 

后端输出 UserControl:方法一

怎么输出 UserControl?如果你不理解 Asp.net 核心的一些对象,也完全可以实现。

其实最简单的就是,把 .ascx 的页面用 .aspx 的页面包装一下,就可以啦!

 

我们先新建一个 .ascx 页面:

ascx

并在里面写上一些代码,这里是用来随便输出一些时间的,页面上需要点“刷新”来更新这些数据。

 

接下来我们新建一个 .aspx 页面:

wrap

这个页面完全是一个空页面,仅仅是为了把 UserControl 输出。

 

突然发现,输出 UserControl 就这样实现了!

这时候,前端请求它的时候只要请求这个 TimeList_.aspx 即可,相当于是它的外壳。

 

 

后端输出 UserControl:方法二

上面的那个方法虽然好用,但是总感觉的怪怪的,也略显麻烦。

其实上述的过程就是一个把 UserControl类 用 Page类 包裹的过程,这个过程完全可以用代码来实现。

 

首先我们先要自己实现一个 HttpHandler 来输出 UserContol:

namespace WebApplication1
{
    public class AjaxHandler : IHttpHandler
    {
        //private const string FLAG = ".ajax";//去掉这里的注释即可实现自定义后缀
        public void ProcessRequest(HttpContext context)
        {
            var page = new Page();
            var writer = new StringWriter();
            var url = context.Request.AppRelativeCurrentExecutionFilePath;

            //判断后缀是否为指定的后缀,是的话就替换成 .ascx
            //if (url.IndexOf('?') < 0 || url.IndexOf('?') > url.IndexOf(FLAG)) { url = url.Replace(FLAG, ".ascx"); }//去掉这里的注释即可实现自定义后缀

            //加载控件,并输出页面
            var control = page.LoadControl(url);
            page.Controls.Add(control);
            context.Server.Execute(page, writer, false);
            context.Response.Write(writer.ToString());
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}

 

接下来修改 Web.Config 文件,在这里添加一条记录,让所有的 .ascx 页面都由这个 AjaxHandler 来处理:

<system.web>
      <httpHandlers>
        <add verb="*" path="*.ascx" type="WebApplication1.AjaxHandler,WebApplication1"/>
      </httpHandlers>
    </system.web>

这里,type 中传入的两个参数分别是这个 HttpHandler 的完整名称,包括前面的命名空间;都好后面是这个 HttpHandler 所在的 dll 文件名。

 

让我们来直接访问一下:

timelist

 

成功!还有一个问题,怎么使用自定义扩展名?

只要把 HttpHandler 中的两行注释去掉,并且在 Web.Config 文件中的 path=”*.ascx” 改成 path=”.[自定义]” 就行了。

 

 

如何在 WebForm 中使用 MVC 的 Unobtrusive JavaScript

看过 Unobtrusive JavaScript 的人都知道,这种方式可以让 js 代码和 html 完全分离。也就是说,只要 WebForm 输出了同样格式的 html,并引用了相关的 js,就可以实现这个功能了!让我们一步步实现它。

 

我们先来用一个MVC 项目输出一个“刷新”按钮。

得到了如下代码:

<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#testDiv" href="/Home/ajax">刷新</a>

这里的 html 代码和是否使用 MVC 没有关系,那我们就尝试着直接在 WebForm 里直接打入以上代码吧。

另外,我们也要引入2个js,jquery.js 和 jquery.unobtrusive-ajax.js

前者去下载最新版即可,后者可以新建一个 MVC3 的项目后在项目中找到。

 

最终代码如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>

<%@ Register Src="TimeList.ascx" TagName="TimeList" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>
    <script src="Scripts/jquery.unobtrusive-ajax.js" type="text/javascript"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="timeList">
        <uc1:TimeList ID="TimeList1" runat="server" />
    </div>
    <a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#timeList"
        href="/timelist.ascx">刷新</a>
    </form>
</body>
</html>

 

前面,我在链接中的 attribute 是直接复制 MVC3 项目生成的 html 代码,那在 WebForm 里怎么生成这个代码呢?

其实只要把 MVC3 中的一个 class 和 enum 赋值过来即可。它们分别是:AjaxOptions 和 InsertionMode

利用 AjaxOptions 这个类的 ToUnobtrusiveHtmlAttributes 方法就可以生成一组符合标准的 attribute,然后再用代码把这些 attribute 附加到 a 标签上即可。

ajaxoptions

这里要注意的是,从 MVC3 源码中赋值过来的 AjaxOptions 在 .Net3.5 下会有点问题,自己编译一下修改下即可。

主要就是有几个方法在 .Net3.5 中不存在,所以只能自己实现了,但效果是一样的,它并没有依赖很多东西。

 

下面是一段参考代码:

.aspx.cs页面:

protected string GetAttributes(AjaxOptions ajaxOptions)
{
    var sb = new StringBuilder(" ");
    foreach (var attribute in ajaxOptions.ToUnobtrusiveHtmlAttributes())
    {
        sb.Append(string.Format("{0}=\"{1}\" ", attribute.Key, attribute.Value));
    }
    return sb.ToString();
}

.aspx 页面:

<a <%=GetAttributes(new WebApplication1.AjaxOptions{ UpdateTargetId = "timeList"}) %> href="/timelist.ascx">刷新</a>

看上去还没那么优雅,但是已经能实现这个功能了!

 

本作品由 Dozer 创作,采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。