How to force MVC to Validate IValidatableObject - validation

It seems that when MVC validates a Model that it runs through the DataAnnotation attributes (like required, or range) first and if any of those fail it skips running the Validate method on my IValidatableObject model.
Is there a way to have MVC go ahead and run that method even if the other validation fails?

You can manually call Validate() by passing in a new instance of ValidationContext, like so:
[HttpPost]
public ActionResult Create(Model model) {
if (!ModelState.IsValid) {
var errors = model.Validate(new ValidationContext(model, null, null));
foreach (var error in errors)
foreach (var memberName in error.MemberNames)
ModelState.AddModelError(memberName, error.ErrorMessage);
return View(post);
}
}
A caveat of this approach is that in instances where there are no property-level (DataAnnotation) errors, the validation will be run twice. To avoid that, you could add a property to your model, say a boolean Validated, which you set to true in your Validate() method once it runs and then check before manually calling the method in your controller.
So in your controller:
if (!ModelState.IsValid) {
if (!model.Validated) {
var validationResults = model.Validate(new ValidationContext(model, null, null));
foreach (var error in validationResults)
foreach (var memberName in error.MemberNames)
ModelState.AddModelError(memberName, error.ErrorMessage);
}
return View(post);
}
And in your model:
public bool Validated { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// perform validation
Validated = true;
}

There's a way to do it without requiring boilerplate code at the top of each controller action.
You'll need to replace the default model binder with one of your own:
protected void Application_Start()
{
// ...
ModelBinderProviders.BinderProviders.Clear();
ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider());
// ...
}
Your model binder provider looks like this:
public class CustomModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
return new CustomModelBinder();
}
}
Now create a custom model binder that actually forces the validation. This is where the heavy lifting's done:
public class CustomModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
ForceModelValidation(bindingContext);
}
private static void ForceModelValidation(ModelBindingContext bindingContext)
{
var model = bindingContext.Model as IValidatableObject;
if (model == null) return;
var modelState = bindingContext.ModelState;
var errors = model.Validate(new ValidationContext(model, null, null));
foreach (var error in errors)
{
foreach (var memberName in error.MemberNames)
{
// Only add errors that haven't already been added.
// (This can happen if the model's Validate(...) method is called more than once, which will happen when
// there are no property-level validation failures.)
var memberNameClone = memberName;
var idx = modelState.Keys.IndexOf(k => k == memberNameClone);
if (idx < 0) continue;
if (modelState.Values.ToArray()[idx].Errors.Any()) continue;
modelState.AddModelError(memberName, error.ErrorMessage);
}
}
}
}
You'll need an IndexOf extension method, too. This is a cheap implementation but it'll work:
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
var i = 0;
foreach (var item in source)
{
if (predicate(item)) return i;
i++;
}
return -1;
}

Related

DI with parameters in Castle Windsor

