jquery unobtrusive validation: custom methods do not fire after postback - asp.net-mvc

This is an asp.net framework 4.8 mvc application using jquery unobtrusive validation.
I'm using custom validation methods in the usual fashion.
First, I've added the custom validation attribute in the model:
public class MyValidateAttribute : ValidationAttribute, IClientValidatable
{
private const string errorMessage = "My custom error message.";
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = errorMessage,
ValidationType = "myvalidateattribute"
};
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//server-side validation here
}
}
And then applied the validation attribute to a property in the usual fashion:
[MyValidateAttribute]
public bool MyProperty {get;set;}
In javascript I add a matching custom client-side validation method, which is loaded in the $(document).ready() event:
$.validator.addMethod("myvalidateattribute",
function (value, element, param) {
//client side validation here
return false;
}
return true;
});
$.validator.unobtrusive.adapters.addBool("myvalidateattribute");
This works fine. The custom validation fires when I submit the form, which I do like so on a button click:
let $form = $('form');
//clear the validation summary
$form.validate().resetForm();
$form.find("[data-valmsg-summary=true]")
.removeClass("validation-summary-errors")
.addClass("validation-summary-valid")
.find("ul").empty();
//reset unobtrusive field level
$form.find("[data-valmsg-replace]")
.removeClass("field-validation-error")
.addClass("field-validation-valid")
.empty();
if ($form.valid()) {
$form.submit();
}
I've been following this pattern for years and it has worked flawlessly. All of the validation fires client-side, and then fires again server-side. The server-side validation is a failsafe. Any validation problem is caught client-side. Any server-side validation error is assumed to be an attempted hack, and I throw an application exception.
I now have a new requirement where I must do some validation server-side, after the form is posted, and send the form back to the browser in an invalid state if the server-side validation fails. This works, but when I send the model back to the browser none of my custom validation fires.
Here is how the server-side validation looks in the controller method:
public ActionResult MyControllerMethod(MyModel myModel)
{
if (MyServerSideValidationFails(myModel))
{
myModel.MyProperty = false; //this is a validation error
TryValidateModel(myModel);
return View("MyView", myModel);
}
//because of client-side validation any invalid model should be assumed an attempted hack
if (!ModelState.IsValid)
throw new ApplicationException("ModelState should never be invalid.");
//other server-side code here
}
If I now resubmit the form, none of my custom validation fires, and so I get an application exception.
When I step into the $(document).ready event when the model is sent back to the browser, all of my $.validator.addMethod() calls are done successfully. But when I then re-submit, and step into the $form.valid() method in the javascript, I notice that the rules collection for each element does not contain any of my custom validation methods. All of the non-custom validation fires as normal (maxlength, required and etc). Just the custom validation is missed.
It's not just deliberately setting the model invalid on the server that causes the problem. As a workaround, in my controller method, I'm doing a RedirectToAction to the GET method that initially loads the page. Since I don't want the user to lose all of the information in the model, I tried creating a new model, setting all of the properties on that model from the posted model, then storing the new model in TempData. When I'm redirected to the GET action I pulled the model out of TempData and displayed the page using it. I still get the same problem--none of my client-side custom validation fires on re-submission.
It appears that posting the model back, and then loading the page with a model, breaks my client-side custom validation. What am I missing?

Related

Call ASP.NET's input validation after using ValidateInput(false)

