MVC razor page, string not being sent to custom verifier - asp.net-mvc

i am new to microsofts MVC, and have been trying to make a custom validator.
i managed to call the custom validator, but the parameter is not being sent.
this is the model:
[Remote("VerifyEmail", "CustomValidation")]
public string UserEmail { get; set; }
and here is the controller:
public IActionResult VerifyEmail(string UserEmail)
{
bool EmailUsed = Db.Users.Where(x => x.UserName == UserEmail).Any();
if (EmailUsed)
{
return Json($"Email {UserEmail} is already in use.");
}
return Json(true);
}
the string Username in the custom validator is null. any ideas why?

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);
}
}

mvc 4 custom format validator does not show error and allows the form to submit

I have the following validator for a MVC 4 view
public class IpFormatValidator: ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ValidationResult resp;
if (value != null)
{
string ip ;
IPAddress noUsed ;
ip = value.ToString();
if(IPAddress.TryParse(ip, out noUsed))
{
resp = ValidationResult.Success;
}
else
{
resp = new ValidationResult("Please enter a valid IP Address.");
}
}
else
{
resp = new ValidationResult("" + validationContext.DisplayName + " is required");
}
return resp;
}
}
And in the model I set it to the required field like this
[IpFormatValidator]
[Required]
[Display(Name = "IP Address")]
public String ipAddress { get { return nullOrTrim(this.ipAddressk__BackingField); } set { ipAddressk__BackingField = value.Trim(); } }
I have placed a breakpoint in the validator and I have seend that is being called and is returning the expected value but still the form does not show an error when an invalid IP address is typed.
While looking for an answer another post similar to this mention to include this at the end of the _Layout.cshtml
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
#RenderSection("scripts", required: false)
In order to get client side validation, your attribute must implement IClientValidatable and you must include a client side script that adds a method to the client side $.validator object.
This article THE COMPLETE GUIDE TO VALIDATION IN ASP.NET MVC 3 gives some good examples of how to implement it.
However from the comments, you can do this using the built-in RegularExpressionAttribute which will give you both server side and client side validation.
[RegularExpression("..your regex..",ErrorMessage = "Invalid IP Address")]
public String ipAddress { get; set; }
You can also create your own attribute based on RegularExpressionAttribute
namespace yourProject.Validation
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class IPAddressAttribute : RegularExpressionAttribute
{
public IPAddressAttribute() : base(#"..your regex..")
{
// Add default error message
ErrorMessage = "Invalid IP Address";
}
}
}
And then register it in the global.asax.cs file
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(IPAddressAttribute), typeof(RegularExpressionAttributeAdapter));
and use it as
[IPAddress]
public String ipAddress { get; set; }

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);
}
}

How to check in Razor view if ApplicationUser.Roles contains certain role?

I have 3 roles in my webapp: Admin,Moderator,User.
I have a user #model WebApplication2.Models.ApplicationUser I want to check inside Razor view if user.Roles contains role Moderator. How to do so? I tried #if(#Model.Roles.Contains(DON'T_KNOW_WHAT_TO_WRITE_HERE){}.
NOTE: I am not asking how to check if currently authorized user is in certain role.
What you could do is create an extension method on IPrincipal that operates the same way as User.IsInRole(...)
public static bool IsInAppRole(this IPrincipal user, string role)
{
using(var db = new MyEntities())
{
var dbUser = db.Users.Find(user.Identity.GetUserId());
return dbUser.Roles.Any(r => r.RoleName == role)
}
}
Import extensions into a view
#using MyApplication.Web.Extensions
Use like you would IsInRole()
#if(User.IsInAppRole("Admin"))
{
}
Though, not sure why you'd do this as the user's roles can be put into their Identity object.
The simplest way that I could find is to use:
#Model.Roles.SingleOrDefault().RoleId
This, of course, requires you to work with the ID rather than the name in your comparison. Without creating a new ViewModel, I have not found a good way to get direct access to the name.
EDIT: If multiple roles are assigned, you should be able to do something like this:
#{
var isMod = false;
}
foreach (var r in Model.Roles)
{
if(r.RoleId == "1")
{
isMod = true;
}
}
I think you should use User.IsInRole(...) in your view code
Why don't you just print them out to preview possible values?
Your code seems to have minor bug to me. I believe it should be
#if(Model.Roles.Contains(DON'T_KNOW_WHAT_TO_WRITE_HERE){}
(just one '#')
EDIT:
Since Model.Roles are just plain Many-To-Many references, you need to call UserManager to obtain user roles. For example:
public class UserDetailsModel {
public string Id { get; set; }
public ApplicationUser User { get; set; }
public IList<string> Roles { get; set; }
}
and Details action in controller:
public ActionResult Details(string id) {
var model = new UserDetailsModel
{
Id = id,
User = UserManager.FindById(id),
Roles = UserManager.GetRoles(id)
};
return View(model);
}
You can get UserManager from OwinContext or inject it in controller:
private readonly ApplicationUserManager _userManager;
public UsersController(){}
public UsersController(ApplicationUserManager userManager) {
_userManager = userManager;
}
public ApplicationUserManager UserManager {
get {
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}

How to omit/prevent data from being sent to the POST method in the Controller in MVC

I have a view that is using a model and I am using that information to create a form.
I have three steps of the form that are optional or may not be shown.
The problem is that these hidden sections get posted along with the form data and break the business logic. (I have no control over the business logic)
So is there a way to tell the framework not to pass certain sections or fields? Perhaps VIA a class or something?
I know I could use AJAX to send certain sections as they are needed, but the site spec is to have them hidden and displayed as needed.
Although you could do this client-side, it won't stop malicious over-posting/mass assignment.
I suggest reading 6 Ways To Avoid Mass Assignment in ASP.NET MVC.
Excerpts:
Specify Included Properties only:
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
Specify Excluded Properties only:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Use TryUpdateModel()
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel(user, includeProperties: new[] { "FirstName" });
// ...
}
Using an Interface
public interface IUserInputModel
{
string FirstName { get; set; }
}
public class User : IUserInputModel
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
[HttpPost]
public ViewResult Edit()
{
var user = new User();
TryUpdateModel<IUserInputModel>(user);
// ...
}
Use the ReadOnlyAttribute
public class User
{
public string FirstName { get; set; }
[ReadOnly(true)]
public bool IsAdmin { get; set; }
}
Lastly, and the most recommended approach is to use a real ViewModel, instead a domain Model:
public class UserInputViewModel
{
public string FirstName { get; set; }
}
Show/Hide will not allow/disallow the value from being sent to the Controller.
Elements that are Disabled or just not editable will (99% of the time) be returned as null / minVal.
You can set the elements in the View as Disabled by using JQuery in the script:
$('#elementID').attr("disabled", true);
OR you could use a DOM command:
document.getElementById('elementID').disabled = "true";
So you can set the fields as both Disabled AND Hidden, so that it is neither displayed, nor populated. Then in your Controller you can just base the Business Logic on whether or not certain fields (preferable Mandatory fields, if you have any) are null.
You can check this in C# like this:
For a string:
if (string.IsNullOrWhiteSpace(Model.stringField))
{
ModelState.AddModelError("stringField", "This is an error.");
}
For a DateTime:
if (Model.dateTimeField == DateTime.MinValue)
{
ModelState.AddModelError("dateTimeField ", "This is an error.");
}
Just for interest sake, here is how you can Hide/Show elements on the View using JQuery:
$('#elementID').hide();
$('#elementID').show();

Resources