ASP.NET MVC : DataAnnotation validation execution order - validation

I'm having some trouble understanding validation logic behing DataAnnotation validation :
With the following model :
[AlwaysInvalid]
public class TestModel
{
[Required]
public string Test { get; set; }
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class AlwaysInvalidAttribute : ValidationAttribute
{
private readonly object typeId = new object();
public AlwaysInvalidAttribute() : base("Fail !") {}
public override object TypeId { get { return this.typeId; } }
public override bool IsValid(object value)
{
return false;
}
}
The AlwaysInvalidAttribute error message gets displayed only if the Required attribute is valid : I can't get both messages at the same time. Anyone got an idea why ? I think it's an issue with DefaultModelBinder, but still haven't found where, or why.

Class-level validators only run if all the property-level validators were successful. This behavior is coded up in the ModelValidator class.

Related

How to access property of parent in validation attribute

In my code below I want to check with AttributeValidation if a field is given dependent on a property of its parent element. The comment in the class
RequiredIfParentState1
describes my question best.
public class ChildModel()
{
[RequiredIfParentState1]
public string ImRequired { get; set; }
}
public class ParentViewModel()
{
public int state { get; set; }
public ChildModel child = new ChildModel();
}
public class RequiredIfParentState1: ValidationAttribute, IClientModelValidator
{
RequiredIfParentState1()
{
}
void AddValidation(ClientModelValidationContext context)
{
}
protected override ValidationResult IsValid(object i_value, ValidationContext i_context)
{
var element = i_context.ObjectInstance;
if(i_value == null && //what do i have to put here to check if the state is 1?)
{
return new ValidationResult($"Field is Required in state 1.");
}
return ValidationResult.Success;
}
}
I feel this is the wrong approach.
An object being in a valid state is one thing (required fields and type checking), but handling business logic is a separate concern.
You could write a validation service, that examines the model in detail, checking business logic concerns, and build up a list of errors.
Where errors are found you can return these in your response.

ModelState.Isvalid invalidating field even though default value existed

public abstract class Base : IBase
{
[Required]
public int key {get;set;}
}
public class Entity: Base
{
public string Name {get;set;}
}
public class child : Entity
{
[Required]
public string Park {get;set;}
}
ActionFilter
public class ValidateViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false) {
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Now, when value is posted to API then, not setting "Key" field as it is the request for SAVE. Problem is, above attribute says, MODEL IS INVALID for field "key" . Its already there as 0 value for Id field (as default int).
I expect, it should validate true as 0 is default value.
NOTE: I could not remove or make any change in BASEENTITY and PARENT entity above.
I have only control in CHILD entity and this attribute class.
To ignore a property that is marked as [Required] you can use ModelState.Remove("propertyName");
Also, your property has a value of 0 because an int cannot have a value of NULL so the 0 is automatically attributed. But if you did not pass this value in the form data, the model validation will "consider" that it is NULL and thus will make the model invalid. If you do not want to use the call to Remove as shown above, you will have to explicitly give a value to the Key property :-)
source: The first comment on this page - credit for this explanation #Stephen Muecke
Use something like
public class ValidateViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
ModelState.Remove("key");
if (actionContext.ModelState.IsValid == false) {
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Note: By default, MVC6 model validation will simplicity tag all non-nullable value types as required (god knows why).
call
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
disable this behaviour.
I have the same problem and solved it by this way :
[AcceptVerbs("Post")]
public ActionResult EditingInline_Create([DataSourceRequest] DataSourceRequest request, Model.Server server)
{
if (server != null && ModelState.IsValid) //Invalid!!
{
_exchangeService.Create(server);
}
return Json(new[] { server }.ToDataSourceResult(request, ModelState));
}
and replace model with viewModel ,because in viewModel we don`t have ID (refer to Use ViewModel)
then we will have :
[AcceptVerbs("Post")]
public ActionResult EditingInline_Create([DataSourceRequest] DataSourceRequest request, ViewModel.ServerViewModel server)
{
if (server != null && ModelState.IsValid)
{
_exchangeService.Create(server);
}
return Json(new[] { server }.ToDataSourceResult(request, ModelState));
}

Web API - Access Custom Attribute Properties inside ActionFilterAttribute OnActionExecuting

I need to access a property inside a custom DataAnnotation attribute. How can I access this attribute in order to set the response value? The attribute is added to the model property.
public class BirthDateAttribute : ValidationAttribute
{
public string ErrorCode { get; set; }
....
}
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
List<Errors> errors = new List<Errors>();
// Set error message and errorCode
foreach (var key in keys)
{
if (!actionContext.ModelState.IsValidField(key))
{
error.Add(new HttpResponseError
{
Code = ???????????,
Message = actionContext.ModelState[key].Errors.FirstOrDefault().ErrorMessage
});
}
}
// Return to client
actionContext.Response = actionContext.Request.CreateResponse(
HttpStatusCode.BadRequest, errors);
}
}
}
Assuming that the custom attribute is applied to the controller, you can try following in the OnActionExecuting event. This similar thing works with MVC controller but should work with API controller too.
var att = actionContext.ControllerContext.GetType().GetCustomAttributes(typeof(BirthDateAttribute), false)[0] as BirthDateAttribute;
string errorCode = att.ErrorCode;
As mentioned by OP, if this is on a class (Model), it should be pretty starightforward because the type is already known. Replace the Model class.
var att = <<ModalClass>>.GetCustomAttributes(typeof(BirthDateAttribute), false)[0] as BirthDateAttribute;
string errorCode = att.ErrorCode;

Injecting dependencies into MVC3 filters

I've been having a heck of a time trying to get dependencies injected into a custom authorization filter.
OutletService (this is a service I'm trying to inject into my filter)
public class OutletService : IOutletService
{
#region Fields
private readonly IRepository<Outlet> _outletRepository;
#endregion
#region Ctor
public OutletService(IRepository<Outlet> outletRepository)
{
_outletRepository = outletRepository;
}
#endregion
// Rest of class omitted
CustomAuthorizeAttribute (partial, name changed for this example also)
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private IOutletService _outletService;
private IModuleService _moduleService;
public string Action { get; set; }
public int Level { get; set; }
public MarcusAuthorizeAttribute()
{
}
[Inject]
public MyAuthorizeAttribute(IOutletService outletService, IModuleService moduleService)
{
_outletService = outletService;
_moduleService = moduleService;
}
I tried using this post as an example, but as soon as I wire it up, none of my routes seem to work (IIS Express returns a 401/cannot find?)
Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?
If anyone has any ideas or suggestions, I'd appreciate it! (It's literally driving me up a wall now!)
Thanks!
Ninject's MVC extension has a mechanism for injecting dependencies into filters, which is described in the documentation here.
You may try this
Filter
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private IOutletService _outletService;
private IModuleService _moduleService;
public string Action { get; set; }
public int Level { get; set; }
public MarcusAuthorizeAttribute()
{
_outletService = DependencyResolver.Current.GetService<IHelloService>();
_moduleService = DependencyResolver.Current.GetService<IModuleService>();
}
}
Make sure you register your services with dependency resolver you are using.

Implementing Unique Contraint with ValidateEntity gives "The given key was not present in the dictionary" error

While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:
public class Category : IValidatableObject
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var testContext = (TestContext)validationContext.Items["Context"];
if (testContext.Categories.Any(
c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
{
yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
}
yield break;
}
}
and overriding DbEntityValidationResult ValidateEntity:
public class TestContext : DbContext
{
public DbSet<Test.Models.Category> Categories { get; set; }
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
}
And the action on the controller
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid) {
categoryRepository.InsertOrUpdate(category);
categoryRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
But I get the error: "The given key was not present in the dictionary." for the line
var testContext = (TestContext)validationContext.Items["Context"];
It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.
At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.
Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.
Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.
I was facing the same problem... Then after a lot of Googling I finally found this:
Exercise 3: Using IValidatableObject Custom Validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MusicStoreEntities storeDB = new MusicStoreEntities();
if (storeDB.Albums.Any(
a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
a.ArtistId == (int)this.ArtistId))
{
yield return new ValidationResult("Existing Album", new string[] { "Title" });
}
}
As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.

Resources