【问题标题】:RemoteAttribute validator does not fire server-sideRemoteAttribute 验证器不会触发服务器端
【发布时间】:2011-07-20 13:33:30
【问题描述】:

似乎 ASP.NET MVC 3 中引入的 RemoteAttribute 验证器不会在服务器端进行验证,只能通过 JavaScript 进行验证。如果您在浏览器中关闭 JS,您会发现在模型绑定上,验证控制器操作(您在使用 RemoteAttribute 装饰模型属性时指定)将不会被命中。事实上,如果您检查 RemoteAttribute 的源代码,您会发现 IsValid 方法在所有情况下都只返回 true。

这似乎是一个很大的遗漏——我想大多数人会认为 RemoteAttribute 会像所有其他内置验证器一样工作,并在客户端和服务器端进行验证。相反,您必须在控制器操作中手动调用远程验证逻辑。

人们是否意识到这一点,是否有人试图解决这个问题?

我将 RemoteAttribute 子类化并重写了 IsValid 方法,在该方法中我可以访问 RouteData、RouteName 和 Routes 以及返回操作 URL 的 GetUrl 方法。我正在考虑使用反射来调用动作并获取结果,以便我可以查看它是否有效,但是是否有任何内置方法可以在不诉诸反射的情况下使用?

【问题讨论】:

  • "In fact, if you inspect the source code for the RemoteAttribute, you will find the IsValid methods just returns true in all cases." 这样做的原因是你应该用你自己的方法覆盖IsValid
  • aaronstannard.com/post/2010/12/07/… 这个链接应该对你有更多帮助。
  • 这是有道理的,但是您是否有“您应该使用自己的方法覆盖 IsValid”的来源。我在 MSDN 或任何地方都找不到任何东西。

标签: asp.net-mvc-3 data-annotations


【解决方案1】:

这是远程验证的预期行为。没有办法知道 IsValid 的实现是什么,所以它只返回 true。如果你想对 RemoteAttribute 进行服务器端验证,你应该像你所做的那样覆盖 IsValid。

