zoukankan      html  css  js  c++  java
  • Remote验证及其改进(转自红黑联盟)

    一, 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> 
  • 相关阅读:
    IOS Charles(代理服务器软件,可以用来拦截网络请求)
    Javascript中addEventListener和attachEvent的区别
    MVC中实现Area几种方法
    Entity Framework Code First 中使用 Fluent API 笔记。
    自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案
    Code First :使用Entity. Framework编程(8) ----转发 收藏
    Code First :使用Entity. Framework编程(6) ----转发 收藏
    Code First :使用Entity. Framework编程(5) ----转发 收藏
  • 原文地址:https://www.cnblogs.com/swii/p/5563028.html
Copyright © 2011-2022 走看看