I'm trying to resolve a dependency like this:
controller.ActionInvoker = kernel.Resolve<IActionInvoker>(controller.GetType());
It was previously registered in this way:
container.Register(
Component
.For<IActionInvoker>()
.ImplementedBy<WindsorActionInvoker>()
.UsingFactoryMethod(metho)
.LifestylePerWebRequest()
);
internal IActionInvoker metho(IKernel kernel,ComponentModel model, CreationContext context)
{
// here just for debugging and watching the variables in the factory method,
// I would instance WindsorActionInvoker passing the filters to inject.
throw new InvalidOperationException();
}
But I can't figure out how to get the parameter I passed to the resolve call in the factory method.
I need the Type I'm passing as parameter to pass it to one of the dependencies injected into the constructor of the concrete type.
What am I doing wrong?
If you must know, the purpose of this is to inject action filters directly into the action invoker (and therefore the controllers), instead of requiring them decorate a controller or the base controller, additionally, this lets me to inject parameters dynamically, which I can't do with attributes.
public class WindsorActionInvoker : ControllerActionInvoker
{
private readonly IList<IActionFilter> actionFilters;
private readonly IList<IAuthorizationFilter> authorizationFilters;
private readonly IList<IExceptionFilter> exceptionFilters;
private readonly IList<IResultFilter> resultFilters;
public WindsorActionInvoker(IList<IActionFilter> actionFilters, IList<IAuthorizationFilter> authorizationFilters, IList<IExceptionFilter> exceptionFilters, IList<IResultFilter> resultFilters)
{
if (actionFilters == null)
{
throw new ArgumentNullException("actionFilters");
}
if (authorizationFilters == null)
{
throw new ArgumentNullException("authorizationFilters");
}
if (exceptionFilters == null)
{
throw new ArgumentNullException("exceptionFilters");
}
if (resultFilters == null)
{
throw new ArgumentNullException("resultFilters");
}
this.actionFilters = actionFilters;
this.authorizationFilters = authorizationFilters;
this.exceptionFilters = exceptionFilters;
this.resultFilters = resultFilters;
}
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
FilterInfo filterInfo = base.GetFilters(controllerContext, actionDescriptor);
foreach (IActionFilter filter in actionFilters)
{
filterInfo.ActionFilters.Add(filter);
}
foreach (IAuthorizationFilter filter in authorizationFilters)
{
filterInfo.AuthorizationFilters.Add(filter);
}
foreach (IExceptionFilter filter in exceptionFilters)
{
filterInfo.ExceptionFilters.Add(filter);
}
foreach (IResultFilter filter in resultFilters)
{
filterInfo.ResultFilters.Add(filter);
}
return filterInfo;
}
}
Solved, I needed to pass either a dictionary or an anonymous type instead of just any object.
Replacing:
controller.ActionInvoker = kernel.Resolve<IActionInvoker>(controller.GetType());}
With
controller.ActionInvoker = kernel.Resolve<IActionInvoker>(new { loggerType = controller.GetType() });
Fixed it.
:)

ViewModel class-level validation

I want to validate my View Model in class-Level .
I am using a actionFilter. How do I use a data annotation?
and how to inject the Access database?
A validation that would happen if the customer says it is already our customer or not.
I used action filter but I think it must have a way to use a DataAnnotation
Commented the code follows:
public class DadosAssinaturaFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.ActionParameters.Values.FirstOrDefault(x => x.GetType() == typeof(DadosAssinatura)) as DadosAssinatura;
var modelState = filterContext.Controller.ViewData.ModelState;
if (model != null)
{
var jaSouCliente = modelState.FirstOrDefault(x => x.Key == "JaSouCliente");
if (jaSouCliente.Key != null) // select "Is Clilent" radiobutton ?
if (jaSouCliente.Value.Errors.Count > 0) // if so remove the errors of the registration data
{
modelState.RemoveKeysStartsWith("DadosCliente.");
modelState.RemoveKeysStartsWith("DadosAcesso.");
}
else if (model.JaSouCliente != null && model.JaSouCliente.Value) // else, click in "Is Client"
{
modelState.RemoveKeysStartsWith("DadosCliente."); //remove
modelState.Remove("DadosAcesso.ConfirmaSenha"); //how injec UnitOfWor/Repository? AutoFac?
if (unitOfWork.Client.GetClientByUser(model.DadosAcesso.Usuario, model.DadosAcesso.Senha) == null)//user and Password
modelState.AddModelError("DadosAcesso.Usuario", "Usuario Nao Encontrado");
}
else if (model.DadosCliente.PessoaFisica) // is a company our people?
{
modelState.Remove("DadosCliente.RazaoSocial"); // remove validate for company name
modelState.Remove("DadosCliente.Cnpj"); //the brazilian document of company
}
else modelState.Remove("DadosCliente.Cpf"); //the brazilian document of people
}
base.OnActionExecuting(filterContext);
}
}
public static class ModelStateErros
{
public static void RemoveKeysStartsWith(this ModelStateDictionary modelStateDictionary, string startsWith)
{
var keys = modelStateDictionary.Keys.Where(key => key.StartsWith(startsWith)).ToList();
foreach (var variable in keys)
{
modelStateDictionary.Remove(variable);
}
}
}
sorry my English
Simply implement IValidateableObject in your ViewModel class (or create another partial class) and avoid the filter completely, and keep your validation logic with your ViewModel.
How do I use IValidatableObject?

