MVC 5 Remote Validation - c#

I need to validate an input field value from user before the form is submitted.
I have created an action in my custom controller and decorated the field with it:
action name: CheckValue
controller name: Validate
[Remote("CheckValue", "Validate"), ErrorMessage="Value is not valid"]
public string Value { get; set; }
The problem is when I press submit, the form is being submitted and then the message Value is not valid is shown if the value entered by the user is not valid.
How can I validate the value entered by user and prevent the form to be submitted if value is not valid, and display the error message?
If I try in JavaScript to check if the form is valid $("#formId").valid() that returns true, that means no matter what is the status of the value (valid or not) the form is valid.
In the other hand if I decorate another field with the [Required] attribute the form is not submitted and the error is shown for that field that is required. However the validation doesn't occur behind the scene for the remote validation field.

The complete solution of Remote Validation in MVC. It will check if the email exists in database and show the following error:
Email already exists
Account Controller Action
[AllowAnonymous]
[HttpPost]
public ActionResult CheckExistingEmail(string Email)
{
try
{
return Json(!IsEmailExists(Email));
}
catch (Exception ex)
{
return Json(false);
}
}
private bool IsEmailExists(string email)
=> UserManager.FindByEmail(email) != null;
Add Model Validation
[Required]
[MaxLength(50)]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
[System.Web.Mvc.Remote("CheckExistingEmail", "Account", HttpMethod = "POST", ErrorMessage = "Email already exists")]
public string Email { get; set; }
Add Scripts
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

You dont put the Controller code. But must to be something like this:
Your code:
[Remote("CheckValue", "Validate", ErrorMessage="Value is not valid")]
public string Value { get; set; }
My code for the controller(Validate):
public ActionResult CheckValue(string Value)
{
if (Value == "x value")
{
// This show the error message of validation and stop the submit of the form
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
// This will ignore the validation and the submit of the form is gone to take place.
return Json(false, JsonRequestBehavior.AllowGet);
}
}

With the reference of c-sharpcorner demo
We can use RemoteAttribute.
Step 1
In the HomeController create a method and for that write the following.
public JsonResult IsUserExists(string UserName)
{
//check if any of the UserName matches the UserName specified in the Parameter using the ANY extension method.
return Json(!db.Users.Any(x => x.UserName == UserName) ,JsonRequestBehavior.AllowGet);
}
You might be wondering why we are returning JsonResult back. We want the validation to occur at the client side, so we are returning a JsonResult.
Step 2
The next step is to hook this method up with the username property and for that first we need to add a class file in the models folder, add a partial User class and provide the required customization to the UserName property.
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace UniqueField.Models
{
[MetadataType(typeof(UserMetaData))]
public partial class User
{
}
class UserMetaData
{
[Remote("IsUserExists","Home",ErrorMessage="User Name already in use")]
public string UserName { get; set; }
}
}
Step 3
In create.cshtml we need to specify the source of the three jQuery files in the given order.
<h2>Create</h2>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>

The existing answers are great but there are a couple of gotchas:
1) The validation method's parameter name has to exactly match the name of the property being validated, e.g. for
[System.Web.Mvc.Remote("CheckExistingDocumentTypeCode", "DocumentTypes", HttpMethod = "POST", ErrorMessage = "Code already exists")]
public string DocumentTypeCode { get; set; }
The validation method's parameter must be called DocumentTypeCode, including the capital letter, or else you'll get a null as the parameter instead of the value of the property being validated:
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> CheckExistingDocumentTypeCode(string DocumentTypeCode)
Be particularly wary of this if you are a Resharper user, of if you're writing multipurpose validation methods for use by more than one property.
2) I had to get this working with a Telerik grid and I had to implement it slightly differently in order to get the validation failure messages to show correctly in the grid (the examples here showed 'false' as the validation error message):
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> CheckExistingDocumentTypeCode(string DocumentTypeCode)
{
try
{
if (!await IsDocTypeCodeExists(DocumentTypeCode))
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json("This Document Type Code is already in use", JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.ToString(), JsonRequestBehavior.AllowGet);
}
}