【讨论】:

    【解决方案2】:

    也许它不是最好的代码。如果您有一些建议,请告诉我。 开发@MVC4

    具有自定义属性的模型属性

    [CustomRemote("ValidateIP", "Validation", ErrorMessage = "Input is not a valid IP")]
    

    子类 RemoteAttribute

    /// <summary>
    /// Custom Remote Attribute for Client an Server validation.
    /// </summary>
    public class CustomRemoteAttribute : RemoteAttribute
    {
        /// <summary>
        /// List of all Controllers on MVC Application
        /// </summary>
        /// <returns></returns>
        private static List<Type> GetControllerList()
        {
            return Assembly.GetCallingAssembly().GetTypes().Where(type => type.IsSubclassOf(typeof(Controller))).ToList();
        }
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        protected CustomRemoteAttribute()
        {
        }
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public CustomRemoteAttribute(string routeName)
            : base(routeName)
        {
        }
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public CustomRemoteAttribute(string action, string controller)
            : base(action, controller)
        {
        }
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public CustomRemoteAttribute(string action, string controller, string areaName)
            : base(action, controller, areaName)
        {
        }
    
        /// <summary>
        /// Overridden IsValid function
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // Find the controller passed in constructor
            var controller = GetControllerList().FirstOrDefault(x => x.Name == string.Format("{0}Controller", this.RouteData["controller"]));
            if (controller == null)
            {
                // Default behavior of IsValid when no controller is found.
                return ValidationResult.Success;
            }
    
            // Find the Method passed in constructor
            var mi = controller.GetMethod(this.RouteData["action"].ToString());
            if (mi == null)
            {
                // Default behavior of IsValid when action not found
                return ValidationResult.Success;
            }
    
            // Create instance of the controller to be able to call non static validation method
            var instance = Activator.CreateInstance(controller);
    
            // invoke the method on the controller with value
            var result = (JsonResult)mi.Invoke(instance, new object[] { value });
    
            // Return success or the error message string from CustomRemoteAttribute
            return (bool) result.Data ? ValidationResult.Success : new ValidationResult(base.ErrorMessageString);
        }
    }
    

    验证控制器代码

    /// <summary>
    /// Controller for Client and Server validation
    /// <remarks>disable OutputCache</remarks>
    /// </summary>
    [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
    public class ValidationController : Controller
    {
        /// <summary>
        /// !!!!!!!!!!!!!!!!!! Needed for instance creation in custom attribute !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        /// </summary>
        public ValidationController()
        {
        }
    
        /// <summary>
        /// IP regex pattern of my choice
        /// </summary>
        const string IpPattern = @"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
    
        /// <summary>
        /// MAC regex pattern of my choice
        /// </summary>
        const string MacPattern = "^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$";
    
        /// <summary>
        /// Validate IP
        /// </summary>
        /// <param name="ip">IP param is only submited on Serverside validation!!!</param>
        /// <returns>Validation Result</returns>
        public JsonResult ValidateIP(string ip)
        {
            // Check if ip and httpcontext is null to dodge NullReferenceException on Server side validation
            if (string.IsNullOrEmpty(ip) && HttpContext == null)
            {
                return Json(false, JsonRequestBehavior.AllowGet);
            }
    
            /* Use IP on Serverside validation 
             * Use Querystring Param 0 to get IP from Client side vaildation without the need for the correct Id of input control */
            string checkip = string.IsNullOrEmpty(ip) ? HttpContext.Request.QueryString[0] : ip;
            if (string.IsNullOrEmpty(checkip))
            {
                return new JsonResult { Data = true, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
            }
    
            return new JsonResult
                {
                    Data = Regex.IsMatch(checkip, IpPattern),
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
        }
    }
    

    【讨论】:

    • 很好,但是如果我将项目中的代码和其他项目中的模型分开,这将导致控制器组件将不同,如果您知道一种方法来获取主程序的程序集打电话
    【解决方案3】:

    远程验证器通常带有附加字段。这是此案例的实现。

    /// <summary>
    /// Remote Attribute for Client an Server validation.
    /// </summary>
    public class RemoteWithServerSideAttribute : RemoteAttribute
    {
        /// <summary>
        /// List of all Controllers on MVC Application
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<Type> GetControllerList()
        {
            return Assembly.GetCallingAssembly().GetTypes().Where( type => type.IsSubclassOf( typeof(    Controller ) ) ).ToList();
        }
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        protected RemoteWithServerSideAttribute() {}
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public RemoteWithServerSideAttribute( string routeName ) : base( routeName ) {}
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public RemoteWithServerSideAttribute( string action, string controller ) : base( action, controller ){}
    
        /// <summary>
        /// Constructor of base class.
        /// </summary>
        public RemoteWithServerSideAttribute( string action, string controller, string areaName ) : base( action, controller, areaName ) {}
    
        /// <summary>
        /// Overridden IsValid function
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult IsValid( object value, ValidationContext validationContext )
        {
            // Find the controller passed in constructor
            var controller = GetControllerList().FirstOrDefault( x => x.Name == string.Format( "{0}Controller", this.RouteData["controller"] ) );
            if ( controller == null )
            {
                // Default behavior of IsValid when no controller is found.
                return ValidationResult.Success;
            }
    
            // Find the Method passed in constructor
            var mi = controller.GetMethod( this.RouteData["action"].ToString() );
            if ( mi == null )
            {
                // Default behavior of IsValid when action not found
                return ValidationResult.Success;
            }
    
            // Create instance of the controller to be able to call non static validation method
            var instance = Activator.CreateInstance( controller );
    
            // invoke the method on the controller with value and "AdditionalFields"
            JsonResult result;
            if ( !string.IsNullOrWhiteSpace( AdditionalFields ) )
            {
                var additionalField = validationContext.ObjectType.GetProperty( AdditionalFields )
                    .GetValue( validationContext.ObjectInstance );
                result = (JsonResult) mi.Invoke( instance, new [] { value, additionalField } );
            }
            else
                result = (JsonResult)mi.Invoke( instance, new [] { value } );
    
            // Return success or the error message string from CustomRemoteAttribute
            string errorMessaqe = result.Data as string;
            if (errorMessaqe == null)
            {
                bool isValid;
                try
                {
                    isValid = (bool) result.Data;
                }
                catch (Exception)
                {
                    isValid = false;
                }
                return isValid ? ValidationResult.Success : new ValidationResult( base.ErrorMessageString );
            }
            else
                return new ValidationResult( errorMessaqe );    
        }
    }
    

    【讨论】:

      【解决方案4】:

      如前所述,这是设计使然。

      我刚刚偶然发现了一篇关于 CodeProject 的好文章 - http://www.codeproject.com/Articles/361113/Extending-the-MVC-RemoteAttribute-to-validate-ser

      【讨论】:

        【解决方案5】:

        问题是删除属性 需要 Javascript 验证,它在服务器端不能开箱即用......换句话说,如果你启用 javascript 验证,它会工作并且显示错误,如果您禁用 javascript 并转到服务器验证,它根本不会在模型状态上显示任何错误,这不是错误,它是一个功能哈哈哈......好吧,这就是它的构建方式(毫秒)...

        https://docs.devexpress.com/AspNet/17294/aspnet-mvc-extensions/data-editors-extensions/common-concepts/validation/remote-validation#requirements

        如果您需要一个在客户端和服务器端都有效的远程验证属性,您必须创建自己的继承 RemoteAttribute 的类,然后您必须部署自己的验证,请查看以下视频:https://www.youtube.com/watch?v=qopOqppDwc4&t=110s

        视频几乎是正确的,我确实为通过 dbcontext 等注入使用服务的控制器发表了评论......我做到了,它有效......

        【讨论】:

          猜你喜欢
          • 2014-09-30
          • 1970-01-01
          • 2017-11-04
          • 1970-01-01
          • 1970-01-01
          • 2021-01-16
          • 1970-01-01
          • 2010-10-16
          • 2013-02-27
          相关资源
          最近更新 更多