Cast a dynamic attribute after it's posted (Model binding)

What I have is a model which has one of it's attributes dynamic. This dynamic attribute holds one of about 50 different objects. This model is send to a view that dynamic creates the page based on which object is used. This is working perfectly ... the issue is the postback. When the model posts back the modelbinder is not able to bind the dynamic attribute. I was expecting this and thought I would be able to handle it but nothing that I tried works appart from making an action for EACH different objects.
Model
public class VM_List
{
public Config.CIType CIType { get; set; }
public dynamic SearchData { get; set; }
//Lots of static fields
}
This works
public ActionResult List_Person(VM_List Model, VM_Person_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
public ActionResult List_Car(VM_List Model, VM_Car_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
But what I want is a single action
public ActionResult List(VM_List Model)
{
//Stuff
}
I have tried things like
public ActionResult List(VM_List Model)
{
switch (Model.CIType)
{
case Config.CIType.Person:
UpdateModel((VM_Person_List)Model.SearchData);
break;
default:
SearchData = null;
break;
}
//Stuff
}
and a Custom modelbinder
CIType CIType = (CIType)bindingContext.ValueProvider.GetValue("CIType").ConvertTo(typeof(CIType));
switch (CIType)
{
case Config.CIType.Person:
SearchData = (VM_Person_List)bindingContext.ValueProvider.GetValue("SearchData").ConvertTo(typeof(VM_Person_List));
break;
default:
SearchData = null;
break;
}
but I can't get either to work. Any ideas?
After trying many different things I finally found a way that works.
Action:
public ActionResult List(VM_List Model)
{
//If the defaultmodelbinder fails SearchData will be an object
if(Model.SearchData.GetType() == typeof(object))
{
//Get SearchData as a Dictionary
Dictionary<string, string> DSearchData = Request.QueryString.AllKeys.Where(k => k.StartsWith("SearchData.")).ToDictionary(k => k.Substring(11), k => Request.QueryString[k]);
switch (Model.CIType)
{
case Config.CIType.Person:
Model.SearchData = new VM_Person_List(DSearchData);
break;
case Config.CIType.Car:
Model.SearchData = new VM_Car_List(DSearchData);
break;
}
//Rest of action
//..
}
and for each object make a constructor that accepts a dictionary
public VM_Car_List(Dictionary<string, string> DSearchData)
{
this.Make = Convert.ToInt32(DSearchData["Make"]);
this.Model = Convert.ToInt32(DSearchData["Model"]);
this.Year = Convert.ToInt32(DSearchData["Year"]);
// ETC
}

Refactoring Switch statement in my Controller

I'm currently working on a MVC.NET 3 application; I recently attended a course by "Uncle Bob" Martin which has inspired me (shamed me?) into taking a hard look at my current development practice, particularly my refactoring habits.
So: a number of my routes conform to:
{controller}/{action}/{type}
Where type typically determines the type of ActionResult to be returned, e.g:
public class ExportController
{
public ActionResult Generate(String type, String parameters)
{
switch (type)
{
case "csv":
//do something
case "html":
//do something else
case "json":
//do yet another thing
}
}
}
Has anyone successfully applied the "replace switch with polymorhism" refactoring to code like this? Is this even a good idea? Would be great to hear your experiences with this kind of refactoring.
Thanks in advance!
The way I am looking at it, this controller action is screaming for a custom action result:
public class MyActionResult : ActionResult
{
public object Model { get; private set; }
public MyActionResult(object model)
{
if (model == null)
{
throw new ArgumentNullException("Haven't you heard of view models???");
}
Model = model;
}
public override void ExecuteResult(ControllerContext context)
{
// TODO: You could also use the context.HttpContext.Request.ContentType
// instead of this type route parameter
var typeValue = context.Controller.ValueProvider.GetValue("type");
var type = typeValue != null ? typeValue.AttemptedValue : null;
if (type == null)
{
throw new ArgumentNullException("Please specify a type");
}
var response = context.HttpContext.Response;
if (string.Equals("json", type, StringComparison.OrdinalIgnoreCase))
{
var serializer = new JavaScriptSerializer();
response.ContentType = "text/json";
response.Write(serializer.Serialize(Model));
}
else if (string.Equals("xml", type, StringComparison.OrdinalIgnoreCase))
{
var serializer = new XmlSerializer(Model.GetType());
response.ContentType = "text/xml";
serializer.Serialize(response.Output, Model);
}
else if (string.Equals("csv", type, StringComparison.OrdinalIgnoreCase))
{
// TODO:
}
else
{
throw new NotImplementedException(
string.Format(
"Sorry but \"{0}\" is not a supported. Try again later",
type
)
);
}
}
}
and then:
public ActionResult Generate(string parameters)
{
MyViewModel model = _repository.GetMeTheModel(parameters);
return new MyActionResult(model);
}
A controller should not care about how to serialize the data. That's not his responsibility. A controller shouldn't be doing any plumbing like this. He should focus on fetching domain models, mapping them to view models and passing those view models to view results.
If you wanted to "replace switch with polymorphism" in this case, you could create three overloaded Generate() ActionResult methods. Using custom model binding, make the Type parameter a strongly-typed enum called DataFormat (or whatever.) Then you'd have:
public ActionResult Generate(DataFormat.CSV, String parameters)
{
}
public ActionResult Generate(DataFormat.HTML, String parameters)
{
}
public ActionResult Generate(DataFormat.JSON, String parameters)
{
}
Once you get to this point, you can refactor further to get the repetition out of your Controller.

ASP MVC 2 Validation : Passing Javascript code to the client

I am writing a custom validation attribute
It does conditional validation between two fields
When I create my rule, one of the things that I could not solve is how to pass javascript code through ValidationParameters
Usually, I just do
ValidationParameters["Param1"] = "{ required :function(element) { return $("#age").val() < 13;) }"
However, the MicrosoftMvcJQueryValidation.js routines trnasforms this to
Param1 = "{ required :function(element) { return $("#age").val() < 13;) }"
I could use Param1.eval() in Javascript. This will evaluates and executes the code but I just want to evalute the code and execute it later
JSON parser does not parse string contening Javascript code
So I am asking here for any idea
Not sure how you would inject javascript as you describe, but you may want to consider using the custom validation pattern for ASP.NET MVC 2.
Important pieces are the ValidationAttribute, DataAnnotationsModelValidator, registering the validator in Application_Start with DataAnnotationsModelValidatorProvider.RegisterAdapter, and the client side Sys.Mvc.ValidatorRegistry.validators function collection to register your client side validation code.
Here's the example code from my post.
[RegularExpression("[\\S]{6,}", ErrorMessage = "Must be at least 6 characters.")]
public string Password { get; set; }
[StringLength(128, ErrorMessage = "Must be under 128 characters.")]
[MinStringLength(3, ErrorMessage = "Must be at least 3 characters.")]
public string PasswordAnswer { get; set; }
public class MinStringLengthAttribute : ValidationAttribute
{
public int MinLength { get; set; }
public MinStringLengthAttribute(int minLength)
{
MinLength = minLength;
}
public override bool IsValid(object value)
{
if (null == value) return true; //not a required validator
var len = value.ToString().Length;
if (len < MinLength)
return false;
else
return true;
}
}
public class MinStringLengthValidator : DataAnnotationsModelValidator<MinStringLengthAttribute>
{
int minLength;
string message;
public MinStringLengthValidator(ModelMetadata metadata, ControllerContext context, MinStringLengthAttribute attribute)
: base(metadata, context, attribute)
{
minLength = attribute.MinLength;
message = attribute.ErrorMessage;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule
{
ErrorMessage = message,
ValidationType = "minlen"
};
rule.ValidationParameters.Add("min", minLength);
return new[] { rule };
}
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinStringLengthAttribute), typeof(MinStringLengthValidator));
}
Sys.Mvc.ValidatorRegistry.validators["minlen"] = function(rule) {
//initialization
var minLen = rule.ValidationParameters["min"];
//return validator function
return function(value, context) {
if (value.length < minLen)
return rule.ErrorMessage;
else
return true; /* success */
};
};

Resources