Remote Validation in MVC
Model Class must have a namespace "System.Web.Mvc" where you have defined the property.
using System.Web.Mvc;
[Required(ErrorMessage = "E-mail is required")]
[RegularExpression(#"^[a-zA-Z0-9_\.-]+#([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Email is not valid")]
[StringLength(30, ErrorMessage = "Email must not be more than 30 char")]
[Remote("IsEmailAvailable", "User", ErrorMessage = "E-mail already in use")]
public string Email { get; set; }
Make sure you have to implement IsEmailAvailable Action on the Controller.
[HttpGet]
public JsonResult IsEmailAvailable(string email)
{
// Check if the e-mail already exists
return Json(!db.Users.Any(x => x.Email == email), JsonRequestBehavior.AllowGet);
}
Make sure you have added this js on View for client-side validation.
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
and also enable client-side validation from web.config
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
Note:
Remote attribute only works when JavaScript is enabled. If the end-user, disables JavaScript on his/her machine then the validation does not work. This is because RemoteAttribute requires JavaScript to make an asynchronous AJAX call to the server-side validation method. As a result, the user will be able to submit the form, bypassing the validation in place. This why it is always important to have server-side validation.
In case when Javascript is disabled:
To make server-side validation work, when JavaScript is disabled, there are 2 ways
Add model validation error dynamically in the controller action
method.
Create a custom remote attribute and override IsValid() method.
Adding model validation error dynamically in the controller action method. Modify the Create action method that is decorated with [HttpPost] attribute as shown below.
[HttpPost]
public ActionResult Create(User user)
{
// Check if the Email already exists, and if it does, add Model validation error
if (db.Users.Any(x => x.Email == user.Email))
{
ModelState.AddModelError("Email", "Email already in use");
}
if (ModelState.IsValid)
{
db.Users.AddObject(user);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
At this point, disable JavaScript in the browser, and test your application. Notice that, we don't get client-side validation, but when you submit the form, server-side validation still prevents the user from submitting the form, if there are validation errors.
However, delegating the responsibility of performing validation, to a controller action method violates the separation of concerns within MVC. Ideally, all validation logic should be in the Model. Using validation attributes in MVC models should be the preferred method for validation.

I got the same problem i was copying and pasting script links >
problem solved when I drag and drop jquery files from script folder to view page
I hope this help.

Related

Why do I get this 404 error?

In a newly created MVC4 application, insert this function in the account controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AdminLogin(AdminLoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login("administrator", model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The password provided is incorrect.");
return View(model);
}
and this
public class AdminLoginModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
is put into the accountModel.cs. I also created a new file AdminLogin.cshtml and leave it empty.
In the _loginPartial.cshtml file I insert an action link
<li>#Html.ActionLink("Register", "AdminLogin", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
But when I click that Register link, I then see the 404 error stating that
/Account/AdminLogin is not found.
I miss something along the way inserting that tiny mvc; could you help me ? I am a mvc beginner.
Clicking a link in a browser results in a GET request, but your action method is only available for POST requests.
Add the [HttpGet] attribute or remove the [HttpPost] attribute to resolve this particular problem.
In general you will want to keep using POST requests when submitting data. As such, my recommendation would be to change the client side to use a form (or use client side logic to intercept the link click action and submit the data using an ajax request).

Show validation message, but clear value

I've been searching around and I'm not able to find an answer on what seems like a simple requirement:
With MVC Data Annotation validation, can you show the validation message ('must be a string with a maximum length of 5') in the validation summary or next to field, but clear the value of the text box (when validation fails).
I've tried to use ModelState.Clear() and ModelState.Remove("CompanyName"), but this clears both the value and validation message (validation state).
I'm asking this because recently we had a penetration test and one of the recommendations was to not pre-populate secure values (credit card number etc) if validation fails. This is obviously a minor issue, but the recommendation was to not send the value back across the internet (from the server) if we didn't have to.
Here is the code I'm working with:
public ActionResult Edit()
{
return View();
}
[HttpPost]
public ActionResult Edit(CompanyInput input)
{
if (ModelState.IsValid)
{
return View("Success");
}
//ModelState.Clear // clears both the value and validation message
//ModelState.Remove("CompanyName") // same result
return View(new CompanyInput());
}
And the view model:
public class CompanyInput
{
[Required]
[StringLength(5)]
public string CompanyName { get; set; }
[DataType(DataType.EmailAddress)]
public string EmailAddress { get; set; }
}
And the view:
#model Test.Models.CompanyInput
<h2>Edit</h2>
#using (Html.BeginForm("Edit", "Company"))
{
#Html.EditorForModel()
<button type="submit">Submit</button>
}
The ModelState of each field holds more than just the value, so removing it from the collection outright removed your error message as expected. I believe you should be able to clear just the value however, by doing something like.
ModelState["CompanyName"].Value = null;
EDIT: Upon closer inspection I found that the Value property is of type ValueProviderResult, simply nulling it doesn't give the desired result, and because the properties of this class appear to be getters only you have to replace the instance with your own. I've tested the following and it works for me.
ModelState["CompanyName"].Value = new ValueProviderResult(string.Empty, string.Empty, ModelState["CompanyName"].Value.Culture);
Because the ModelState isn't valid, you will either have to create a custom validator or a jQuery ajax/json call to determine if the data needs to be cleared or not.
Just changing the model property to string.Empty or something like that won't do the trick because the entire view gets re-rendered with the previous successful posted model but with the ModelState validation errors.
Yes you can add error message like this
[Required(ErrorMessage = "must be a string with a maximum length of 5")]
Update after clarity from OP:
To clear e.g. Input.Field = string.Empty;
You can create a custom validation class which is inherited from ValidationAttribute class
The following link gives a clear idea about how to implement custom validation class suitable for your problem.
Custom Data Annotation

MVC 3 How to make a form with Post Redirect Get method while maintaining views

I've read a LOT on Stack and read a few articles else where on how to design a proper form. I ended up adopting the PRG method. article here
The problem I'm having is actually getting it to work in IE 8. It sort of works in Firefox. I just want to know what I'm doing wrong.
Update
The Form validation is now fixed thanks to trial and error. Validation scripts with IE DO NOT work with Jquery 1.7. They do work with Jquery 1.5 as they are suppose to.
Edit
The form validations also don't work in IE with this current setup. Any light into this would be appreciated.
Problems
1.The Create View is actually loaded as a partial view which is inside the Index view. Picture a popup to add a new Subscriber.
So when the form submits and it's invalid, it goes to the partial view Create which now looks ridiculous because the partial view was meant to be a popup not a page.
How can I fix this so the pop stays up if it's invalid, instead of posting back to the Create View?
Should I just forget about popups and do a new page for forms? What's the best practice and most practical solution to PRG and forms?
The structure I have setup looks like this:
Model
public class Subscribers
{
[Required(ErrorMessage="Name is required")]
[Display(Name = "Subscriber Name: ")]
public string Name { get; set; }
[Required(ErrorMessage = " URI is required")]
[Display(Name = "URI (email or url): ")]
public string URI { get; set; }
[Display(Name = "Channel: ")]
[Required(ErrorMessage = " Channel is required")]
public int SelectedChannelID { get; set; }
[Display(Name = "Subscriber Type: ")]
[Required(ErrorMessage = " Type is required")]
public int? SelectedSubscriberTypeID { get; set; }
public List<Models.Subscriber> getSubscribers()
{
Models.SwitchboardEntities db = new Models.SwitchboardEntities();
List<Models.Subscriber> subscriberList = db.Subscribers.ToList();
return subscriberList;
}
View
Create.cshmtl
#model Switchboard.Models.Subscribers
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (#Html.BeginForm("Create", "Subscriber", Model, FormMethod.Post, new { id = "addForm" }))
{
<fieldset>
<legend><h3>Create</h3></legend>
<br />
#Html.ValidationSummary(true)
<div class="editor-fields">
#Html.LabelFor(xModels => xModels.Name)
#Html.EditorFor(xModels => xModels.Name)
#Html.ValidationMessageFor(xModels => xModels.Name)
</div>
<div class="editor-fields">
#Html.LabelFor(xModels => xModels.URI)
#Html.EditorFor(xModels => xModels.URI)
#Html.ValidationMessageFor(xModels => xModels.URI)
</div>
}
...
Controller
[HttpPost]
public ActionResult Create(Models.Subscribers model)
{
try
{
// TODO: Add insert logic here
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
else
{
return View(model);
}
}
catch
{
return View(model);
}
}
There are many things you're not doing quite right. First, jQuery should be loaded in your layout file (in the head section), not in your partial view (I assume Create.cshtml is partial). jQuery needs to be loaded as early as possible (and should be the first script you load), and it should be loaded before the page is rendered. By putting it so far in, you are just asking for trouble.
I would also load the jquery.validation and jquery.validation.unobtrusive in the layout as well.
If you can do so, i'd download the MVC4 beta, and generate a default internet application. The MVC4 default app uses a popup login dialog, that includes validation and does not go away if the validation fails.. exactly what you're looking for. You should be able to adapt their code to your own needs.
Also, make sure you have the latest versions of jQuery.validation, and jQuery.validation.unobtrusive. Use NuGet to retrieve the latest versions of everything.
You would need to store the data in the TempData object available to both the controller and the view (if needed) and then extract it and use it if its there on the next request.
Controller
public ActionResult Create(Models.Subscribers model) {
// .. snip ..
if (ModelState.IsValid) {
return RedirectToAction("Index");
}
// .. snip ..
this.TempData["SubscribersTemp"] = model;
this.RedirectToAction("Index");
}
This stores the model in the TempData object for the next request and sends the user back to the Index action. This action can do its normal routine and once it hits the #Html.RenderAction("RenderCreate") line, the new logic will grab the temp data and send it to the view.
public PartialViewResult RenderCreate() { // use name of partial view here
// The next line will use the model from the TempData if it exists or
// create a new, empty model if it does not.
// Using the create method above as an example, if the model is valid,
// the next line will create a new subscribers object to pass to the partial
// view. If it was invalid, it'll use the object stored in TempData instead.
Models.Subscribers model =
this.TempData["SubscribersTemp"] as Model.Subscribers ??
new Model.Subscribers();
return this.View(model);
}
The TempData object will persist the data for the next request only. Once that request is complete, the TempData object is cleaned.

Manually invoking ModelState validation

I'm using ASP.NET MVC 3 code-first and I have added validation data annotations to my models. Here's an example model:
public class Product
{
public int ProductId { get; set; }
[Required(ErrorMessage = "Please enter a name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter a description")]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required(ErrorMessage = "Please provide a logo")]
public string Logo { get; set; }
}
In my website I have a multi-step process to create a new product - step 1 you enter product details, step 2 other information etc. Between each step I'm storing each object (i.e. a Product object) in the Session, so the user can go back to that stage of the process and amend the data they entered.
On each screen I have client-side validation working with the new jQuery validation fine.
The final stage is a confirm screen after which the product gets created in the database. However because the user can jump between stages, I need to validate the objects (Product and some others) to check that they have completed the data correctly.
Is there any way to programatically call the ModelState validation on an object that has data annotations? I don't want to have to go through each property on the object and do manual validation.
I'm open to suggestions of how to improve this process if it makes it easier to use the model validation features of ASP.NET MVC 3.
You can call the ValidateModel method within a Controller action (documentation here).
ValidateModel and TryValidateModel
You can use ValidateModel or TryValidateModel in controller scope.
When a model is being validated, all validators for all properties are
run if at least one form input is bound to a model property. The
ValidateModel is like the method TryValidateModel except that the
TryValidateModel method does not throw an InvalidOperationException
exception if the model validation fails.
ValidateModel - throws exception if model is not valid.
TryValidateModel - returns bool value indicating if model is valid.
class ValueController : Controller
{
public IActionResult Post(MyModel model)
{
if (!TryValidateModel(model))
{
// Do something
}
return Ok();
}
}
Validate Models one-by-one
If you validate a list of models one by one, you would want to reset ModelState for each iteration by calling ModelState.Clear().
Link to the documentation
//
var context = new ValidationContext(model);
//If you want to remove some items before validating
//if (context.Items != null && context.Items.Any())
//{
// context.Items.Remove(context.Items.Where(x => x.Key.ToString() == "Longitude").FirstOrDefault());
// context.Items.Remove(context.Items.Where(x => x.Key.ToString() == "Latitude").FirstOrDefault());
//}
List<ValidationResult> validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(model, context, validationResults, true);
if (!isValid)
{
//List of errors
//validationResults.Select(r => r.ErrorMessage)
//return or do something
}
I found this to work and do precisely as expected.. showing the ValidationSummary for a freshly retrieved object on a GET action method... prior to any POST
Me.TryValidateModel(MyCompany.OrderModel)

ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummary

I've been trying to follow the validation tutorials and examples on the web, such as from David Hayden's Blog and the official ASP.Net MVC Tutorials, but I can't get the below code to display the actual validation errors. If I have a view that looks something like this:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Parent>" %>
<%-- ... content stuff ... --%>
<%= Html.ValidationSummary("Edit was unsuccessful. Correct errors and retry.") %>
<% using (Html.BeginForm()) {%>
<%-- ... "Parent" editor form stuff... --%>
<p>
<label for="Age">Age:</label>
<%= Html.TextBox("Age", Model.Age)%>
<%= Html.ValidationMessage("Age", "*")%>
</p>
<%-- etc... --%>
For a model class that looks like this:
public class Parent
{
public String FirstName { get; set; }
public String LastName { get; set; }
public int Age { get; set; }
public int Id { get; set; }
}
Whenever I enter an invalid Age (since Age is declared as an int), such as "xxx" (non-integer), the view does correctly display the message "Edit was unsuccessful. Correct errors and retry" at the top of the screen, as well as highlighting the Age text box and put a red asterisk next to it, indicating the error. However, no list of error messages is displayed with the ValidationSummary. When I do my own validation (e.g.: for LastName below), the message displays correctly, but the built-in validation of TryUpdateModel does not seem to display a message when a field has an illegal value.
Here is the action invoked in my controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditParent(int id, FormCollection collection)
{
// Get an updated version of the Parent from the repository:
Parent currentParent = theParentService.Read(id);
// Exclude database "Id" from the update:
TryUpdateModel(currentParent, null, null, new string[]{"Id"});
if (String.IsNullOrEmpty(currentParent.LastName))
ModelState.AddModelError("LastName", "Last name can't be empty.");
if (!ModelState.IsValid)
return View(currentParent);
theParentService.Update(currentParent);
return View(currentParent);
}
What did I miss?
I downloaded and looked at the ASP.NET MVC v1.0 source code from Microsoft, and discovered that, either by accident or by design, there isn't a way to do what I want to do, at least by default. Apparently during a call to UpdateModel or TryUpdateModel, if validation of an integer (for example) fails, an ErrorMessage is not explicitly set in the ModelError associated with the ModelState for the bad value, but instead the Exception property is set. According to the code from the MVC ValidationExtensions, the following code is used to fetch the error text:
string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
Notice the null parameter for the modelState is passed. The GetUserErrorMEssageOrDefault method then begins like this:
private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState) {
if (!String.IsNullOrEmpty(error.ErrorMessage)) {
return error.ErrorMessage;
}
if (modelState == null) {
return null;
}
// Remaining code to fetch displayed string value...
}
So, if the ModelError.ErrorMessage property is empty (which I verified that it is when trying to set a non-integer value to a declared int), MVC goes on to check the ModelState, which we already discovered is null, thus null is returned for any Exception ModelError. So, at this point, my 2 best work-around ideas to this issue are:
Create a custom Validation extension that correctly returns an appropriate message when ErrorMessage is not set, but Exception is set.
Create a pre-processing function that is called in the controller if ModelState.IsValid returns false. The pre-processing function would look for values in the ModelState where the ErrorMessage is not set, but the Exception is set, and then derive an appropriate message using the ModelState.Value.AttemptedValue.
Any other ideas?

Categories