ASP.NET MVC Remote attribute method parameter always passing null - asp.net-mvc

I have this AdvertiserNameAvailable method that is being used by Remote validation attribute.
The problem is that the AdvertiserNameAvailable is being called without passing the input value to the method Name parameter. When I enter in debug into the method, I see that the Name parameter is always null.
public JsonResult AdvertiserNameAvailable(string Name)
{
return Json("Some custom error message", JsonRequestBehavior.AllowGet);
}
public class AdvertiserAccount
{
[Required]
[Remote("AdvertiserNameAvailable", "Accounts")]
public string Name
{
get;
set;
}
}

Had to add [Bind(Prefix = "account.Name")]
public ActionResult AdvertiserNameAvailable([Bind(Prefix = "account.Name")] String name)
{
if(name == "Q")
{
return Json(false, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
To find out your prefix, right click and do inspect element on the input that you are trying to validate. Look for the name attribute:
<input ... id="account_Name" name="account.Name" type="text" value="">

[HttpPost]
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public ActionResult AdvertiserNameAvailable(string Name)
{
bool isNameAvailable = CheckName(Name); //validate Name and return true of false
return Json(isNameAvailable );
}
public class AdvertiserAccount
{
[Required]
[Remote("AdvertiserNameAvailable", "Accounts", HttpMethod="Post", ErrorMessage = "Some custom error message.")]
public string Name
{
get;
set;
}
}
Also to note:
The OutputCacheAttribute attribute is required in order to prevent
ASP.NET MVC from caching the results of the validation methods.
So use [OutputCache(Location = OutputCacheLocation.None, NoStore = true)] on your controller action.

Related

Model View Remote Validation

I am doing a remote validation using Remote attribute in my MVC model, please find the code below:
[Required]
[System.Web.Mvc.Remote("IsEmailExist", "Account", HttpMethod = "POST", ErrorMessage = "The Email Already Exists")]
In the controller action method I use the user input of email as a parameter and check with the db, please find the code below:
public JsonResult IsEmailExist(string emailAddress)
{
using (var db = new YouTubeNZ())
{
var isExist = !db.Users.Any(X => X.EmailAddress == emailAddress);
return Json(isExist, JsonRequestBehavior.AllowGet);
}
}
But during run time the parameter in the action method is "Null" when the value should be the user input email address and it is not getting validated for existing email.
Make sure you decorate your action method with HttpPost attr, and your property name matches the method parameter (i.e: EmailAddress):
[HttpPost]
public JsonResult IsEmailExist(string emailAddress)
{
using (var db = new YouTubeNZ())
{
var isExist = !db.Users.Any(X => X.EmailAddress == emailAddress);
return Json(isExist, JsonRequestBehavior.AllowGet);
}
}
Please make sure the following.
Your model property should be like
[Required]
[System.Web.Mvc.Remote("IsEmailExist", "Account", ErrorMessage = "The Email Already Exists")]
public string EmailAddress { get; set; }
Also change your AccountsController action method to
public ActionResult IsEmailExist(string emailAddress)
{
using (var db = new YouTubeNZ())
{
bool isExist = !db.Users.Any(X => X.EmailAddress == emailAddress);
return Json(isExist, JsonRequestBehavior.AllowGet);
}
}
Your Model should be like this
[Required]
[Remote("IsEmailExist","Account",ErrorMessage="This Email is already exists")]
public string EmailAddress{get;set;}
And Controller should be like this
public JsonResult IsEmailExist(string emailAddress)
{
return Json(isExist(emailAddress),JsonRequestBehavior.AllowGet);
}
private bool isExist(string emailAddress)
{
using (var db = new YouTubeNZ())
{
return !db.Users.Any(X => X.EmailAddress == emailAddress);
}
}

remote validation in mvc affected edit

I was trying to validate the user name through remote validation in client side and it's working fine in while adding the duplicate field in create Module but now it is not allowing me to edit the record using same name it's showing me the same error which I defined for create. I tried all the possible ways but not succeeded please help me. I have followed these link but it's not working in either way.
http://stackoverflow.com/questions/4778151/asp-net-mvc-3-remote-validation-to-allow-original-value
http://stackoverflow.com/questions/6407096/asp-net-mvc-3-remote-attribute-passing-3-fields
here is my code what i have tried so far .please help experts.
[Required]
[Remote("IsUserAvailable", "User", HttpMethod = "Post", ErrorMessage = "User already exist.", AdditionalFields = "InitialUserName")]
[RegularExpression(#"^(?![\W_]+$)(?!\d+$)[a-zA-Z0-9 ]+$", ErrorMessage = "Invalid UserName ")]
public string UserName { get; set; }
[HttpPost]
public JsonResult IsUserAvailable([Bind(Prefix = "User.UserName")]string UserName, string initialUserName)
{
var result = uDbContext.Users.FirstOrDefault(a => a.UserName == UserName);
if (result == null)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json(JsonRequestBehavior.AllowGet);
}
#model User.ViewModel.ViewModelUser
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(m => m.User.UserId)
#Html.LabelFor(m.User.UserName)
#Html.TextBoxFor(m => m.User.UserName)
#Html.ValidationMessageFor(m.User.UserName)
#Html.Hidden("initialUserName", Model.User)
</div>
</div>
}
Please help experts to complete my assignment.
User appears to be a complex object so
#Html.Hidden("initialUserName", Model.User)
is likely to generate something like
<input type="hidden" name="initialUserName" value="YourAssemly.User" ... />
which is not going to help with validation.
You could ignore the validation by sending back the original name using
#Html.Hidden("InitialUserName", Model.User.UserName)
#Html.Hidden("User.InitialUserName", Model.User.UserName)
and then compare the values in the controller using
public JsonResult IsUserAvailable([Bind(Prefix = "User.UserName")]string UserName, string initialUserName)
public JsonResult IsUserAvailable([Bind(Prefix = "User.UserName")]string UserName, [Bind(Prefix = "User.InitialUserName")]string initialUserName)
{
if (UserName == initialUserName)
{
// Nothing has changed so signal its valid
return Json(true, JsonRequestBehavior.AllowGet);
}
// Check if the user name already exists
var result = uDbContext.Users.FirstOrDefault(a => a.UserName == UserName);
return Json(result == null, JsonRequestBehavior.AllowGet);
}
Side note: jquery remote validation is a GET call so the [HttpPost] attribute is not necessary
Edit
After debugging both the jquery-validate.js and jquery-validate-unobtrusive.js files, it turns out that the name attribute of any AdditionalFields must include the same prefix as the property being validated, and that the [Bind(Prefix="..")] attribute is then also required on those parameters in the method (refer amendments above)
An alternative might to create a simple class to post back to, for example
public class ValidateUserNameVM
{
public string UserName { get; set; }
public string InitialUserName { get; set; }
}
and
public JsonResult IsUserAvailable([Bind(Prefix = "User")]ValidateUserNameVM model)
{
if (model.UserName == model.InitialUserName)
....
Your validation function is incomplete. Put a [Required] attribute on the UserName property of your model and try this:
public JsonResult IsUserAvailable(string userName, string initialUserName)
{
if (userName.Trim().ToLower() != (initialUserName ?? "").Trim().ToLower())
{
var result = YourMethodToCheckTheDatabaseForUsernameIsAvailable(userName);
return Json(result, JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
For Who Get Null in the second paramter this simple idea could help
public JsonResult IsUserNameAvailable(string Name, string EditNameIssue)
{//it will return true if match found elese it will return false. so i add !
if (Name == EditNameIssue)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
else
{
return Json(!db.Employees.Any(e => e.Name == Name), JsonRequestBehavior.AllowGet);
}
}
Go to The Class and add string EditNameIssue to the class so it could be sent to the controller
[MetadataType(typeof(EmployeeMetaData))]
public partial class Employee
{
public string EditNameIssue { get; set; }
}
And Edit the Remote attribute to send this addtional property
[Remote("IsUserNameAvailable","Employees",ErrorMessage ="User Name Already Taken",AdditionalFields = "EditNameIssue")]
public string Name { get; set; }
This Logic may help if you add a name to edit textbox that is already taken
public JsonResult IsUserNameAvailable(string Name, string EditNameIssue)
{//it will return true if match found elese it will return false. so i add !
//Edit Request
if (Name == EditNameIssue)
{
//this mean he didn't change the name
return Json(true, JsonRequestBehavior.AllowGet);
}
else if (Name != EditNameIssue)
{
//if he change the name in the edit go and check if the new name exist
//note if he modify and reenter it origin name it will be also erro he has to reload
return Json(!db.Employees.Any(e => e.Name == Name), JsonRequestBehavior.AllowGet);
}
else if (string.IsNullOrEmpty(EditNameIssue))
{//this mean you came from create request as there is no EditNameIssue in this view
return Json(!db.Employees.Any(e => e.Name == Name), JsonRequestBehavior.AllowGet);
}
else
{//just for the completeness
return Json(false, JsonRequestBehavior.AllowGet);
}
}

Validate multi-field in [MetadatType(typeof(myClass)]

I have a model class with [MetadataType(typeof(ThisEntityMetaData))] and [Bind(...)] annotations. I need to validate post back combined property values and a route parameter(viewType). The viewType is not a property of the entity class. So far I can only do this validation in [post] of the action. I'd like to do this validation in the entity class or the ThisEntityMetaData class. How can I do that? Thanks.
[HttpPost]
[ActionName("Create")]
[AcceptParameter(Name = "Save", Value = "Save")]
[ValidateInput(false)]
public ActionResult Create(int id, thisViewModel newViewModel,
string cancel, enumViewType viewType)
{
/* code omitted */
switch(viewType)
{
case enumViewType.OutAndNoReturn:
case enumViewType.OutAndReturn:
if(!thisEntity.Source.HasValue || !thisEntity.Reason.HasValue)
ViewData["Message"] = "Source, Reason are required.";
break;
case enumViewType.DirectOut:
case enumViewType.IndirectOut:
if ((!thisEntity.Source.HasValue || !thisEntity.Reason.HasValue ||
!thisEntity.Desired.HasValue))
{
thisEntity.ShowOutBlock = true;
ViewData["Message"] = "Source, Reason, Desired are required.";
return View(thisEntity);
}
break;
}
/* code omitted */
}
The viewType is not a property of the entity class.
You could use a real view model, not something that you have named view model but actually is not a view model at all:
[HttpPost]
[ActionName("Create")]
[AcceptParameter(Name = "Save", Value = "Save")]
[ValidateInput(false)]
public ActionResult Create(thisViewModel newViewModel)
{
...
}
where thisViewModel obviously contains everything you need:
[MetadataType(typeof(ThisEntityMetaData))]
public class thisViewModel
{
public int Id { get; set; }
public string Cancel { get; set; }
public enumViewType ViewType { get; set; }
...
}
Now inside your ThisEntityMetaData feel free to validate whatever you want in this view model.

How can use MVC DropDownlist

I have a problem with DropDownlist in MVC
I use ModelView in my application and this is my code
namespace MedicallexiconProject.ViewModel
{
public class WordViewModel
{
private readonly ICategoryService _categoryService;
public WordViewModel(ICategoryService categoryService)
{
_categoryService = categoryService;
var selectList = _categoryService.GetAllCategorysSelectList().
Select(x => new SelectListItem
{
Text = x.Name,
Value = x.ID.ToString()
}).ToList();
Categories = selectList;
}
public WordViewModel()
{
}
public string Name { get; set; }
private IList<SelectListItem> _categories;
public IList<SelectListItem> Categories
{
get
{
if (_categories == null)
{
_categories = new List<SelectListItem>();
}
return (_categories);
}
set { _categories = value; }
}
}
}
and this is my controller
[HttpGet]
public ActionResult Create()
{
var wordViewModel = new WordViewModel(_categoryService);
ViewBag.CategoryID = wordViewModel.Categories;
return View();
}
[HttpPost]
public ActionResult Create(WordViewModel wordViewModel)
{
Mapper.CreateMap<WordViewModel, Word>();
var word = new Word();
Mapper.Map(wordViewModel, word);
if (ModelState.IsValid)
{
_wordService.AddNewWord(word);
_uow.SaveChanges();
return RedirectToAction("Index");
}
return View(wordViewModel);
}
Now how can I insert dropdownlist in my View?
As AlfalfaStrange mentioned, you should not add logic in your ViewModel. That makes it ugly ! Keep your ViewModel simple POCO.
Add one more property in your ViewModel called "SelectedCategoryID" like this
public class WordViewModel
{
public int SelectedCategoryID { set;get;}
public IList<SelectListItem> Categories { set;get;}
public string Name { set;get;}
}
Initialize your Items (Categories) of your ViewModel in your GET method. Here i am calling a method called GetCategories which returns a list of categories.I can simply call the method wherever i want.
public ActionResult Create()
{
var model=new WordViewModel();
model.Categories=YourService.GetCategories();
return View(model);
}
In your strongly typed Create view , use this
#model WordViewModel
using(#Html.BeginForm())
{
#Html.DropDownFor(x=>x.SelectedCategoryID,
new SelectList(Model.Categories,"Value","Text"),"Select Category")
<input type="submit" value="Save" />
}
In your HttpPost action method , you can check for wordViewModel.SelectedCategoryID for the selected value.
[HttpPost]
public ActionResult Create(WordViewModel wordViewModel)
{
if(ModelState.IsValid)
{
//Checck for wordViewModel.SelectedCategoryID here now
}
//some validation failed. Let's reload the category data again.
wordViewModel.Categories=YourService.GetCategories();
return View(wordViewModel);
}
It's absolutely fine to include code that loads a dropdown list in your view model. A select list and a drop down are both "view" items.... they are not related to business logic and your controller and model need not know anything about SelectLists or SelectListItems or DropDownList, etc.

ASP.NET MVC does not add ModelError when invoking from unit test

I have a model item
public class EntryInputModel
{
...
[Required(ErrorMessage = "Description is required.", AllowEmptyStrings = false)]
public virtual string Description { get; set; }
}
and a controller action
public ActionResult Add([Bind(Exclude = "Id")] EntryInputModel newEntry)
{
if (ModelState.IsValid)
{
var entry = Mapper.Map<EntryInputModel, Entry>(newEntry);
repository.Add(entry);
unitOfWork.SaveChanges();
return RedirectToAction("Details", new { id = entry.Id });
}
return RedirectToAction("Create");
}
When I create an EntryInputModel in a unit test, set the Description property to null and pass it to the action method, I still get ModelState.IsValid == true, even though I have debugged and verified that newEntry.Description == null.
Why doesn't this work?
This is because model binding doesn't take place when you invoke an action from a test. Model binding is the process of mapping posted form values to a type and pass it as a parameter to an action method.

Resources