I am now working on an existing asp.net MVC application which has a few submit button in one web page(e.g. ignore, delete, update, submit...) And they all send a post request to the server and the server will perform different operation based on the request action type (e.g. ignore, delete, update, submit).
However, an issue is raised that the model validation will always triggered regardless of the operation. According to the business rule, we only need to perform validation for some certain operations. I can clear the error in the modelstate after the validation. (for example MVC3 Remove ModelState Errors).
However I am hoping there is a better way to just bypass the model validation according to the operation parameter from the client.
BTW: I am not allowed to significantly change the way how it works (e.g. send to another action for those operation that does not care about the validation), so I am hoping there is some trick that I can use to achieve this easily.
I assume now you are checking model state errors like...
if (ModelState.Valid)
{... valid and do something...}
If so, you can include some additional checking before hand that will be considered in your conditional. For instance, if it is a delete submit, you can check that before hand.
bool isDelete = ***check some value***
if (isDelete || ModelState.Valid)
{... valid and do something...}
This way even if the model state is invalid, it will still move forward.
One thing to consider is you will need to set something in the model to tell you what action is happening. This probably means using javascript to capture the submit, set a model field, and then perform the submit.
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'm currently working on a large project involving Sitecore CMS (7.2). For viewmodel validation we are using FluentValidations. Because of the combination of Sitecore and FluentValidations I seem to be running in some kind of technical deadlock. I sort-of found a solution myself, but I'm not sure whether this is the right approach or not. Here's the problem:
Situation
There is a Sitecore component which contains a HTML form. Via the modelbinder each value of this form is binded to it's corresponding field in the (complex) viewmodel. This is standard .NET MVC approach.
However, some values in the viewmodel are NOT part of the form. For instance, a date at which the mutation will be applied is calculated by the application. The user can only see this date-value as plain text, and thus can not edit it. It's still part of the viewmodel though. To make sure this value is being posted back to the model in code, one would normally use a hidden field. But if I use a hidden field, it means that users are able to spoof that date and because some validations depend on this value, they are able to spoof the entire validity of the form.
Moreover, in the same viewmodel I have a list of complex objects that I can't simply put in a hidden field (or I should serialize it to JSON, which I don't want).
The conclusion is that I need to store this data somewhere else. Somewhere the user can't spoof it, but I'm still able to validate user input with FluentValidations. I therefore decided to put the entire viewmodel in the users Session, and delete it directly after a succesful mutation.
Problem
By using session data I run into problems. Let's first see these steps:
(GET) Viewmodel is created. Calculated date is set and list of complex types is retrieved once from a (slow) webservice.
Entire viewmodel is stored as session data.
Form is shown to the user, who fills the form. Some data is only shown as readonly, like the date and list of complex types.
User submits form, FluentValidations kicks in to validate the data (POST).
That's where I run into problems. The validation done by FluentValidations kicks in before it reaches the POST controller method. That's exactly the way we want it, because that means validation errors are automatically added to the ModelState. However, because of security reasons I don't want to add this data as hidden fields to the cshtml file, which means they are empty at the time FluentValidations is going to validate the form.
This is creating problems because some of the form validations rely on the missing data. What I basically want is to merge the viewmodel that is stored in the session with the viewmodel that was posted to the controller method. But I have to do that before FluentValidations is going to do it's work.
My current solution
Gladly, I learned about FluentValidation's IValidatorInterceptor: an interface that can be used to 'do stuff' before or after the validations process kicks in. I used the BeforeMvcValidation method to do my merging process. The code is as follows:
public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
{
if (controllerContext.HttpContext.Session == null)
return validationContext;
var sessionData = controllerContext.HttpContext.Session["some_identifier"];
if (sessionData == null)
return validationContext;
var mergedObjectToValidate = Utils.MergeViewModelData(sessionData, validationContext.InstanceToValidate);
// Unfortunately, we have to do this..
var privateSetterProperty = validationContext.GetType().GetProperty(ValidationContextInstancePropertyName);
if (privateSetterProperty == null)
return validationContext;
privateSetterProperty.SetValue(validationContext, mergedObjectToValidate);
return validationContext;
}
Basically this interceptor method allows me to do my merging-process before validation. So I thought I had the solution here, but as you can see I am using reflection to set a property. That is because the property InstanceToValidate in the ValidationContext object has a private setter. I simply can not set it without using reflection. Which is, obviously, a bit dirty.
It does work exactly as I want though! :) I do not need any hidden fields that can be spoofed (which is horrible for straight-trough-processing) and I can still use FluentValidations exactly as I always did before. Also, the MVC modelbinding-process is left untouched, which I prefer.
The actual question
So the above solution works exactly as you want so what are your questions?! Well, simple:
I'm using reflection to set a private property in a 3rd party library (FluentValidations). The obvious answer is: don't go that way. But in this case it works flawlessly. If the InstanceToValidate-property had a public setter, I wouldn't even be posting this question at all: I would feel like I nailed it. But unfortunately it is private, so are there any real reasons why I shouldn't do this, maybe someone being an expert in FluentValidations behaviour?
Let's say there is a genuine reason why I shouldn't go this way; is there another approach which has the same effect? Can I hook in even earlier, so before FluentValidations kicks in, perhaps some kind of 'hook' just after the MVC model-binding process but before validation kicks in?
Is this entire approach simply wrong and should I tackle it in a completely different way?
Thanks!
Let's say I have one view model. It has one required Name property. And I have disabled client-side validation. I have this code in my action method:
if (!ModelState.IsValid)
{
return View(model);
}
So, everything works fine. It will highlight the required field after post. But, I can't understand that which jQuery validaion function do this process? How, jQuery Validation detects that the form has been submitted once?
I want to find that code, because I want to change it slightly. For example, I have my own helpers, which has custom validation logic. For now, my custom helper validation are not showing after invalid Post. And, I want to add my logic to the built-in function, which I CAN NOT FIND ANYWHERE.
Firstly, if you have disabled client side validation, jquery validation has nothing to do with it (you have disabled it!). To briefly explain what happens when you post and return the view.
The DefaultModelBinder initializes a new instance of you model
The DefaultModelBinder then reads the form data (name/value pairs)
and if a property name matches one of the form data values, its
property is set (assuming its valid) and its value is also added to
ModelState. If the value is not valid, the property is not set but
its value is added to ModelState (the attemptedValue) along with
a ModelState error
When you return the view, your #Html.ValidationMessageFor() method
reads the ModelState values and if there is an error associated
with the property, the error message is added to the html generated
by the ValidationMessageFor() method and the relevant class name
(which highlights it) is added
You can inspect the source code for the DefaultModelBinder and ValidationExtensions if you want to see more detail of how these work.
As for "I want to find that code, because I want to change it slightly", then DONT. You have not indicated what you trying to do, or shown any code for your html helper extension method, but html helpers do not (and should not) contain validation logic. They are responsible for generating html based on a property and the validation attributes applied to that property.
If you have custom validation logic for a property, then you create an attribute that inherits from ValidationAttribute (and if you also want client side validation then it also needs to implement IClientValidatable). A good guide for creating your own validation attributes is this article.
Mvc has its own validation that is not server side and works with the data annotations you set on your model. On post it simply goes to the controller then checks the modelstate errors if its valid it runs your code in the function, if not it returns the model with its errors. If you had jquery validation it would never go to the controller in the first place. When server side validation is enabled the validation is done before the form is sent to the controller. Without jquery it is validated at the controller. If not what you're looking for please let me know
I'm using ASP.NET MVC2.
I love how I can annotate my model's fields and check ModelState.IsValid.
The obvious reason to check it is when doing validation, but should you always check it in case new validation rules are applied to the model?
That way you don't forget / need to check whether the controller checks IsValid.
Is there some reason not to do this check in all cases? Maybe just when the controller action has side-effects (DB-writing etc)?
but should you always check it in case new validation rules are
applied to the model?
You should do this check always else you end up persisting models that don't have valid state.
Generally, in asp.net-mvc, models passed to actions are view models, and they contain all and only data needed for action to act. If that data is invalid, theoretically it should be impossible for action to do its job.
ModelState.IsValid ensures that all data needed for action is in good state, ready to be acted on. I do not see reason where you would not want to validate that data.
You should always check if the model is valid immediately before attempting to use the data collected in the model to do something. That's the purpose of validation in the first place.
It looks obvious to mention, but i will give it a shot.
There is NO need to check model state when you are NOT posting your model. The common scenarios are when you are getting your data from service and displaying on a get action.
I'm using MVC2.
Whats the recommended way of server side validation of forms when using knockout?
Currently, most of my forms are in partial views, which have a C# ViewModel with Validation Attributes. Something like this:
public class SomeThingViewModel
{
[Required]
public string Name { get; set; }
[Required]
public int Number{ get; set; }
}
So when the form gets submitted to the server, I get all the model errors and I can return the form with errors, which are displayed with something like: <%: Html.ValidationMessageFor(m => m.Name)%>. This is then reloaded into the element that holds the form on the main page so that the user can see the errors. This would kill any bindings I had with the form in knockout I would assume.
I'm not really sure how to go about this using knockout.
This can be tricky, but done right works like a breeze.
First, synchronize your viewmodels. What you have client-side in knockout you pass exactly to the server. Second, don't do server-side HTML with knockout. Create fields that are set server-side and read client-side that indicate the validity of each data field in your ViewModel.
So if your Model has a field Name, your ViewModel has Name and Name_ValidationResult, which is an enum that indicates whether or not the Name field is valid and why it's not. If your server-side validation fails, set your validation result fields and pass the whole server-side ViewModel back to the client to be re-set as the client-side ViewModel after the request completes. Basically, you a re-creating the ViewState portion of ASP.NET, but doing so in a format that will work with Knockout.js
On the client-side, you have error messages that only show based on values of the ValidationResult fields. So you might have a canned error message that states "The Name field must be set" that is only displayed if Name_ValidationResult has the value "Empty" (for example).
Basically, you actually use the MVVM pattern with a minor tweak to account for having to round-trip to the server.
So you are suggesting that I add ValidationResult fields in my C# ViewModel for each property. Then set the ValidationResult Properties in my controller when I check for the Model's validity. Then pass back the viewmodel as JSON? so that I can update my knockout viewmodel. This will require me to manually validate to some extent right? Or can I leverage the ModelState errors that I will end up with? – Blankasaurus
Bottom line is yes to all your questions.
In truth, I missed the fact that you were using DataAnnotations for your validation, or I'd have mentioned it. You should be able to leverage ModelState errors to set your validation results that you pass back to your knockout page.
The problem is that you're using two fundamentally incompatible technologies and hoping they'll play nice together, and I don't think that's going to work out the way you hope. Something is going to have to give, and I suggest that the best point for that is server-side. Drink the knockout cool-aid and fix what you have to server-side.