转:ASP.Net MVC:校验、AJAX与过滤器

原文地址:http://blog.jobbole.com/85005/

一、校验 — 表单不是你想提想提就能提

1.1 DataAnnotations(数据注解)

位于 System.ComponentModel.DataAnnotations 命名空间中的特性指定对数据模型中的各个字段的验证。这些特性用于定义常见的验证模式,例如范围检查和必填字段。而 DataAnnotations 特性使 MVC 能够提供客户端和服务器验证检查,使你无需进行额外的编码来控制数据的有效

通过为模型类增加数据描述的 DataAnnotations ,我们可以容易地为应用程序增加验证的功能。DataAnnotations 允许我们描述希望应用在模型属性上的验证规则,ASP.NET MVC 将会使用这些 DataAnnotations ,然后将适当的验证信息返回给用户。

在DataAnnotations为我们所提供的众多内置验证特性中,用的最多的其中的四个是:

(0)[DisplayName]:显示名 – 定义表单字段的提示名称

(1)[Required] :必须 – 表示这个属性是必须提供内容的字段

(2)[StringLength]:字符串长度 – 定义字符串类型的属性的最大长度

(3)[Range]:范围 – 为数字类型的属性提供最大值和最小值

(4)[RegularExpression]:正则表达式 – 指定动态数据中的数据字段值必须与指定的正则表达式匹配

1.2 使用DataAnnotations为Model进行校验

假设我们的Model中有一个UserInfo的实体,其定义如下:


1

2

3

4

5

6

public class UserInfo

    {

        public int Id { get; set; }

        public string UserName { get; set; }

        public int Age { get; set; }

    }

UserInfo的属性很简单,只有三个:Id,UserName和Age三个字段;现在我们可以为其增加验证特性,看看其为我们提供的强大的校验功能。

(1)非空验证

添加特性:


1

2

3

4

5

6

7

[Display(Name="用户名")]

[Required(ErrorMessage = "*姓名必填")]

public string UserName { get; set; }

[Display(Name = "年龄")]

[Required(ErrorMessage = "*年龄必填")]

public int Age { get; set; }

验证效果:

(2)字符串长度验证

添加特性:


1

2

3

4

[Display(Name="用户名")]

[Required(ErrorMessage = "*姓名必填")]

[StringLength(5, ErrorMessage = "*长度必须小于5")]

public string UserName { get; set; }

验证效果:

(3)范围验证

添加特性:


1

2

3

4

[Display(Name = "年龄")]

[Required(ErrorMessage = "*年龄必填")]

[Range(18, 120)]

public int Age { get; set; }

验证效果:

(4)正则表达式验证

添加特性:验证用户输入的是否是数字,正则表达式匹配


1

2

3

4

5

[Display(Name = "年龄")]

[Required(ErrorMessage = "*年龄必填")]

[Range(18, 120)]

[RegularExpression(@"^\d+$", ErrorMessage = "*请输入合法数字")]

public int Age { get; set; }

验证效果:

(5)浏览所生成的HTML代码

从上图可以看出,我们在浏览器端的校验都是通过为html标签设置自定义属性来实现的,我们在Model中为其添加的各种校验特性,都会在客户端生成一个特定的属性,例如:data-val-length-max=“5”与data-val-length=”*长度必须小于5″对应[StringLength(5, ErrorMessage = "*长度必须小于5")]。然后,通过jquery validate在客户端每次提交之前进行校验,如果校验匹配中有不符合规则的,则将message显示在一个特定的span标签(class=”field-validation-valid”)内,并阻止此次表单提交操作。

1.3 使用DataAnnotations的注意事项

(1)首先,要确保需要进行校验的页面中引入了指定的几个js文件:


1

2

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>

<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

当然,jquery库的js文件也是必须的,而且在上面这两个js之前引入;

