最近拜读了博客园我一直很敬仰的大神几年前关于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(); } }
以上仅为个人遇见,有不对之处,还望斧正。