当前位置: 移动技术网 > IT编程>开发语言>.net > Asp.net MVC验证哪些事(3)-- Remote验证及其改进(附源码)

Asp.net MVC验证哪些事(3)-- Remote验证及其改进(附源码)

2018年02月25日  | 移动技术网IT编程  | 我要评论

英国牙医虎口拔牙,太原诺基亚专卖,婆媳斗法之婆媳是怎样炼成的

一, RemoteAttribute验证使用

 

如果需要用户把整个表单填完后,提交到后台,然后才告诉用户说,“你注册的用户已经被占用了,请换一个用户名”,估计很多用户都可能要飚脏话了. MVC中的Remote验证是通过Ajax实现的,也就是说,当你填写用户名的时候,就会自动的发送你填写的内容到后台,后台返回检查结果。

 

1. 实现Remote验证非常简单,首先需要有个后台的方法来响应验证请求, 也就是需要创建一个Controller, 这里我们用ValidationController:

 

 

public class ValidationController : Controller

{

       public JsonResult IsEmployeeNameAvailable(string employeeName)

       {

           //这里假设已经存在的用户是”justrun”, 如果输入的名字不是justrun,就通过验证

           if (employeeName != "justrun")

           {

               return Json(true, JsonRequestBehavior.AllowGet);

           }

           return Json("The name 'justrun' is not available, please try another name.", JsonRequestBehavior.AllowGet);

       }

}

 

2. 接着在我们的Employee Model上应用上RemoteAttribute

 

 

public class Employee

{

      public int EmpId { get; set; }

      [DisplayName("Employee Name")]

     [Remote("IsEmployeeNameAvailable", "Validation")] //使用RemoteAttribute,指定验证的Controller和Action

      public String EmployeeName { get; set; }

 

}

 

3. 对应的View

 

 

@using (Html.BeginForm()) {

    @Html.AntiForgeryToken()

    @Html.ValidationSummary()

 

    <fieldset>

        <legend>Registration Form</legend>

        <ol>

            <li>

                @Html.LabelFor(m => m.EmployeeName)

                @Html.EditorFor(m => m.EmployeeName)

                @Html.ValidationMessageFor(m => m.EmployeeName)

            </li>

        </ol>

        <input type="submit" value="Register" />

    </fieldset>

}

 

4. 最后,看看验证的效果

 

MVC-Validation-remote

 

通过firebug能够看到,在填写表单的过程中,会不断的把表单的EmployeeName发送到我们指定的Controller, Action上做验证。

 

二, RemoteAttribute的局限性

 

使用 【RemoteAttribute】 来做远端验证的确是很棒– 它会自动的发起AJAX请求去访问后台代码来实现验证. 但是注意, 一旦表单提交了,就不会在存在这个验证了。比如当我用上【Required】这个验证标签的时候,无论在客户端还是服务器端,都存在着对于必填项的验证。服务器端可以通过ModelState.IsValid非常容易地判断,当前提交到后台的表单数据是否合法。但是【RemoteAttribute】只有客户端验证,而没有服务器端验证。 也就是说,如果用户的浏览器中,关闭js,我们的Remote检查就形同虚设。

 

是不是非常意外, 当接触Remote验证的时候,原以为默认的就会认为它会和其它验证标签一样。所以使用RemoteAttribute验证,是存在一定的安全隐患的。

 

三, RemoteAttribute的改进

 

先介绍一下对于RemoteAttribute的改进思路:

 

如果我们也想让RemoteAttribute和其它的验证特性一样工作,也就是说,如果不符合Remote的验证要求,我们希望ModelState.IsValid也是false, 同时会添加上相应的ModelError. 这里选择在MVC的Model binding的时候,做这个事情,因为在Model Binding的时候,正是将表单数据绑定到对应的model对象的时候。只要在绑定的过程中,如果发现Model中的属性有使用RemoteAttribute, 我们调用相应的验证代码。验证失败了,就添加上对于的ModelError.

 

由于涉及到了Model Binding和Atrribute的使用,如果有兴趣的,可以先看看这2篇文章:

 

Asp.net MVC使用Model Binding解除Session, Cookie等依赖 

 

.Net Attribute详解(上)-Attribute本质以及一个简单示例

 

1. 继承RemoteAttribute, 创建CustomRemoteAttribute

 

 

public class CustomRemoteAttribute : RemoteAttribute

   {

       public CustomRemoteAttribute(string action, string controller)

           : base(action, controller)

       {

           Action = action;

           Controller = controller;

       }

       public string Action { get; set; }

       public string Controller { get; set; }

   }

 

看了上面的代码,你也学会说,这不是什么都没干吗? 是的,这个CustomRemoteAttribute 的确是什么都没干,作用只是公开了RemoteAttribute的Controller和Action属性,因为只有这样我们才能知道Model添加的remote验证,是要访问那段代码。

 

2. 替换RemoteAttribute为CustomRemoteAttribute

 