(2)在 Web.config 的appSettings中,已经默认支持了客户端验证(MVC3.0及更高版本中默认支持,MVC2.0则需要修改一下):


1

2

3

<!-- 是否启用全局客户端校验 -->

<add key="ClientValidationEnabled" value="true" />

<add key="UnobtrusiveJavaScriptEnabled" value="true" />

PS:Unobtrusive Javascript有三层含义:

一是在HTML代码中不会随意的插入Javsscript代码,只在标签中加一些额外的属性值,然后被引用的脚本文件识别和处理;

二是通过脚本文件所增加的功能是一种渐进式的增强,当客户端不支持或禁用了Javsscript时网页所提供的功能仍然能够实现,只是用户体验会降低;

三是能够兼容不同的浏览器。

(3)在Action中如果要对客户端是否通过了校验进行验证,可以通过以下代码实现:


1

2

3

4

5

6

7

8

9

10

[HttpPost]

public ActionResult Add(UserInfo userInfo)

{

    if (ModelState.IsValid)

    {

         // To do fun

    }

    return RedirectToAction("Index");

}

如果通过校验,则ModelState的IsValid属性(bool类型)会变为true,反之则为false。

二、ASP.Net MVC下的两种AJAX方式

2.1 使用JQuery AJAX方式

首先,在ASP.Net MVC中使用此种方式跟普通的WebForm的开发方式是一致的,需要注意的是:Url地址不同->请求的是Controller下的Action,例如在WebForm中请求的url通常是/Ajax/UserHandler.ashx,而在MVC中请求的url通常为:/User/GetAll。

例如,我们在一个View中添加一个按钮,用于使用AJAX获取一个服务器端的时间:


1

2

<h1>JQuery Ajax方式</h1>

<input id="btnJQuery" type="button" value="获取服务器时间" />

在Home控制器中增加一个用于返回时间的Action:


1

2

3

public ActionResult GetServerDate()

{return Content(DateTime.Now.ToString());

}

在View中增加一段JQuery代码,为btnJQuery按钮绑定一个Click事件:


1

2

3

4

5

6

7

8

9

$(function () {

        $("#btnJQuery").click(function () {

            $.post("/Home/GetServerDate", {}, function (data) {

                if (data != null) {

                    $("#spTime").html(data);

                }

            });

        });

});

这里通过JQuery AJAX发送一个异步的POST请求,获取服务器时间结果,并将其显示在span标签内:

至此,一个使用JQuery Ajax的MVC页面就完成了。但是,这仅是一个最简单的AJAX示例,在实际开发中往往比较复杂一点。

需要注意的是:

(1)如果你在JQuery AJAX中使用的是get方式的提交,那么在在使用Json返回JsonResult时注意要将第二个参数设置允许Get提交方式:return Json(“”,JsonRequestBehavior.AllowGet),否则你用get方式是无权执行要请求的Action方法的。

(2)在Ajax开发中要注意Ajax方法体内的参数设置正确,特别是参数名要和Action中的参数名保持一致;

(3)如果在Action中为其设置了[HttpPost]或[HttpGet],那么提交方式要跟Action打的标签一致;

2.2 使用Microsoft AJAX方式

在ASP.Net MVC中除了可以使用JQuery AJAX外,Microsoft为我们提供了另一套实用且更简单的AJAX方案,我们姑且称其为:Microsoft AJAX方式。

(1)首先:

需要将微软提供的js脚本引入到页面中:其实就是jquery.unobtrusive-ajax.js


1

2

<script src="~/Scripts/jquery-1.7.1.min.js"></script>

<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>

确保在Web.config中启用了Unobtrusive JavaScript


1

<add key="UnobtrusiveJavaScriptEnabled" value="true" />

(2)其次,使用Ajax.BeginForm方法构造一个form表单:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