When I bash my hands on special characters in the entry fields of my form, when I hit submit, all I get back is a blank page, as ASP.NET detected potentially malicious input and threw an error into my Event Log, all before reaching my MVC controller action.
I would much rather return an error to the user, so they don't just get a blank page. So I want to use ValidateInput(false) and then call the validator from inside my code, so I can send a response. How do I call the validator from an ASP.NET MVC action?
I am little confused with your question .Are you looking for invoking the code which does the model validation ? IF yes you can do that by checking the ModelState.IsValid property. If any of your validation fails, it will return false and in your view you will see the error messages. You can also specify some custom error message from your action method as well if you want to do so.
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//Model validation is successful. Proceed to other stuff
//If you want to return some custom error message, do like this
ModelState.AddModelError("", "The user name is deactivated");
}
return View(model);
}
Do you want to validate input at server side?
You can use client side validation, you need to add regular expression attribute on your model property and set a nice error message for it. like below
[RegularExpression("your regex", ErrorMessage = #"please check your input .... ")]
public string MyInput { get; set; }

MVC3 Remote Validation and AntiForgeryToken

After reading about XSS attacks I have applied the AntiForgeryToken to my website using the ValidateAntiForgeryTokenWrapperAttribute described in this article:
http://weblogs.asp.net/dixin/archive/2010/05/22/anti-forgery-request-recipes-for-asp-net-mvc-and-ajax.aspx
It seems to work well however I've come across a problem when using Remote Validation in MVC3. I have a ValidationController which contains all of the common validation within my site and if I apply the ValidateAntiForgeryTokenWrapperAttribute to it then the remote validation no longer works and I get an 'A required anti-forgery token was not supplied or was invalid.' exception logged in Elmah. I've tried debugging this and it doesn't even hit the controller action before throwing the exception. I assume this happens because the remote validation doesn't know to pass the AntiForgeryToken to the controller - has anyone else had this problem or knows whether the two are not meant to be used together?
It also made me question whether I should be using the ValidateAntiForgeryTokenWrapperAttribute on every controller or not, any thoughts?
In the remote attribute do as below:
[Remote("MyValidationMethod","MyController", HttpMethod = "POST", AdditionalFields = "__RequestVerificationToken")]
public object MyField { get; set; }
the AdditionalFields property can accept comma separated fields names in the form; the __RequestVerificationToken is the name of the hidden field which contains the AntiForgeryToken.
I haven't used Remote Validation. However I had similar experience with AntiForgeryToken. When I had it applied for all the actions in my controller. Later, I removed it from all the actions and applied to only those actions which were sending data back to database (insert/update/delete).
As it seems you have applied AntiForgeryToken validation attribute to entire controller, it will always create a new token value every time an action is executed and so when response goes back to client for remote validation action the value of token is different than what is on the form which gets submitted later for other actions.
You can remove AntiForgeryToken attribute from the controller and use it with other action apart from remote validation action or wherever you really need it.
//Instead of this
[ValidateAntiForgeryToken]
public class mycontroller
{
//...
}
//Do something like this
public class mycontroller
{
public ActionResult myotheraction ()
{ }
[ValidateAntiForgeryToken]
public ActionResult valdaitionaction ()
{ }
}

Struts 2 Action Methods

Time for yet another stupid question, adding to a long line of them.
I'm a newbie with Struts 2, having spent years using old 1.X. Struts 2 actions can be roughly equivalent to Struts 1 DispatchActions, simply by adding methods (and defining them in struts.xml).
So, suppose I have this method:
public String create() throws Exception {
// insert create logic here
}
What I want is to have create() do double duty. If create() is called without being passed any parameters, it returns INPUT (and displays the JSP form page), otherwise it processes the form data and returns SUCCESS (and displays a default page).
The only way I have now is to check and see if any values are in the request, which seems silly to me. If I could say "if I call this via HTTP GET show the form, if I call this via HTTP POST, process then redirect to the default".
Like I said, I'm probably being pretty dumb here, but any help would be appreciated.
What you are looking for is to use the same action to show a form and then (after submit) process the form.
public class MyAction {
#SkipValidation
public String execute() throws Exception {
return INPUT; // shows the form
}
public void validate() {
// do your validations here...
}
public String submit() throws Exception {
// process the form
// redirect somewhere
}
}
If your action is mapped as "myaction", then your form should submit to "myaction!submit" (that's Dynamic Method Invocation, which invokes your submit() method).
You'll need to create a custom interceptor if you want to enforce that the execute and submit methods are invoked only by HTTP GET and POST methods (respectively).

ASP.NET MVC: Is Data Annotation Validation Enough?

I'm using the Data Annotation validation extensively in ASP.NET MVC 2. This new feature has been a huge time saver, as I'm now able to define both client-side validation and server-side validation in one place. However, while I was doing some detailed testing, I realized that it's quite easy for someone to bypass the server-side validation if I relied on Data Annotation validation alone. For example, if I defined a required field by annotating the property with the [Required] attribute and placed a textbox for that required field in a form, a user could simply remove the textbox from the DOM (which can easily be done through Firebug) and now the Data Annotation validation will not be triggered on that property during ModelBinding inside of a Controller. To ensure that the "required" validation is triggered, I can repeat the validation after ModelBinding happens, but then I'd be repeating my validation logic.
What is everyone's recommendation on validation? Is Data Annotation validation enough? Or does the validation need to be repeated to ensure that validations get triggered in all situations?
Follow-up comment:
Based on the answers below, it seems that I can't rely on the Model Binder and Data Annotation validation alone. Since we're concluding that additional server-side validation is required, is there an easy way for my Service layer to trigger validation based on what's been defined in the Data Annotations? It seems that this will get us the best of both words...we won't need to repeat the validation code, but we'll still ensure that the validation gets executed even if Model Binder doesn't trigger it.
I'm going to post this follow-up comment as a separate question, as it poses a different question than the original one.
I think to be vigilant concerning security you should choose to you make server validation the priority and ensure that this is always your fallback. Your server validation should work without the client validation. Client validation is more for UX and tho that is paramount to your design, it is secondary to security. With this in mind you will find yourself repeating your validation. A goal is often trying to design your app so that the server and client validation can be integrated as much as possible to reduce the work required to validate on the server and the client. But be assured you must do both.
If bypassing the client validation (by means of DOM manipulation) is avoiding the server validation (which it seems you are indicating) then your server validation for this instance may not be employed appropriately. You should be invoking your server validation again in your controller action or in a service layer. The scenario you describe should not be defeating your server validation.
With the scenario you describe, the DataAnnotation attributes method should be sufficient. It seems that you simply need to make a few code changes to ensure that your server validation is invoked also when submitting the form.
I paired xVal with DataAnnotations and have written my own Action filter that checks any Entity type parameters for validation purposes. So if some field is missing in the postback, this validator will fill ModelState dictionary hence having model invalid.
Prerequisites:
my entity/model objects all implement IObjectValidator interface which declares Validate() method.
my attribute class is called ValidateBusinessObjectAttribute
xVal validation library
Action filter code:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
IEnumerable<KeyValuePair<string, object>> parameters = filterContext.ActionParameters.Where<KeyValuePair<string, object>>(p => p.Value.GetType().Equals(this.ObjectType ?? p.Value.GetType()) && p.Value is IObjectValidator);
foreach (KeyValuePair<string, object> param in parameters)
{
object value;
if ((value = param.Value) != null)
{
IEnumerable<ErrorInfo> errors = ((IObjectValidator)value).Validate();
if (errors.Any())
{
new RulesException(errors).AddModelStateErrors(filterContext.Controller.ViewData.ModelState, param.Key);
}
}
}
}
My controller action is defined like this then:
[ValidateBusinessObject]
public ActionResult Register(User user, Company company, RegistrationData registrationData)
{
if (!this.ModelState.IsValid)
{
return View();
}
...
}
The DataAnnotation is certainly not enough. I use it extensively also to pre-validate my calls to the domain model to get better error reporting and fail as early as possible.
You can however tweak the DataAnnotation Model yourself to ensure properties with [Required] MUST be posted. (will follow up with code later today).
UPDATE
Get the source for DataAnnotations Model Binder and find this line in DataAnnotationsModelBinder.cs
// Only bind properties that are part of the request
if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) {
Change it to
// Only bind properties that are part of the request
bool contextHasKey = bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey);
bool isRequired = GetValidationAttributes(propertyDescriptor).OfType<RequiredAttribute>().Count() > 0;
if (contextHasKey || (!contextHasKey && isRequired)) {
I wrote my own ValidationService for MVC 1.0 by copying patterns from both xVal's DataAnnotationsRuleProvider and Microsoft's DataAnnotationsModelBinder (and Martijn's comments). The service interface is below:
public interface IValidationService
{
void Validate(object instance);
IEnumerable<ErrorInfo> GetErrors(object instance);
}
public abstract class BaseValidationService : IValidationService
{
public void Validate(object instance)
{
var errors = GetErrors(instance);
if (errors.Any())
throw new RulesException(errors);
}
public abstract IEnumerable<ErrorInfo> GetErrors(object instance);
}
The service is a validation runner that walks the property tree of the object instance it receives and actually executes the validation attributes that it finds on each property, building a list of ErrorInfo objects when attributes are not valid. (I'd post the whole source but it was written for a client and I don't know yet if I'm authorized to do so.)
You can then have your controllers, business logic services explicitly invoke validation when you are ready, rather than relying exclusively on the model binder for validation.
There are a couple of other pitfalls that you should be aware of:
The default DataTypeAttribute in data
annotations doesn't actually do any
data type validation, so you'll need
to write a new attribute that
actually uses xVal regular
expressions (or something else) to
perform server-side data type
validation.
xVal doesn't walk
properties to create client-side
validation, so you may want to make
some changes there to get more robust
client-side validation.
If I am allowed and have time, I will try to make more source available...
See codeProject Server-side Input Validation using Data Annotations
Input validation can be done automatically on the client side in
ASP.NET MVC or explicitly validating the model against the rules. This
tip will describe how it can be done manually on the server-side of an
ASP.NET applications or within the repository code of WPF
applications.
// Use the ValidationContext to validate the Product model against the product data annotations
// before saving it to the database
var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null);
var validationResults = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(productViewModel, validationContext,validationResults, true);

MVC Model Binding

I am using the MVC validation library from link text. I chose this library because I am also using .NetTiers which generates all of the Validation Attributes using MS Enterprise Library Validation Blocks.
It works fine except that that model binding is automatically validating the object and populating the Validation summary. I believe this in normal behavior.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register([Bind()]NetTiersObject obj)
{
return View();
}
The validation library also has a method that is documented as follows:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register([Bind()]NetTiersObject obj)
{
try
{
obj.Validate<NetTiersObject>();
}
catch (EntityValidationException ex)
{
ViewData.ModelState.PopulateWithErrors(ex);
}
return View();
}
This also works fine.
My problem is that when using the validation library's method it duplicates the error messages. When just using the model binding the error messages appear strange. The errors have the property name in the message.
So, I think I should either need to format the model binding error messages or disable model binding altogether.
Any recommendation, help?
Thanks.
Have you try:
/*At the Point the ModelState should be Valid(TRUE)
because we still didn't enforce any validations */
var v = ModelState.IsValid;
try
{
obj.Validate<NetTiersObject>();
}
catch (EntityValidationException ex)
{
ViewData.ModelState.PopulateWithErrors(ex);
}
And check if v is True, it should be.
If it is true then obj.Validate<NetTiersObject>(); is doing something wrong because its the only populating the errors duplicated.
My problem is that when using the
validation library's method it
duplicates the error messages. When
just using the model binding the error
messages appear strange. The errors
have the property name in the message.
Its is weird because the modelBinding should not populate Model errors, at least if your are not using a custom Model Binding or trying to save into the DB without validating fields.
I found the answer to why my ModelState was invalid. The form elements do not match up to the model, that is the only way that the ModelState would be invalid immediately.
If I find more specifics I will update this post.

Resources