Reputation: 11773
I am building a custom validator for my MVC4 application. To keep things simple and focus on the purpose of this question let's say that I'm reimplementing the RequiredAttribute
class's server and client side validation:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MyRequiredAttribute: ValidationAttribute, IClientValidatable
{
public MyRequiredAttribute() : base("The {0} field is required.")
{
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRequiredRule(ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()));
}
}
To test this I have a very simple controller:
public class TestController : Controller
{
public ActionResult Index()
{
return View(new MyThing {Value = "whatever"});
}
[HttpPost]
public ActionResult Index(MyThing thing)
{
if (ModelState.IsValid)
{
return Content("Good choice!");
}
return View(thing);
}
}
and a very simple model:
public class MyThing
{
[MyRequired]
public string Value { get; set; }
[Required]
public string OtherValue { get; set; }
}
and finally the Index View:
@model MyThing
@{
Layout = "~/Views/Shared/_LayoutWithAllTheAppropriateValidationScripts.cshtml";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
@using(Html.BeginForm()){
@Html.ValidationSummary(true)
@Html.EditorForModel()
<input type="submit" value="submit"/>
}
</div>
</body>
</html>
You'll note that I have Value
which is decorated with the custom MyRequiredAttribute
and OtherValue
which is decorated with the standard RequiredAttribute
.
Both of these work beautifully server-side and return the appropriate validation messages on postback, but only the standard RequiredAttribute
works on the client side. Since I'm using a ModelClientValidationRequiredRule
instead of a ModelClientValidationRule
with a custom ValidationType
property I'm assuming that no custom clientside validation rules need to be defined, but I don't know that for sure because I haven't been able to get that far. The debugger never breaks inside of the GetClientValidationRules
method and the input html element generated for the Value
property on my model does not have the data-val-required
and data-val
attributes as does the OtherValue
field, which I'm assuming is what the GetClientValidationRules
method is responsible for doing.
I must be missing something simple here... what is it?
Upvotes: 1
Views: 3650
Reputation: 11773
This turned out to be user error, but I'll post the solution anyway in hopes that it helps someone else with a similar problem down the road.
The code in the original question is in a separate assembly from the MVC project that uses it. The MVC project had recently been upgraded from MVC3 to MVC4. I had followed the instructions provided by Microsoft on upgrading, or so I thought. I missed one piece in the web.config:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
This tells the MVC project how to play nice with assemblies on which it depends that were compiled with a reference to an earlier version of the System.Web.Mvc
assembly. After upgrading my MVC project it referenced System.Web.Mvc
v4 and my "Common" project (where the extended ValidatorAttribute
implementing IClientValidatable
lived) referenced v3. The configuration above should have been updated with the upgrade but it got missed. The issue did not manifest in any other way except for the client-side validation issue outlined in the original question.
The offending line here was
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0"/>
which should have been
<bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
which fixed the problem.
Upvotes: 3
Reputation: 11177
Add this to your Global.asax:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MyRequiredAttribute), typeof(RequiredAttributeAdapter));
This should solve your client-side validation issue.
Upvotes: 0