<h1>Microsoft Ajax方式</h1>

 @using (Ajax.BeginForm("GetServerDate", "Home", new AjaxOptions()

 {

     HttpMethod = "POST",

     Confirm = "您确定要提交?",

     InsertionMode = InsertionMode.Replace,

     UpdateTargetId = "spResult",

     OnSuccess = "afterSuccess",

     LoadingElementId="loading"

 }))

 {

     <table>

         <tr>

             <td>用户名:</td>

             <td>

                 <input id="txtUserName" name="UserName" /></td>

         </tr>

         <tr>

             <td>密   码:</td>

             <td>

                 <input id="txtPassword" name="Password" /></td>

         </tr>

         <tr>

             <td align="center" colspan="2">

                 <input id="btnAjax" type="submit" value="提 交" />

             </td>

         </tr>

         <tr>

             <td align="center" colspan="2">

                 <div id="loading" style="display:none">

                     <img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在获取中,请稍候...

                 </div>

                 <span id="spResult"></span>

             </td>

         </tr>

     </table>

 }

这里需要注意的是:

①Ajax.BeginForm没有提供闭合的方法,需要使用Using配合关闭;

②AjaxOptions参数的设置:

HttpMethod代表此次AJAX请求到底是POST方式还是GET方式?这里是POST方式;

Confirm代表点击提交按钮后提出的确认对话框,并给出用户给定的提示语,这里是:您确定要提交?

InsertionMode代表请求获得后的数据是要替换还是追加,一般选择替换,即Replace;

UpdateTargetId代表需要替换的div标签的Id,这里是一个span标签,代表需要显示的信息都显示在这个span内;

OnSuccess代表请求成功后所需要执行的回调方法,是一个js方法,可以自定义,这里是一个function afterSuccess()的方法;


1

2

3

function afterSuccess(data) {

    //alert("您已成功获取数据:" + data);

}

LoadingElementId=”loading”是一个很有意思的属性,代表在ajax请求期间为了提供良好的用户体验,可以给出一个正在加载中的提示,而这个LoadingElementId则代表一个提示的div区域的Id。这里主要是指id为loading的这个div,其中有一张gif图片及一句话:正在获取中,请稍等…的提示。


1

2

3

<div id="loading" style="display:none">

      <img style="vertical-align:middle" src="~/Content/ico_loading2.gif" />正在获取中,请稍候...

</div>

为了显示加载提示的效果,我们人为地修改一下Action方法,使用Thread.Sleep(3000)来延迟一下请求返回时间


1

2

3

4

5

public ActionResult GetServerDate()

{

    System.Threading.Thread.Sleep(3000);

    return Content(DateTime.Now.ToString());

}

好了,现在我们可以看一下效果如何:

到此,我们的Microsoft AJAX就算完成了一个最简单的Demo了。那么,我们不禁想知道Microsoft AJAX是怎么做到的?跟校验一样,我们浏览一下生成的form表单就知道了:

原来我们在AjaxOptions中所设置的参数也被解析成了form的自定义属性,它们的对应关系如下:

三、为AOP而生 — ASP.Net MVC默认的过滤器

3.1 过滤器初步

  大一点的项目总会有相关的AOP面向切面的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中Action在执行前或者执行后我们想做一些特殊的操作(比如身份验证,日志,异常,行为截取等),而不想让MVC开发人员去关心和写这部分重复的代码。那么,我们可以通过AOP截取实现,而在MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复杂的AOP了。

AOP:Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

  利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

3.2 微软提供的几种默认过滤器

微软默认为我们提供了四种类型的过滤器(Filter),如下图所示:

这里,我们主要来看看ActionFilter(Action过滤器)和ExceptionFilter(异常过滤器)的使用:

(1)Action Filter

ActionFilterAttribute默认实现了IActionFilter和IResultFilter。而ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我们想使用它必须继承一下它然后才能使用。

①因此,我们首先在Models中新建一个类,取名为:MyActionFilterAttribute(以Attribute结尾比较符合编码规范),并使其继承自ActionFilterAttribute,然后重写基类所提供的虚方法:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

