RemoteAttribute验证程序不会触发服务器端

时间:2011-03-22 14:44:20

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

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

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

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

我已经将RemoteAttribute子类化,并覆盖了我可以访问RouteData,RouteName和Routes的IsValid方法以及返回操作URL的GetUrl方法。我正在考虑使用反射调用动作并获得结果,以便我可以看到它是否有效,但是有没有内置的方法可以使用而无需借助反射?

5 个答案:

答案 0 :(得分:3)

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

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

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

Subclassed 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
            };
    }
}

答案 1 :(得分:1)

这是远程验证的预期行为。无法知道IsValid的实现是什么,所以它只返回true。如果您想要RemoteAttribute的服务器端验证,您应该像完成一样覆盖IsValid。

答案 2 :(得分:0)

远程验证器通常带有附加字段。这是本案的实施。

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

答案 3 :(得分:0)

如前所述,这是设计上的。

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

答案 4 :(得分:0)

问题是,删除属性需要需要Java验证,它不能在服务器端立即可用...换句话说,如果启用javascript验证,则可以正常工作,并且显示错误,如果禁用javascript并进行服务器验证,它将不会在modelstate上显示任何错误,这不是bug,它的功能是哈哈哈...嗯,这就是它的构建方式(在毫秒上)...

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等注入方式使用服务的控制器发表了评论。我做到了,它的工作原理...