I'm trying to remote validate some code and for the parameter, its passing undefined in as a parameters. Here is my validation code:
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class ValidationController : Controller
{
public JsonResult IsUserNameAvailable(string userName, int? UserId)
{
var users = new BusinessLayer.BdsAdmin.Users();
if (UserId == null || UserId == 0)
// Do something
else // Do something else
if (users.Count == 0)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
string msg = string.Format("{0} is already taken and is not available.", userName);
return Json(msg, JsonRequestBehavior.AllowGet);
}
}
Here is my model:
public class EditUserAdministrationViewModel
{
public int UserId { get; set; }
[Required(ErrorMessage = "You must enter a user name.")]
[Display(Name = "User Name")]
[Remote("IsUserNameAvailable", "Validation", AdditionalFields = "UserId")]
public string UserName { get; set; }
// More properties
}
Looking at the request in Fiddler, here is what I see:
GET /Validation/IsUserNameAvailable?UserName=sara&UserId=undefined
Why is MVC injecting the string undefined into the request instead of the actual UserId?
You need to add
#Html.HiddenFor(m=>m.UserId)
at the view so that the binder will bind it to the remote validation controller or otherwise there is no value to bind
Related
i am little confused about my code:
Here is some function from my controller:
public void signIn(string userName, string userPass)
{
User user = new User();
user.getUser(userName , userPass);
if (user.userName != null)
{
Response.Redirect("/Home/Menu");
}
else
{
Response.Redirect("/Index/Index?Fail=" + "fail");
}
}
the " user.getUser" suppose to return a User object.. here is the code from my Model directory:
public class User
{
public ObjectId _id { get; set; }
public string userName { get; set; }
public string userPass { get; set; }
public User getUser(string name , string pass)
{
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("testdb");
var coll = db.GetCollection<User>("user");
List<User> list = coll.Find(x => x.userName == name && x.userPass == pass).ToList<User>();
User uObj = new User();
uObj = list.FirstOrDefault();
return uObj;
}
}
when i am debugging the code i can see the uJob object contain values. but when the function end and i return to the controller i see that the user object contain only null values, and the condition - " if (user.userName != null)" is returning FALSE!.. instead of TRUE..
i would like to get some help. Thanks !
You have to assign it.
user = user.getUser(userName , userPass);
Either you assign the value returned by the getUser method in calling program like this
user = user.getUser(userName , userPass);
Or you change the code in Model like this
public class User
{
public ObjectId _id { get; set; }
public string userName { get; set; }
public string userPass { get; set; }
public void getUser(string name , string pass)
{
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("testdb");
var coll = db.GetCollection<User>("user");
var user = coll.FirstOrDefault(x => x.userName == name && x.userPass == pass);
if(user != null)
{
this._id = user._id;
this.userName = user.userName;
this.userPass = user.userPass;
}
}
}
if you replace
if (user.userName != null)
with
if ( user.getUser(userName , userPass).userName != null)
wil works for you.
When Testing, I have attempted to match the input from my RegisterViewModel, no matter what different input I have tried using in postman to post the user it does not work, unsure if its something to do with the method or my input in postman.
The RegisterViewModel looks like this:
public class RegisterViewModel
{
[Required]
public string Username { get; set; }
[Required, DataType(DataType.Password)]
public string Password { get; set; }
[Required, DataType(DataType.Password)]
public string ReEnterPassword { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Heres my attempted input into postman:
{
"username" : "user01",
"password" : "test123",
"reEnterPassword" : "test123",
"email" : "user01#example.com",
"firstName" : "Joe",
"lastName" : "Bloggs"
}
The method being used:
[HttpPost("api/auth/register")]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> NotWrkingRegister([FromBody] RegisterViewModel user)
{
if (user.Password != user.ReEnterPassword)
{
ModelState.AddModelError(string.Empty, "Password don't match");
return BadRequest();
}
try
{
var newUser = Mapper.Map<RegisterViewModel, AppUser>(user);
newUser.UserName = user.Username;
newUser.APIKey = Guid.NewGuid();
//newUser.Email = user.Email;
var userCreationResult = await userManager.CreateAsync(newUser, user.Password);
if (!userCreationResult.Succeeded)
{
foreach (var error in userCreationResult.Errors)
ModelState.AddModelError(string.Empty, error.Description);
return View(user);
}
else
{
string confirmationToken = userManager.
GenerateEmailConfirmationTokenAsync(newUser).Result;
string confirmationLink = Url.Action("ConfirmEmail",
"Account", new
{
userid = newUser.Id,
token = confirmationToken
},
protocol: HttpContext.Request.Scheme);
var callbackUrl = Url.Action("confirmEmail", "api/auth",
new { userId = newUser.Id.ToString(), code = confirmationToken },
protocol: HttpContext.Request.Scheme);
callbackUrl = $"http://localhost:49181/api/auth/confirmemail?userId=" + newUser.Id.ToString() + $"&token={confirmationToken}";
SendEmail(callbackUrl, newUser);
}
UserViewModel _userVM =
Mapper.Map<AppUser, UserViewModel>(newUser);
return new OkObjectResult(_userVM);
}
catch (Exception ex)
{
_logger.LogError($"Exception thrown while creating JWT: {ex}");
}
return BadRequest();
}
Working now, there were duplicated values already registered within the database. After commenting out the[ValidateAntiForgeryToken] and running through the code break pointing each individual component, found the problem and rectified and got desired output.
I am trying to remove the password captured in the parameters from my action filter and replace it with the word "Removed", in order for the parameters to be stored in the database for logging. The password is stored in a ViewModel (depending on the action). Below is sort of a "pseudo-code" as to what I am trying to achieve.
How would I go about masking/replacing the password to be saved in the database? The main issue I am having is that I do not know how to access the password parameter and change it. I have tried getting it using the actionParams.TryGetValue("model, out value) but the problem is that I do not know the type of value and it changes depending on the action. Also, I am unable to call many methods on actionParams["model"] (such as contains)
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If actionParam["model"].ToLower().Contains("password")
// actionParam["model"]["password"] = "Removed";
// If actionParam["model"].ToLower().Contains("confirm password")
// actionParam["model"]["confirm password"] = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}
Example of possible view models
public class LoginViewModel
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage="Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Example of possible action parameters
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
One approach would be to use an interface as an abstraction so you are not dealing directly with a ViewModel. First, create some interfaces for the action filter to interact with.
public interface IPassword
{
string Password { get; set; }
}
public interface IConfirmPassword
{
string ConfirmPassword { get; set; }
}
Next, make your ViewModel classes implement those interfaces.
public class LoginViewModel : IPassword
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage = "Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel : IPassword, IConfirmPassword
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Then it is just a matter of updating your filter code. The filter doesn't need to know anything more about your model other than the fact that it implements IPassword or IConfirmPassword, which it can check with a cast.
Of course, for it to work correctly, you have to restore the original values before executing the action method (or alternatively do the logging after the action is run) so the action method will have the correct values.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
IPassword password = null;
IConfirmPassword confirmPassword = null;
string originalPassword;
string originalConfirmPassword;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If the model doesn't implement the interface, the result
// here will be null.
password = actionParam["model"] as IPassword;
confirmPassword = actionParam["model"] as IConfirmPassword;
}
if (password != null)
{
// Store the original value so it can be restored later
originalPassword = password.Password;
password.Password = "Removed";
}
if (confirmPassword != null)
{
// Store the original value so it can be restored later
originalConfirmPassword = confirmPassword.ConfirmPassword;
confirmPassword.ConfirmPassword = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Restore the original values
if (password != null)
{
password.Password = originalPassword;
}
if (confirmPassword != null)
{
confirmPassword.ConfirmPassword = originalConfirmPassword;
}
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}
I have a user class with annotations for required fields.
public class User
{
[DataMember(Name = "firstName")]
[Required(ErrorMessage="FIST_NAME_REQUIRED")]
public string FirstName { get; set; }
[DataMember(Name = "lastName")]
[Required(ErrorMessage = "LAST_NAME_REQUIRED")]
[Custom(ErrorMessage = "CUSTOM_MESSAGE")]
public string LastName { get; set; }
}
This class is the argument of a POST API call.
[HttpPost]
public HttpResponseMessage Create(User request)
{
var response = new ApiResponse();
if (request != null && ModelState.IsValid)
{
[Code here]
}
else
{
response.Success = false;
response.Message = ModelState.Values.Count() > 0 ModelState.Values.Select(value => value.Errors).Select(error => error.First().ErrorMessage).Aggregate((result, next) => result + ", " + next) : string.Empty ;
return Request.CreateResponse(HttpStatusCode.OK, response);
}
}
My problem is when I call the API controller action with no first name for example, I get the default error message "The FirsName property is required." instead of my custom error message "FIRST_NAME_REQUIRED".
The error message for the Custom validator works fine though.
Couldn't find any clue about it on Google so it might be something very specific to my code but I can't think of anything.
Any idea ?
I've made a Profile Model that stores certain property values. Such as firstName, lastName... etc... Among others state is one of them.
Now problem occurred when I've replaced TextBox with DropDownList for State property.
This is the way I've made my Edit method in ProfileController.
When opened app will populate if any existing values. First issue, how to get selected value from the dropdown, so i can pass it into Profile property like I did in this method.
public ActionResult Edit(string username)
{
ViewBag.StateID = new SelectList(db.States, "StateID", "StateName");
ProfileBase _userProfile = ProfileBase.Create(username);
ProfileModel _profile = new ProfileModel();
System.Web.HttpContext.Current.Session["_userName"] = username;
if (_userProfile.LastUpdatedDate > DateTime.MinValue)
{
_profile.FirstName = Convert.ToString(_userProfile.GetPropertyValue("FirstName"));
_profile.LastName = Convert.ToString(_userProfile.GetPropertyValue("LastName"));
_profile.Address = Convert.ToString(_userProfile.GetPropertyValue("Address"));
_profile.City = Convert.ToString(_userProfile.GetPropertyValue("City"));
_profile.State = Convert.ToString(_userProfile.GetPropertyValue("State"));
_profile.Zip = Convert.ToString(_userProfile.GetPropertyValue("Zip"));
}
return View(_profile);
}
This worked fine when State was a string passed in TextBox, and then saved with Edit post method.
[HttpPost]
public ActionResult Edit(ProfileModel model)
{
if (ModelState.IsValid)
{
ProfileBase profile = ProfileBase.Create(System.Web.HttpContext.Current.Session["_userName"].ToString(), true);
if (profile != null)
{
profile.SetPropertyValue("FirstName", model.FirstName);
profile.SetPropertyValue("LastName", model.LastName);
profile.SetPropertyValue("Address", model.Address);
profile.SetPropertyValue("City", model.City);
profile.SetPropertyValue("State", model.State);
profile.SetPropertyValue("Zip", model.Zip);
profile.Save();
}
else
{
ModelState.AddModelError("", "Error writing to Profile");
}
}
return RedirectToAction("Index");
}
This is how I created dropdown for State.
Model:
public class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public IEnumerable<RegisterModel> RegModel { get; set; }
public IEnumerable<ProfileModel> Profiles { get; set; }
}
Controller:
ViewBag.StateID = new SelectList(db.States, "StateID", "StateName");
View:
#Html.DropDownList("StateID", (SelectList)ViewBag.StateID, new { #class = "dropdown" })
I've tried several things. No luck so far. What am I missing?!