public class MyActionFilterAttribute : ActionFilterAttribute

{

    public string Name { get; set; }

    /// <summary>

    /// Action 执行之前先执行此方法

    /// </summary>

    /// <param name="filterContext">过滤器上下文</param>

    public override void OnActionExecuting(ActionExecutingContext filterContext)

    {

        base.OnActionExecuting(filterContext);

        HttpContext.Current.Response.Write("OnActionExecuting :" + Name);

    }

    /// <summary>

    /// Action执行之后

    /// </summary>

    /// <param name="filterContext">过滤器上下文</param>

    public override void OnActionExecuted(ActionExecutedContext filterContext)

    {

        base.OnActionExecuted(filterContext);

        HttpContext.Current.Response.Write("OnActionExecuted :" + Name);

    }

    /// <summary>

    /// ActionResult执行之前先执行此方法

    /// </summary>

    /// <param name="filterContext">过滤器上下文</param>

    public override void OnResultExecuting(ResultExecutingContext filterContext)

    {

        base.OnResultExecuting(filterContext);

        HttpContext.Current.Response.Write("OnResultExecuting :" + Name);

    }

    /// <summary>

    /// ActionResult执行之后先执行此方法

    /// </summary>

    /// <param name="filterContext">过滤器上下文</param>

    public override void OnResultExecuted(ResultExecutedContext filterContext)

    {

        base.OnResultExecuted(filterContext);

        HttpContext.Current.Response.Write("OnResultExecuted :" + Name);

    }

}

这里我们重写了四个虚方法,他们各自代表了在Action执行之前和之后需要执行的业务逻辑,以及在Result执行之前和之后需要执行的业务逻辑。这里的Result主要是指我们在Action中进行return 语句返回结果时(例如:return Content(“Hello Filter!”);),之前和之后要执行的逻辑处理。

比如:我们想要在每个Action执行之前进行用户是否登录的校验,可以在OnActionExecuting中判断用户Session是否存在,如果存在则继续执行Action的具体业务代码,如果不存在则重定向页面到登陆页,后边的Action业务代码不再执行。

②现在有了自定义的过滤器,我们怎么将其应用到Action中呢?这里有三种方式:

一是给某个控制器的某个Action指定此Filter:


1

2

3

4

5

6

[MyActionFilter(Name = "Filter Action")]

public ActionResult Filter()

{

    Response.Write("<p>Action正在努力执行中...</p>");

    return Content("<p>OK:视图成功被渲染</p>");

}

二是给某个控制器的所有Action指定此Filter:


1

2

3

4

[MyActionFilter(Name="Home Filter")]

public class HomeController : Controller

{

}

但是,要注意的是:如果既给Controller指定了Filter,又给该Controller中的某个Action指定了Filter,那么具体的这个Action以离其定义最近的Filter为准,也就是一个优先级的顺序问题:Action的Filter优先级高于Controller的Filter。

三是给此项目中的所有控制器即全局指定此Filter:在App_Start中更改FilterConfig类,此种方式优先级最低


1

2

3

4

5

6

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

{

    filters.Add(new HandleErrorAttribute());

    // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action

    filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });

}

③现在我们来看看具体的效果:

可以看到,我们的/Home/Filter这个Action中只有两句代码,一句Response.Write,另一句是return Content();在Response.Write之前执行了OnActionExecuting的过滤器方法,之后则执行了OnActionExecuted的过滤器方法;我们刚刚说了,在Action中的return语句代表了Result,那么在Result之前执行了OnResultExecuting过滤器方法,之后则执行了OnResultExecuted过滤器方法。这里仅仅是为了展示,在实际开发中是需要写一些具体的业务逻辑处理的,例如:判断用户的登录状态,记录用户的操作日志等等。

(2)Exception Filter