这个非常简单,没有什么要解释的。

 

 

public class Employee

  {

      public int EmpId { get; set; }

      [DisplayName("Employee Name")]

     [CustomRemote("IsEmployeeNameAvailable", "Validation")]

      public String EmployeeName { get; set; }

 

  }

 

3. 自定义的CustomModelBinder

 

下面的CustomModelBinder就是在Model绑定的时候,调用相应的Action方法做验证,失败了,就写ModelError. 注释中已经解释了整个代码的工作流程。

 

 

public class CustomModelBinder : DefaultModelBinder

   {

         protected override void BindProperty(ControllerContext controllerContext,

         ModelBindingContext bindingContext,

         PropertyDescriptor propertyDescriptor)

       {

           if (propertyDescriptor.PropertyType == typeof(string))

           {

 

               //检查Model绑定的属性中,是否应用了CustomRemoteAttribute

               var remoteAttribute =

                 propertyDescriptor.Attributes.OfType<CustomRemoteAttribute>()

                   .FirstOrDefault();

 

               if (remoteAttribute != null)

               {

 

                    //如果使用了CustomRemoteAttribute, 就开始找到CustomAttribute中指定的Controller

                   var allControllers = GetControllerNames();

 

                   var controllerType = allControllers.FirstOrDefault(x => x.Name ==

                                                                            remoteAttribute.Controller + "Controller");

 

                   if (controllerType != null)

                   {

 

                       //查找Controller中的Action方法

                       var methodInfo = controllerType.GetMethod(remoteAttribute.Action);

 

                       if (methodInfo != null)

                       {

 

                           //调用方法,得到验证的返回结果

                           string validationResponse = callRemoteValidationFunction(

                             controllerContext,

                             bindingContext,

                             propertyDescriptor,

                             controllerType,

                             methodInfo,

                             remoteAttribute.AdditionalFields);

 

                           //如果验证失败,添加ModelError

 

                           if (validationResponse != null)

                           {

                               bindingContext.ModelState.AddModelError(propertyDescriptor.Name,

                                 validationResponse);

                           }

                       }

                   }

               }

           }

 

           base.BindProperty(controllerContext, bindingContext, propertyDescriptor);

       }

 

       /// This function calls the indicated method on a new instance of the supplied

       /// controller type and return the error string. (NULL if not)

       private string callRemoteValidationFunction(

         ControllerContext controllerContext,

         ModelBindingContext bindingContext,

         MemberDescriptor propertyDescriptor,

         Type controllerType,

         MethodInfo methodInfo,

         string additionalFields)

       {

 

           var propertyValue = controllerContext.RequestContext.HttpContext.Request.Form[

               bindingContext.ModelName + propertyDescriptor.Name];

 

           var controller = (Controller)Activator.CreateInstance(controllerType);

           object result = null;

           var parameters = methodInfo.GetParameters();

           if (parameters.Length == 0)

           {

               result = methodInfo.Invoke(controller, null);

           }

           else

           {

               var parametersArray = new List<string> {propertyValue};

 

               if (parameters.Length == 1)

               {

                   result = methodInfo.Invoke(controller, parametersArray.ToArray());

               }

               else

               {

                   if (!string.IsNullOrEmpty(additionalFields))

                   {

                       foreach (var additionalFieldName in additionalFields.Split(','))

                       {

                           string additionalFieldValue =

                               controllerContext.RequestContext.HttpContext.Request.Form[

                                 bindingContext.ModelName + additionalFieldName];

                           parametersArray.Add(additionalFieldValue);

                       }

 

                       if (parametersArray.Count == parameters.Length)

                       {

                           result = methodInfo.Invoke(controller, parametersArray.ToArray());

                       }

                   }

               }

           }

 

           if (result != null)

           {

               return (((JsonResult)result).Data as string);

           }

           return null;

       }

 

       /// Returns a list of all Controller types

       private static IEnumerable<Type> GetControllerNames()

       {

           var controllerNames = new List<Type>();

           GetSubClasses<Controller>().ForEach(controllerNames.Add);

           return controllerNames;

       }

 

       private static List<Type> GetSubClasses<T>()

       {

           return Assembly.GetCallingAssembly().GetTypes().Where(

             type => type.IsSubclassOf(typeof(T))).ToList();

       }

 

   }

 

4. 在MVC项目中应Global.asax.cs用上CustomModelBinder

 

打开Global.asax.cs, 添加上这段代码

 

 

protected void Application_Start()

       {

           //修改MVC默认的Model Binder为CustomBinder

           ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

           ……

 

      }

 

5. 关闭客户端验证,看看效果

 

打开web.config文件,ClientValidationEnabled设置成false, 关闭客户端验证

 

 

<appSettings>

    <add key="webpages:Version" value="2.0.0.0" />

    <add key="webpages:Enabled" value="false" />

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

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

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

  </appSettings> 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网