I'm trying to set a default value to a field with FluentValidation when the provided value is invalid (ex: if the provided language isn't supported, put an error message and default the language to english). I know it's not a good practice to change a value in a validation but in the case of language, I need it to be properly set for further validations and to display the error messages.
How can I do that ?
Thanks
You can't do this within a fluent validation validator. The validator class should only be concerned with providing validation: this is its single responsibility.
To do what you describe, you could perform the validation and then check for the language related error at which point you could set the default value.
Or perhaps you could default the language value prior to validation - it's difficult to make a recommendation without any code.
Related
I have a Blazor application. In this application I have several models with various ValidationAttributes.
To validate these models I use an EditForm from Microsoft.AspNetCore.Components.Forms. On this form there is a parameter Called EditContext where I call the Validate() method to validate my models.
The validation itself works fine. However the order of when validations are run seems to be based on the type, like this:
Required
Other(Like Range)
IValidatableObject
This results in Required validations being validated first and only after these are valid in the model the other validations are running.
What I want is for all validations to run at the same time.
Does anyone know how to achieve this in Blazor?
Thanks
What I want is for all validations to run at the same time.
Not sure what you mean? All registered validations are run when you call Validate. There's has to be a sequence. If you want to change the sequence then you need to write your own validator.
the order of when validations are run seems to be based on the type
Validate on the edit context looks like this. It simply invokes any delegates registered with the OnValidationRequested event.
public event EventHandler<ValidationRequestedEventArgs>? OnValidationRequested;
public bool Validate()
{
OnValidationRequested?.Invoke(this, ValidationRequestedEventArgs.Empty);
return !GetValidationMessages().Any();
}
DataAnnotationsValidator or whatever validator you use registers a handler on this event.
In your case the validator is finding fields to validate by searching through the properties in the Model (referenced in EditContext) for specifc attributes. The first attribute it looks for is Required, ....
I'm apparently not allowed to write comments - only answers - but you're right and what Shaun Curtis says is definitely wrong. For anybody doubting this there's a gist here.
When using <DataAnnotationsValidator /> the validation attributes on the model are processed first - only once all of the attribute validation rules pass will the IValidatableObject.Validate method be called.
The functionality is by design - IValidatableObject is to validate the entire object once all of its properties have been filled out. Why is IValidatableObject.Validate only called if property validation passes?
I think Required validation is checked first for a reason. All subsequent validations can and must be run only after the property has a value.
Otherwise, all the other validations would fail by default. It's the framework just reminding you that you forgot to type in a field. After you supply the value, then it goes on to validate its format and what not.
I hope that makes sense.
I am having the following problem, and so far haven't been able to find an effective solution for it. I have the following code in my Swagger spec:
SomeRequest:
type: object
properties:
client_date_of_birth:
type: string
format: date
example: "2021-09-17"
required:
- client_date_of_birth
This of course works fine, but the problem is introduced when I try to use FluentValidation instead of this approach. My FluentValidation code looks like this:
.RuleFor(i => i.ClientDateOfBirth)
.NotEmpty().WithMessage("Client's Date of Birth is empty")
.InclusiveBetween(new LocalDate(1870, 01, 01), todayDate);
I wish to display the FluentValidation message instead of the generic one from Swagger, but so far I haven't found a good way to do this.
If I remove client_date_of_birth as a required field, I will also open up the API's contract to allow this field as a null value (which I want to deal with at API level, not FluentValidation level) and if I set it up as a string instead of a date, I will also be opening up the API's contract to potentially unwanted behavior. Is there a way to override this particular behavior of Swagger with the FluentValidation's implementation?
Just as a clarification: I know I will get the FluentValidation error message if I remove the client_date_of_birth as a required field in the Swagger spec. My question is more about: can I override Swagger's behavior with FluentValidation's? the reason I want to keep it in both places is so the rules of the API's contract are transparent and written in the spec itself, rather than obscure and hidden in the code, even if I will be eventually using said code.
please help me with this:
I have model with property of Enum type. This property is required, but on View initial value must be empty. Will show watermark..
If I understand correct than model will have not nullable property. But viewModel will have nullable. Is this right way?
Looks like in this case:
need create viewModel
viewModel and model will have different data sometimes
viewModel need logic for set and get correct values to/from model or get null in first time and leave as is model
viewModel need one more validation rule
Other way is:
Model will have nullable property and required validation:
can bind to model
model does not "correspond to reality"
we need always expose data from nullable enum and forgot about null? or handle it (convert to DTO or something like this)
nullable property is only UI requirement and can be changed in future
Having the (domain) model use a non-nullable enum suggest that the value MUST have a valid value for use case and / or data integrity.
Having the requirement on the UI to allow a non-valid value (initial or otherwise) will result in 1 of 2 things:
When the View posts a null value (via a dedicated ViewModel) and checks are performed to ensure its not null (I assume that the user selects the "Please Select" value from a select box) the server will respond with an error message saying something like "Please select a valid value blah blah".
Or, the server will allow the null from the View Model and default the domain model property to some predetermined sensible value.
In my experience this kind of setup leads to a frustrating user experience because having the UI present an invalid selection option that only feedsback upon submit (or using some frontend / client validation) is rather annoying (especially on a long form, Etc).
Perhaps you would be better rethinking that requirement so the UI only presents valid enum values for selection and optionally make the default one a sensible value.
Having said that if you really want the UI to not have a valid initial value then make a view model with a nullable version and validate on the server as you suggest. As far as changing the (domain) model with a nullable, you already seem concerned that it doesn't "correspond to reality" which is a good impulse considering you already mentioned that this is a UI only requirement, thus place the detail in that layer and keep your domain modals as close to reality as is needed to satisfy your use cases and / or business requirements.
Is there a way to change the default error message for an in in FluentValidation?
We are able to set up validations for more complex types but the simple 'the data you entered isn't an int' style things we can't seem to get at.
The built in error for these is: 'the value x isn't valid for y' or something along those lines - is there a way to override these?
There's no easy/clean way to achieve that. The first possibility is to override the DefaultModelBinder.ResourceClassKey property in your application start and point it to a custom resource file:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
DefaultModelBinder.ResourceClassKey = "Messages";
}
and then define the key PropertyValueInvalid inside App_GlobalResources/Messages.resx.
Another possibility is to use a backing field on your view model as suggested by Jeremy Skinner.
The reason for this is that this error message is generated by the default model binder before any validation can occur on the field. Before you can validate the field it must first be assigned a value. And since you are attempting to convert a string which doesn't represent a valid integer into an integer during model binding, the default model binder assigns a default message.
You can override that as well.
Follow the below link
http://fluentvalidation.codeplex.com/wikipage?title=Customising
I have a binding with data validation implemented by IDataErrorInfo. When data validation fails, the property is set. How could I avoid the change of the property when data validation fails?
Thank you very much.
I don't think IDataErrorInfo can be used to avoid the property value change if value is not valid. It only helps binding proper error message to the UI so that the user can change given value to valid value.
To avoid invalid values being set in your properties, if they are CLR Properties, use validation methods in setters and if they are Dependency Properties, use validation callback to properly validate the values.