①同样,在Models中新建一个类,取名为:MyExceptionFilterAttribute,并使其继承自HandleErrorAttribute。


1

2

3

4

5

6

7

8

9

public class MyExceptionFilterAttribute : HandleErrorAttribute

{

    public override void OnException(ExceptionContext filterContext)

    {

        base.OnException(filterContext);

        HttpContext.Current.Response.Redirect("/Home/Index");

    }

}

这里,重写基类的OnException方法,这里仅仅为了演示效果,没有对异常进行处理。在实际开发中,需要获取异常对象,并将其记录至日志中。例如,下面一段代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public override void OnException(ExceptionContext filterContext)

   {

       base.OnException(filterContext);

       //获取系统异常消息记录

       string strException = filterContext.Exception.Message;

       if (!string.IsNullOrEmpty(strException))

       {

           //使用Log4Net记录异常信息

           Exception exception = filterContext.Exception;

           if (exception != null)

           {

               LogHelper.WriteErrorLog(strException, exception);

           }

           else

           {

               LogHelper.WriteErrorLog(strException);

           }

       }

  filterContext.HttpContext.Response.Redirect("~/GlobalErrorPage.html");

   }

②有了异常过滤器,我们怎么来应用到项目中呢?答案也在App_Start中,还是在FilterConfig类中,新添一句代码进行注册:


1

2

3

4

5

6

7

8

9

10

11

public class FilterConfig

{

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)

    {

        filters.Add(new HandleErrorAttribute());

        // 注册自定义Action过滤器:优先级最低,但是可以作用到所有的控制器和Action

        filters.Add(new MyActionFilterAttribute() { Name = "Global Controller" });

        // 注册自定义Exception过滤器

        filters.Add(new MyExceptionFilterAttribute());

    }

}

③为了测试,我们新增一个Action,使其能够出现一个异常:DividedByZero


1

2

3

4

5

6

7

public ActionResult Exception()

{

    int a = 10;

    int b = 0;

    int c = a / b;

    return Content("Exception is happened.");

}

④当我们测试这个Action时,会发现系统执行了自定义的异常过滤器,将我们的这个请求改为重定向到Index这个Action了。

参考资料

(1)蒋金楠,《ASP.NET MVC下的四种验证编程方式》,http://www.cnblogs.com/artech/p/asp-net-mvc-validation-programming.html

(2)蒋金楠,《ASP.NET MVC下的四种验证编程方式[续篇]》,http://www.cnblogs.com/artech/p/asp-net-mvc-4-validation.html

(3)马伦,《ASP.NET MVC 2014特供教程》,http://bbs.itcast.cn/thread-26722-1-1.html

(4)w809026418,《MVC中使用 DataAnnotations 进行模型验证》,http://www.cnblogs.com/haogj/archive/2011/11/16/2251920.html

(5)刘俊峰,《ASP.NET MVC中Unobtrusive Ajax的妙用》,http://www.cnblogs.com/rufi/archive/2012/03/31/unobtrusive-ajax.html

时间: 2024-10-10 02:46:16

转:ASP.Net MVC:校验、AJAX与过滤器的相关文章

ASP.NET MVC 实现AJAX跨域请求方法《1》

ASP.NET MVC 实现AJAX跨域请求的两种方法 通常发送AJAX请求都是在本域内完成的,也就是向本域内的某个URL发送请求,完成部分页面的刷新.但有的时候需要向其它域发送AJAX请求,完成数据的加载,例如Google. 在ASP.Net MVC 框架里实现跨域的AJAX请求有几种方式可以实现,以下就介绍常用的两种方法. 1.     发送JSONP请求 客户端: jQuery对发送JSONP请求有很好的支持,客户端通过. ajax() 函数发送请求,其中需要制定 dataType 为"j

ASP.NET MVC 实现 AJAX 跨域请求

