自定义Remote验证,对博客园文章“Asp.net MVC验证哪些事?

最近拜读了博客园我一直很敬仰的大神几年前关于asp.net mvc中自定义Remote验证的文章:

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

获益匪浅。但是文中给出的解决方案,个人觉得里面动态执行验证方法的那段代码有可以完善的地方(并没有半点对大神的诋毁,代码为下面标色部分):

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)
                        {
                            //调用方法,得到验证的返回结果
                            bool isValidate = callRemoteValidationFunction(
                            controllerContext,
                            bindingContext,
                            propertyDescriptor,
                            controllerType,
                            methodInfo,
                            remoteAttribute.AdditionalFields);
                            //如果验证失败,添加ModelError
                            if (!isValidate)
                            {
                                bindingContext.ModelState.AddModelError(propertyDescriptor.Name, remoteAttribute.ErrorMessage);
                            }
                        }
                    }
                }
            }
            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 bool 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<object>();
                if (!string.IsNullOrEmpty(additionalFields))
                {
                    int i = 0;
                    foreach (var field in additionalFields.Split(','))
                    {
                        //可以根据参数名字对应,这里根据参数顺序对应
                        string value = controllerContext.RequestContext.HttpContext
                                    .Request.Form[bindingContext.ModelName + field];
                        parametersArray.Add(Convert.ChangeType(value, parameters[i].ParameterType));
                        i++;
                    }
                    if (parametersArray.Count == parameters.Length)
                    {
                        result = methodInfo.Invoke(controller, parametersArray.ToArray());
                    }
                }
            }
            if (result != null)
            {
                return Convert.ToBoolean(((JsonResult)result).Data);
            }
            return false; ;
        }

        /// 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();
        }
    }

当然这段代码是没有任何问题的,也考虑到了验证方法包含多个参数的问题,但是有个问题就是:Remote验证方法的参数都被指定成了string类型,但是实际中,我们不能保证我们的验证方法的参数都是string类型。这里给出自己改进的代码部分

var parametersArray = new List<object>();
if (!string.IsNullOrEmpty(additionalFields))
{
    int i = 0;
    foreach (var field in additionalFields.Split(','))
    {
        //可以根据参数名字对应,这里根据参数顺序对应
        string value = controllerContext.RequestContext.HttpContext
                    .Request.Form[bindingContext.ModelName + field];
        parametersArray.Add(Convert.ChangeType(value, parameters[i].ParameterType));
        i++;
    }
    if (parametersArray.Count == parameters.Length)
    {
        result = methodInfo.Invoke(controller, parametersArray.ToArray());
    }
}

上面是根据验证参数顺序来对应的,当然,你也可以根据参数名称来对应了。下面是调用方法:

[Required]
[CustomRemote("CheckMMSRecord", "MMSRecord", AdditionalFields = "CustPhone,Type,VCode", ErrorMessage = "请输入正确的手机验证码")]
public string VCode { get; set; }

验证方法定义为:

public JsonResult CheckMMSRecord2(string Mobile, int Type, string VCode)

CustomModelBinder完整如下:

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)
                        {
                            //调用方法,得到验证的返回结果
                            bool isValidate = callRemoteValidationFunction(
                            controllerContext,
                            bindingContext,
                            propertyDescriptor,
                            controllerType,
                            methodInfo,
                            remoteAttribute.AdditionalFields);
                            //如果验证失败,添加ModelError
                            if (!isValidate)
                            {
                                bindingContext.ModelState.AddModelError(propertyDescriptor.Name, remoteAttribute.ErrorMessage);
                            }
                        }
                    }
                }
            }
            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 bool 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<object>();
                if (!string.IsNullOrEmpty(additionalFields))
                {
                    int i = 0;
                    foreach (var field in additionalFields.Split(','))
                    {
                        //可以根据参数名字对应,这里根据参数顺序对应
                        string value = controllerContext.RequestContext.HttpContext
                                    .Request.Form[bindingContext.ModelName + field];
                        parametersArray.Add(Convert.ChangeType(value, parameters[i].ParameterType));
                        i++;
                    }
                    if (parametersArray.Count == parameters.Length)
                    {
                        result = methodInfo.Invoke(controller, parametersArray.ToArray());
                    }
                }
            }
            if (result != null)
            {
                return Convert.ToBoolean(((JsonResult)result).Data);
            }
            return false; ;
        }

        /// 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();
        }
    }

以上仅为个人遇见,有不对之处,还望斧正。