ASP.NET MVC 3中引入的RemoteAttribute验证器似乎不在服务器端验证,只能通过JavaScript验证。如果在浏览器中关闭JS,您会发现在模型绑定上,验证控制器操作(在使用RemoteAttribute修饰模型属性时指定)将不会被命中。实际上,如果检查RemoteAttribute的源代码,您会发现IsValid方法在所有情况下都返回true。
这似乎是一个遗漏 - 我想大多数人会认为RemoteAttribute会像所有其他内置验证器一样工作,并在客户端和服务器端进行验证。相反,您必须在控制器操作中手动调用远程验证逻辑。
人们是否意识到这一点并且有人试图解决这个问题?
我已经将RemoteAttribute子类化,并覆盖了我可以访问RouteData,RouteName和Routes的IsValid方法以及返回操作URL的GetUrl方法。我正在考虑使用反射调用动作并获得结果,以便我可以看到它是否有效,但是有没有内置的方法可以使用而无需借助反射?
答案 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,它的功能是哈哈哈...嗯,这就是它的构建方式(在毫秒上)...
如果需要在客户端和服务器端都可以使用的远程验证属性,则必须创建继承RemoteAttribute的自己的类,然后必须部署自己的验证,请查看以下视频:https://www.youtube.com/watch?v=qopOqppDwc4&t=110s
视频几乎是正确的,我确实为使用dbcontext等注入方式使用服务的控制器发表了评论。我做到了,它的工作原理...