ASP.NET MVC 实现AJAX跨域请求的两种方法 和大家分享下Ajax 跨域的经验,之前也找了好多资料,但是都不行,后来看到个可行的修改了并测试下 果然OK了   希望对大家有所帮助! 通常发送AJAX请求都是在本域内完成的,也就是向本域内的某个URL发送请求,完成部分页面的刷新.但有的时候需要向其它域发送AJAX请求,完成数据的加载,例如Google. 在ASP.NET MVC 框架里实现跨域的AJAX请求有几种方式可以实现,以下就介绍常用的两种方法. 1.     发送JSONP请求

用Fine Uploader+ASP.NET MVC实现ajax文件上传[代码示例]

Fine Uploader(http://fineuploader.com/)是一个实现 ajax 上传文件的 Javascript 组件. This project attempts to achieve a user-friendly file-uploading experience over the web. It's built as a Javascript plugin for developers looking to incorporate file-uploading int

asp.net mvc 使用ajax请求 控制器 (PartialViewResult)分部的action,得到一个分部视图(PartialView)的HTML,进行渲染

在asp.net mvc 使用ajax请求获取数据的时候,我们一般是返回json或者xml,然后解析这些数据进行渲染,这样会比较麻烦,可以请求一个 分部action,返回一个分部视图 直接可以渲染,不需要解析这一步. 下面是示例: 控制器类: public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } [HttpPost] public Pa

ASP.NET MVC的Ajax.ActionLink 的HttpMethod=&quot;Get&quot; 一个重复请求的BUG

这段时间使用BootStrap+Asp.net Mvc5开发项目,Ajax.ActionLink遇到一个重复提交的BUG,代码如下: 1 @model IList<WFModel.WF_Temp> 2 @{ 3 Layout = null; 4 } 5 6 <!DOCTYPE html> 7 8 <html> 9 <head> 10 <meta name="viewport" content="width=device-w

ASP.NET MVC中有四种过滤器类型

在ASP.NET MVC中有四种过滤器类型 Action 1.在ASP.NET MVC项目中,新建文件夹Filter,然后新建类MyCustormFilter,继承自ActionFilterAttribute类,我们来看下ActionFilterAttribute类有如下四个方法,从命名我应该就可以看出他们的执行时机. public class MyCustormFilter:ActionFilterAttribute { public override void OnActionExecuti

Asp.Net MVC Unobtrusive Ajax

1.   Unobtrusive JavaScript介绍 说到Unobtrusive Ajax,就要谈谈UnobtrusiveJavaScript了,所谓Unobtrusive JavaScript即为非侵入式JavaScript(即将Js代码与html代码分离,方便阅读与维护),是目前在Web开发领域推行的一种思想. 2.   在ASP.NET MVC中的应用 2.1 概述 使用VS新建一个ASP.NET MVC项目就会在~/Scripts/目录下面看到很多以unobtrusive结尾的ja

ASP.NET MVC 如何使用自定义过滤器(筛选器)

原文:ASP.NET MVC 如何使用自定义过滤器(筛选器) 继承*****Attribute(筛选器三种具体类)-->重写方法-->标记在控制器 或者 方法上面 [类名(类属性 = 值)]还有[AllowAnonymous]允许匿名访问(免授权) 原文地址:https://www.cnblogs.com/lonelyxmas/p/10597463.html

asp.net mvc 使用Ajax调用Action 返回数据【转】

使用asp.net mvc 调用Action方法很简单. 一.无参数方法. 1.首先,引入jquery-1.5.1.min.js 脚本,根据版本不同大家自行选择. <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> 2.在Controllers中书写前台Ajax需要调用的Action,比如:

Asp.net mvc 使用Ajax调用Action 返回数据。

使用asp.net mvc 调用Action方法很简单. 一.无参数方法. 1.首先,引入jquery-1.5.1.min.js 脚本,根据版本不同大家自行选择. <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script> 2.在Controllers中书写前台Ajax需要调用的Action,比如: