JSR-303 how to ignore validation on specific fields in a bean some of the time - bean-validation

I am validating a large bean. It is based of a dynamic form page. Some fields that are being validated are not visible on the form and hence empty or null. But I don't want the invisible fields to be validated. Sometimes they are visible and I want them to be validated, sometimes they are not visible and I don't want them to be validated. I first took the approach of stripping these fields from the serialized form before submitting. But it still validates the missing fields because they exist in the bean with validation tags. What is the right way to do what I am trying to do?

One possible approach is using validation groups. You define different validation rules for different groups. Afterwards you can call the validator just for one of these groups or for a set of groups.
public class TestBean {
#NotNull(groups= {Group1.class})
#Size.List({
#Size(min=1, groups= {Group1.class}),
#Size(min=0, groups= {Group2.class})
})
private String test;
}
public interface Group1 { }
public interface Group2 { }
then you can call the validator for one or more of these groups
Validator validator = ....;
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(objectToValidate, Group1.class);
For more information about validating groups see here.

Related

Add record to table with FK

I have a table UserStoreName,
Columns are :
int Id
string UserNameId (as a FK of the table AspNetUsers (Column Id))
sring StoreName
I have a page AddStore, a very simple page where user just enter the store name into the StoreName Field.
I already know the UserNameId, i'm taking it from the User.
So when user populate the storeName field and click submit i just need to add a record to the table UserStoreName.
sounds easy.
when i click submit the AddStore function from the controller is giving me ModelState.IsValid = false.
reason for that is cause userNameId is a required field.
i want to populate that field in the AddStore
function but when we get there the modelState is already invalid because of a required field in userStoreNameId enter code here
Here is the AddStore in case it will help :
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddStore(UserStoreName userStoreName)
{
userStoreName.UserNameId =
(_unitOfWork.ApplicationUser.GetAll().Where(q => q.UserName == User.Identity.Name).Select(q => q.Id)).FirstOrDefault();
userStoreName.UserName = User.Identity.Name;
userStoreName.IsAdminStore = false;
if (ModelState.IsValid)
{
_unitOfWork.UserStoreName.Add(userStoreName);
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
return View(userStoreName);
}
Any idea what am i doing wrong? new to asp.net core mvc, its my first project.
Thanks :)
Thank you
If the UserNameId field is required, it must be supplied to pass model validation.
There are two ways around this. First, you could create a View Model, with just the fields you plan on actually submitting, and use it in place of the userStoreName variable. Then in the controller action, you can just instantiate a new UserStoreName object, and fill out the fields.
Alternatively, you could pass the UserNameId variable to the view, and populate the model client side using a hidden field, so it passes validation when returned to the controller. Hidden fields can potentially have their values edited client-side, however, so it may be worth checking the value again server side, especially if there are any security implications.
Foreign keys can be nullable so just make sure the UserNameId field is not marked with the "[Required]" Data Annotation in your model.
You'll also need to make sure that the column is nullable on the UserStoreName table to match the model otherwise it'll cause problems if your model is different from its underlying table.
Just a small suggestion also, I wouldn't foreign key on strings, I would change your model foreign key to an int, and make sure that the column in the table it's related to is also an int. It's a lot safer to do so, especially if you're dealing with IDENTITY columns.
If there is anything wrong with the reference, an exception will throw when the code tries to save your change, usually because the value it has in the FK reference cannot be found in the related table.

Unique constraint at field in collection

is it possible to make this validation:
class Man {
#Unique
String name;
}
class Order {
#Valid
List<Man> manCollection;
}
where is unique logic is: every item in collection manCollection is unique.
You could make this snippet ambiguous just by adding a Customer class that contains a List of Orders:
class Man {
#Unique
String name;
}
class Order {
#Valid
List<Man> manCollection;
}
class Customer {
#Valid
List<Order> orderCollection;
}
Then one couldn't possibly know whether the Man objects must be unique within a given Order or within a given Customer (or both).
So I don't think it's possible with this exact syntax, regardless of what the Bean Validation APIs allow.
What you could do is move the annotation to manCollection, e.g. #UniqueMen List<Man> manCollection;, and implement a ConstraintValidator<List<Man>>.
If it's useful to you, you could even make a more generic #UniqueContent annotation, but that would be much more complex. You would need to pass the target type as a parameter (#UniqueContent(target = Man.class)) and write a validator that parses annotations on the target class in its initialize method. Be careful to use some caching mechanism, though, because annotation parsing is quite slow.

Bypass client side validation mvc 3

This is the model which we are using...
Public Class Person
{
[Display(ResourceType = typeof(BasicTags), Name = "FirstName")]
[Required(ErrorMessageResourceName = "FirstNameRequired", ErrorMessageResourceType = typeof(BasicErrors))]
public string FirstName;
[Display(ResourceType = typeof(BasicTags), Name = "LastName")]
[Required(ErrorMessageResourceName = "LastNameRequired", ErrorMessageResourceType = typeof(BasicErrors))]
public string LastName;
}
Both the fields are set as required true. Now we have another developer using this same Model in another view, he doesn`t want this validation for his page, how to skip the validation in the server side and before saving?
The database fields are set as allow null.
ViewData.ModelState.Remove("FirstName")
ViewData.ModelState.Remove("LastName")
This only removes the client side message but the actual validation still remains. Is there any way, so that I can save.
Thanks.
You should make a custom View Model that has these properties but doesn't have their validation annotations for that specific page.
Simply don't check ModelState.IsValid on the server side and save your data.
HOWEVER - I would just make a copy of that view model, remove your attributes and be done with it. A ViewModel is for a View - if you have a different view, the standard thing to do is create a new Model. However - its your app - so the solution to do what you wanted is above.
If you are worried about the client side validation, then you have to create your own handler for the submit function and don't check if its valid - just post. Another hack. So - again.. try not to do it this way. : )
You can also call ModelState.Remove("FirstName");
Before ModelState.IsValid and it will do the trick, Like
ModelState.Remove("FirstName");
if(ModelState.IsValid){
// your code
}

Prevent value to be preserved in ModelState

Good day!
ASP.NET MVC makes a good job by storing values of inputs during GET/POST cycle inside ModelState and automagically putting them into inputs in case of validation errors.
But on my form I have CAPTCHA field which shouldn't be preserved during validation errors (CAPTCHA value is regenerated on each request).
I've tried to achieve this by setting
if (TryUpdateModel(model))
{
// ...
}
else
{
ModelState.Remove("CaptchaValue"); // ModelState does have CaptchaValue
return View(model); // CaptchaValue is empty in model
}
But it doesn't work.
May be there is an attribute which I can apply to my model field to prevent it from preserve in ModelState?
Thanks in advance!
You can use the bind attribute on the action parameter to control model binding behaviour:
public ActionResult YourActionName([Bind(Exclude = "CaptchaValue")]ModelType model)
I've found this in nearby thread MVC - How to change the value of a textbox in a post?:
ModelState.SetModelValue("CaptchaValue", new ValueProviderResult(String.Empty, String.Empty, System.Threading.Thread.CurrentThread.CurrentCulture));
But it seems to be a bit ugly.

ASP.NET MVC model binding and validation question

I'm trying to use MVC for a new project after having been around the block with all the samples and tutorials and such. However, I'm having a hard time figuring out where certain things should take place.
As an example, I have an entity called Profile. This entity contains the normal profile type stuff along with a DateOfBirth property that is of type DateTime. On the HTML form, the date of birth field is split into 3 fields. Now, I know I can use a custom model binder to handle this, but what if the date entered is not a valid date? Should I be checking for that in the model binder? Should all my validation go in the model binder? Is it ok to have only a few things validated in the model binder and validate the rest in the controller or the model itself?
Here's the code I have now, but it just doesn't look right to me. Seems dirty or smelly.
namespace WebSite.Models
{
public class ProfileModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DateTime birthDate;
var form = controllerContext.HttpContext.Request.Form;
var state = controllerContext.Controller.ViewData.ModelState;
var profile = new Profile();
profile.FirstName = form["FirstName"];
profile.LastName = form["LastName"];
profile.Address = form["Address"];
profile.Address2 = form["Address2"];
profile.City = form["City"];
profile.State = form["State"];
profile.Zip = form["Zip"];
profile.Phone = form["Phone"];
profile.Email = form["Email"];
profile.Created = DateTime.UtcNow;
profile.IpAddress = controllerContext.HttpContext.Request.UserHostAddress;
var dateTemp = string.Format("{0}/{1}/{2}",
form["BirthMonth"], form["BirthDay"], form["BirthYear"]);
if (string.IsNullOrEmpty(dateTemp))
state.AddModelError("BirthDate", "Required");
else if (!DateTime.TryParse(dateTemp, out birthDate))
state.AddModelError("BirthDate", "Invalid");
else
profile.BirthDate = birthDate;
return profile;
}
}
}
Building on the sample code above, how would you do the validation message for a 3 part field? In the case above, I'm using a completely separate key that doesn't actually correspond to a field in the form, because I don't want an error message to appear beside all 3 fields. I only want it to appear to the right of the Year field.
I think it is reasonable to do validation in the model binder. As Craig points out, the validation is mostly the property of your business domain, however:
Sometimes your model is just a dumb presentation model, not a business object
There are various mechanisms you can use to surface the validation knowledge into the model binder.
Thomas gives you an exmaple of #1.
An example of #2 is when you declaratively desribe validation knowlege using attributes (like the DataAnnotation attribute [Required]), or inject some business layer validation service into a custom model binder. In these situations the model binder is an ideal place to take care of validation.
That being said, model binding (finding, converting, and shuffling data into an object) and validation (data meets our specifications) are two seperate concerns. You could argue that they should be seperate phases/components/extensibility points, but we have what we have, although the DefaultModelBinder makes some distinction between these two responsibilities. If all you want to do is provide some validation for a specific type of object you can derive from the DefaultModelBinder and override the OnPropertyValidating method for property level validations or OnModelUpdated if you need the holistic view.
Here's the code I have now, but it
just doesn't look right to me. Seems
dirty or smelly.
For your specific code I would try to write a model binder for DateTime only. The default model binder can take care of binding firstname, lastname, etc., and delegate to your custom model binder when it reaches a DateTime property on the Profile. In addition, try using the valueProvider in the bindingContext instead of going directly to the form. These things can give you more flexibility.
More thoughts here: 6 Tips for ASP.NET MVC Model Binding.
Sometimes the model is a view-model, not a domain model. In this case you could benefit from separating those two and design the view model to match your view.
Now you can let the view model validate the input and parse the three fields into a DateTime. Then it can update the domain model:
public ActionResult SomeAction(ViewModel vm)
{
if (vm.IsValid)
{
var dm = repositoryOrSomething.GetDomainModel();
vm.Update(dm);
}
// more code...
}
I had the same exact situation the other day...below is my model binding code. Basically it binds all the DateTime? fields of a model to month/day/year fields from a form (if possible) So, yes, I do add in the validation here, since it does seem appropriate to do so.
public class DateModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.PropertyType == typeof(DateTime?))
{
string DateMonth = _GetDateValue(bindingContext, propertyDescriptor.Name + "Month");
string DateDay = _GetDateValue(bindingContext, propertyDescriptor.Name + "Day");
string DateYear = _GetDateValue(bindingContext, propertyDescriptor.Name + "Year");
// Try to parse the date if we have at least a month, day or year
if (!String.IsNullOrEmpty(DateMonth) || !String.IsNullOrEmpty(DateDay) || !String.IsNullOrEmpty(DateYear))
{
DateTime fullDate;
CultureInfo enUS = new CultureInfo("en-US");
// If we can parse it, set the model property
if (DateTime.TryParse(DateMonth + "/" + DateDay + "/" + DateYear,
enUS,
DateTimeStyles.None, out fullDate))
{
SetProperty(controllerContext, bindingContext, propertyDescriptor, (DateTime?)fullDate);
}
// The date is invalid, so we need to add a model error
else
{
string ModelPropertyName = bindingContext.ModelName;
if(ModelPropertyName != "")
{
ModelPropertyName += ".";
}
ModelPropertyName += propertyDescriptor.Name;
bindingContext.ModelState.AddModelError(ModelPropertyName, "Invalid date supplied for " + propertyDescriptor.Name);
}
}
return;
}
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
// Get a property from binding context
private string _GetDateValue(ModelBindingContext bindingContext, string key)
{
ValueProviderResult valueResult;
bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName + "." + key, out valueResult);
//Didn't work? Try without the prefix if needed...
// && bindingContext.FallbackToEmptyPrefix == true
if (valueResult == null)
{
bindingContext.ValueProvider.TryGetValue(key, out valueResult);
}
if (valueResult == null)
{
return null;
}
return (string)valueResult.ConvertTo(typeof(string));
}
}
Note: I had some problems with bindingContext.FallbackToEmptyPrefix always being false...can't find any useful info on that, but you get the idea.
Validation should be done in multiple places, according to the functionality of each place. For example, if your model binder cannot find the submitted values into a proper DateTime value, then the binder can add a model state error. If, on the other hand, your business logic requires the date to be within a certain range, this would not be appropriate to do and the model binder; it should be in the business logic layer. Controllers can potentially add validation errors as well if, for example, the edit model cannot be transformed into an entity model.
A validation framework such as xVal makes this much simpler.
The Contact Manager sample application on the http://www.asp.net/mvc site has an excellent description of separating out your validation logic into a service layer from your controller and model.
It's well work a read
I tired of creating little small-purpose ViewModels that only touched parts of my mile-wide domain model.
Thus, I cooked up my own method for addressing this. My ViewModel is a typeOf DomainModel, and I use a custom model binder to ensure its identity properties load first - once identity is set - it triggers a DomainModel.Load, and the remainder of the binding activity essentially performs a 'merge'.
Again, when my ViewModel is bound (e.g. on a form POST), after essential fields comprising the ID are set - it immediately loads up the domain model from the database. I just had to come up with a replacment for the DefaultModelBinder. My custom model binder posted here on StackOverflow allows you to control the binding order of the properties.
Once I can guarantee that identity properties are bound, (the internals of my viewmodel listen for the completion of identity setters) I trigger a load of my domain model, as the rest of the properties are bound, they are overwriting, i.e. 'merging' into the loaded domain model.
Basically, I can have all my various razor views, whether they expose 5 form fields or 50 fields of the model.. all submit to a controller action that looks like this (granted, I still make separate actions where needed to do appropriate custom business stuff.. but the point is, my controller actions are focused and succinct)
<HttpPost()>
<Authorize(Roles:="MYCOMPANY\activeDirRoleForEditing")>
Function Edit(<Http.FromBody()> ByVal mergedModel As OrderModel) As ActionResult
'notice: NO loading logic here - it already happened during model binding
'just do the right thing based upon resulting model state
If Me.ModelState.IsValid Then
mergedModel.SaveAndReload("MyServiceWebConfigKey")
ViewBag.SuccessMessage = String.Format("You have successfully edited the order {0}", mergedModel.Id)
Return View("Edit", mergedModel)
Else
ViewBag.ErrorText = String.Format("Order {0} not saved. Check for errors and correct.", mergedModel.Id)
Return View("Edit", mergedModel)
End If
End Function

Resources