I have a simple model and view. THough, the modelbinder seems to fail when trying to bind my model because I always receive NULL into my controller action. What am I doing wrong?
Razor code:
#model BikeSharing.Views.Shared.Widgets.Popups.LoginInputModel
#using (Ajax.BeginForm("Login",null, new AjaxOptions
{
UpdateTargetId = "login-partial-update",
HttpMethod = "POST"
}, new { id = "js-form-login" }))
{
#Html.TextBoxFor(x => x.Email, new {placeholder = "Email address"})
<div class="errormessage">
#Html.ValidationMessageFor(x=>x.Email)
</div>
#Html.PasswordFor(x => x.Password, new {placeholder = "Password"})
<div class="errormessage">
#Html.ValidationMessageFor(x => x.Password)
</div>
}
Controller Action:
[HttpPost]
public ActionResult Login(LoginInputModel lmod)
{
if (ModelState.IsValid)
{
// this code is never reached because lmod is always NULL
}
return PartialView("Widgets/Popups/_LoginInput", lmod);
}
Model code:
public class LoginInputModel
{
[Required(ErrorMessage = "Your email address is required.")]
[EmailAddress]
public string Email { get; private set; }
[Required(ErrorMessage = "Please provide your password.")]
[MinLength(6,ErrorMessage = "Your password is too short.")]
[MaxLength(50, ErrorMessage = "Your password is too long.")]
public string Password { get; private set; }
public LoginInputModel()
{
}
public LoginInputModel(string email, string password)
{
Email = email;
Password = password;
}
}
The form submit is done via jquery-unobtrusive-ajax and Ajax.BeginForm()
I am only firing it via $('#js-form-login').submit();
Rendered HTML in browser:
<form action="/Home/Login" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#login-partial-update" id="js-form-login" method="post" novalidate="novalidate">
<input data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="Your email address is required." id="Email" name="Email" placeholder="Email address" type="text" value="">
<div class="errormessage">
<span class="field-validation-valid" data-valmsg-for="Email" data-valmsg-replace="true"></span>
</div>
<input data-val="true" data-val-maxlength="Your password is too long." data-val-maxlength-max="50" data-val-minlength="Your password is too short." data-val-minlength-min="6" data-val-required="Please provide your password." id="Password" name="Password" placeholder="Password" type="password">
<div class="errormessage">
<span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span>
</div>
</form>
Try adding the FormBody prefix to hint to the ModelBinder to look in the POST body.
[HttpPost]
public ActionResult Login([FromBody]LoginInputModel lmod)
{
if (ModelState.IsValid)
{
// this code is never reached because lmod is always NULL
}
return PartialView("Widgets/Popups/_LoginInput", lmod);
}
Related
I am learning razor pages and followed several decent examples on Microsoft to create a very rudimentary application. I was wondering if anyone had any resources on how I could add a profile image to razor pages. I am scaffolding identity in Visual studio but nowhere can I find any tutorial on how to integrate an image.
I do have a simple model and controller that will save a file path to the DB but fail to integrate this to the razor templates.
If anyone has any resources for what I am looking to learn I would greatly appreciate it. Apologies if this is very basic, I'm probably not using the correct terminology to search in Google.
identity user in the Db.
I do have a simple model and controller that will save a file path to
the DB but fail to integrate this to the razor templates.
Model:
Let's assume you have Identity base Model as below:
public class IdentityUser
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now you would like to add profile picture functionality there:
So you could extent your class as below:
public class IdentityExtendedModel : IdentityUser
{
public string ImageName { get; set; }
public string ProfilePictureLocation { get; set; }
}
Identity View Model:
We don't want to manipulate our identity Model directly while submitting our form inputso I will use viewModel
public class IdentityViewModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Username")]
public string Username { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
[Display(Name = "Profile Picture")]
public byte[] ProfilePicture { get; set; }
}
View Upload Profile Picture:
#model DotNet6MVCWebApp.Models.IdentityViewModel
<form asp-action="CreateIdentityProfilePic" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName"></label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LastName"></label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PhoneNumber"></label>
<input asp-for="PhoneNumber" class="form-control" />
<span asp-validation-for="PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
</div>
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProfilePicture" style="width: 100%;"></label>
#if (Model.ProfilePicture != null)
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="data:image/*;base64,#(Convert.ToBase64String(Model.ProfilePicture))">
}
else
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="">
}
<input type="file"
accept=".png,.jpg,.jpeg,.gif,.tif"
asp-for="ProfilePicture"
class="form-control"
style="border:0px!important;padding: 0px;padding-top: 10px;padding-bottom: 30px;"
onchange="document.getElementById('profilePicture').src = window.URL.createObjectURL(this.files[0])" />
<span asp-validation-for="ProfilePicture" class="text-danger"></span>
</div>
</div>
</div>
</form>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateIdentityProfilePic(InputModel model, IFormFile profilePicture)
{
if (profilePicture == null || profilePicture.Length == 0)
{
return Content("File not selected");
}
var path = Path.Combine(_environment.WebRootPath, "ImageName/Cover", profilePicture.FileName);
using (FileStream stream = new FileStream(path, FileMode.Create))
{
await profilePicture.CopyToAsync(stream);
stream.Close();
}
if (model == null)
{
return Content("Invalid Submission!");
}
var identityModel = new IdentityModel
{
FirstName = model.FirstName,
LastName = model.LastName,
Username = model.Username,
PhoneNumber = model.PhoneNumber,
ImageName = profilePicture.FileName,
ProfilePictureLocation = path,
};
_context.Add(identityModel);
await _context.SaveChangesAsync();
return RedirectToAction("IdentityProfilePicList");
}
Note: When you would submit Upload profile Picture view this controller should receive the request.
Controller Profile Picture List:
public ActionResult IdentityProfilePicList()
{
var memberList = _context.identityModels.ToList();
return View(memberList);
}
View Profile Picure List:
#model IEnumerable<DotNet6MVCWebApp.Models.IdentityExtendedModel>
#{
ViewData["Title"] = "Index";
}
<div class="form-row">
<table>
<tr>
<td>
<a asp-action="Index" class="btn btn-success">Create New</a>
</td>
<form method="get" action="#">
<td style="padding-right:760px">
</td>
<td>
<input class="form-control" type="text" placeholder="Search for..." name="SearchString" value="#ViewData["CurrentFilter"]" aria-label="Search" aria-describedby="btnNavbarSearch" />
</td>
<td>
<input type="submit" value="Search" class="btn btn-primary" />
</td>
</form>
</tr>
</table>
</div>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Username)
</th>
<th>
#Html.DisplayNameFor(model => model.ProfilePictureLocation)
</th>
<th>
Member Details
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
<img src="~/ImageName/Cover/#item.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
</td>
<td>
<a asp-action="EditMember" class="btn btn-primary" asp-route-memberId="#item.Id">Details</a> | <a asp-action="EditMember" class="btn btn-warning" asp-route-memberId="#item.Id">Edit</a>
</td>
</tr>
}
</tbody>
</table>
Output:
Database:
Note: If you need any further assistance on this please check this GitHub repository.
Thanks for all the help it helped me massively to resolve the issue. At the end of registering the image in the scaffolded razor pages Identity I had to use the code behind file register.cshtl.cs and add it to the Input module there and be consumed by the register.cshtml which now works perfectly.
I hope this helps someone else.
First I extended the ApplicationUser and added the migration.
public class ApplicationUser : IdentityUser
{
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public int? CompanyId { get; set; }
[ForeignKey("CompanyId")]
[ValidateNever]
public Company Company { get; set; }
}
Then added to the Input model in the Register.cshtml.cs
public class InputModel
{
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
//Added custom fields
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
public string? PhoneNumber { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public string? Role { get; set; }
public int? CompanyId { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> RoleList { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> CompanyList { get; set; }
}`
Then in the on Post method add the file path to the database.
public async Task<IActionResult> OnPostAsync(IFormFile file, string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
string wwwRootPath = _hostEnvironment.WebRootPath;
if (file != null)
{
string fileName = Guid.NewGuid().ToString();
var uploads = Path.Combine(wwwRootPath, #"images\companies");
var extension = Path.GetExtension(file.FileName);
if (Input.ImageUrl != null)
{
var oldImagePath = Path.Combine(wwwRootPath, Input.ImageUrl.TrimStart('\\'));
}
using (var fileStreams = new FileStream(Path.Combine(uploads, fileName + extension), FileMode.Create))
{
file.CopyTo(fileStreams);
}
Input.ImageUrl = #"\images\companies\" + fileName + extension;
}
else
{
Input.ImageUrl = #"\images\companies\QPQ-logo.jpg";
}
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
user.Name = Input.Name;
user.StreetAddress = Input.StreetAddress;
user.City = Input.City;
user.PostalCode = Input.PostalCode;
user.PhoneNumber = Input.PhoneNumber;
user.ImageUrl = Input.ImageUrl;
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
if (Input.Role == null)
{
await _userManager.AddToRoleAsync(user, SD.Role_User_Indi);
}
else
{
await _userManager.AddToRoleAsync(user, Input.Role);
}
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}`
One of the first mistakes I made was not declaring the front-end form as a multipart form in the Register.cshtml.
<h1>#ViewData["Title"]</h1><div class="row pt-4">
<div class="col-md-8">
<form id="registerForm" class="row" asp-route-returnUrl="#Model.ReturnUrl" method="post" enctype="multipart/form-data">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating py-2 col-12">
<input asp-for="Input.Email" class="form-control" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Name" class="form-control" aria-required="true" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.StreetAddress" class="form-control" />
<label asp-for="Input.StreetAddress"></label>
<span asp-validation-for="Input.StreetAddress" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.City" class="form-control" />
<label asp-for="Input.City"></label>
<span asp-validation-for="Input.City" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.PostalCode" class="form-control" />
<label asp-for="Input.PostalCode"></label>
<span asp-validation-for="Input.PostalCode" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Password" class="form-control" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.ConfirmPassword" class="form-control" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.Role" asp-items="#Model.Input.RoleList" class=form-select>
<option disabled selected>-Select Role-</option>
</select>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.CompanyId" asp-items="#Model.Input.CompanyList" class=form-select>
<option disabled selected>-Select Company-</option>
</select>
</div>
<div class="form-floating py-2 col-12">
<label asp-for="Input.ImageUrl"></label>
<input asp-for="Input.ImageUrl" type="file" id="uploadBox" name="file" class="form-control" />
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-4">
<section>
<h3>Use another service to register.</h3>
<hr />
#{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</p>
</div>
}
else
{
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post" class="form-horizontal">
<div>
<p>
#foreach (var provider in Model.ExternalLogins)
{
<button type="submit" class="btn btn-primary" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account">#provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
</section>
</div>
I'm trying to accomplish login functionality using UserName instead of Email to Login.
I'm using Bootstrap Modal dialog for loging in users with jquery and ajax for error handling.
The UserName is not an email address so ModelState IsValid always returns false. If I hard code an email address in the javascrip file then all works.
Is there anyway around this issue. Any help would be much appreciated.
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Login</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="UserLoginForm" asp-controller="UserAuth" asp-action="login">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="LoginInValid" />
<div class="form-group row mb-3">
<label asp-for="UserName" class="col-md-4 col-form-label"></label>
<div class="col-md-8">
<input asp-for="UserName" placeholder="User Name" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</div>
<div class="form-group row mb-3">
<label asp-for="Password" class="col-md-4 col-form-label"></label>
<div class="col-md-8">
<input asp-for="Password" placeholder="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group-row">
<div class="col-md-8 offset-md-4">
<div class="form-check">
<input asp-for="RememberMe" class="form-check-input" />
<label asp-for="RememberMe" class="col-md-4" col-form-label"></label>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" id="login" name="login" class="btn btn-primary">Login</button>
</div>
</div>
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginModel loginModel)
{
loginModel.LoginInValid = "true";
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(loginModel.UserName, loginModel.Password, loginModel.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
loginModel.LoginInValid = "";
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt");
}
}
return PartialView("_UserLoginPartial", loginModel);
}
Error handling is not displayed
(function () {
var userloginButton = $("#UserLoginModal button[name='login']").click(onUserLoginClick);
function onUserLoginClick() {
var url = "UserAuth/Login";
var antiForgeryToken = $("#UserLoginModal input[name='__RequestVerificationToken']").val();
var username = $("#UserLoginModal input[name = 'UserName']").val();
var email = "mydomain.com"
/*var email = $("#UserLoginModal input[name = 'Email']").val();*/
var password = $("#UserLoginModal input[name = 'Password']").val();
var rememberMe = $("#UserLoginModal input[name = 'RememberMe']").prop('checked');
var userInput = {
__RequestVerificationToken: antiForgeryToken,
UserName: username,
Email: email,
Password: password,
RememeberMe: rememberMe
};
}
});
LoginModel
public class LoginModel
{
[Required]
[Display(Name = "User Name")]
[StringLength(25, MinimumLength = 2)]
public string UserName { get; set; }
[Required]
[StringLength(100, MinimumLength = 2)]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength (100, MinimumLength = 2)]
[DataType(DataType.Password)]
public string Password { get; set; }
public bool RememberMe { get; set; }
public string LoginInValid { get; set; }
}
LoginModel has UserName, Email, and Password all marked as "Required", so ModelState will not be valid unless all are provided.
You'll need to either provide values for each of these (I don't see any Email input in your HTML markup) or remove the [Required] annotation from the LoginModel properties you don't want to provide.
Error: NullReferenceException: Object reference not set to an instance of an object.
Web.Controllers.ManageController.ChangeUser(BaseViewModel model) in ManageController.cs
+
user.FirstName = model.ChangeUserViewModel.FirstName;
I cannot understand why I am getting this error, could you please help me find what I am doing wrong ?
What i am trying to achieve is updating the user information trough my viewmodel.
Can you please advise if the way i am trying to do it is correct?
BaseViewModel:
public class BaseViewModel
{
public IndexViewModel IndexViewModel { get; set; }
public ChangeUserViewModel ChangeUserViewModel { get; set; }
}
ChangeUserViewModel:
public class ChangeUserViewModel
{
[Required]
[StringLength(20, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 1)]
public string FirstName { get; set; }
[Required]
[StringLength(20, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 1)]
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Display(Name = "Profile Picture")]
[DataType(DataType.Upload)]
[MaxFileSize(5* 1024 * 1024)]
[AllowedExtensions(new string[] { ".jpg", ".png", ".jpeg", ".gif", ".tif" })]
public IFormFile ProfilePicture { get; set; }
}
Controller:
public async Task<IActionResult> Index()
{
var user = await GetCurrentUserAsync();
var model = new BaseViewModel
{
IndexViewModel = new IndexViewModel
{
HasPassword = await _userManager.HasPasswordAsync(user),
PhoneNumber = await _userManager.GetPhoneNumberAsync(user),
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user),
Logins = await _userManager.GetLoginsAsync(user),
BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user),
AuthenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user),
},
ChangeUserViewModel = new ChangeUserViewModel
{
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email
}
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangeUser(BaseViewModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("Index", "Manage");
}
var user = await GetCurrentUserAsync();
if (user != null)
{
user.FirstName = model.ChangeUserViewModel.FirstName;
user.LastName = model.ChangeUserViewModel.LastName;
user.Email = model.ChangeUserViewModel.Email;
await _userManager.UpdateAsync(user);
}
return View("Index", model);
}
View:
#model BaseViewModel
#inject UserManager<ApplicationUser> UserManager
#{
ViewData["Title"] = "Manage your account";
}
<h2 class="content-heading pt-0">
<i class="fa fa-fw fa-user-circle text-muted mr-1"></i> User Profile
</h2>
<form asp-controller="Manage" asp-action="ChangeUser" method="post" class="form-horizontal" role="form" enctype="multipart/form-data">
<div class="row push">
<div class="col-lg-4">
<p class="text-muted">
Your account’s vital info.
</p>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="col-lg-8 col-xl-5">
<div class="form-group">
<label for="dm-profile-edit-firstname">Firstname</label>
<input asp-for="ChangeViewModel.FirstName" type="text" class="form-control" id="dm-profile-edit-firstname" name="dm-profile-edit-firstname" >
</div>
<div class="form-group">
<label for="dm-profile-edit-lastname">Lastname</label>
<input asp-for="ChangeViewModel.LastName" type="text" class="form-control" id="dm-profile-edit-lastname" name="dm-profile-edit-lastname">
</div>
<div class="form-group">
<label for="dm-profile-edit-email">Email Address</label>
<input asp-for="ChangeViewModel.Email" type="email" class="form-control" id="dm-profile-edit-email" name="dm-profile-edit-email">
</div>
<div class="form-group">
<label>Your Avatar</label>
<div class="push">
<img class="img-avatar" src="#Url.Action("ProfilePicture", "Account" )" alt="">
</div>
<div class="custom-file">
<input asp-for="ChangeViewModel.ProfilePicture" type="file" class="custom-file-input js-custom-file-input-enabled" data-toggle="custom-file-input" id="ProfilePicture" name="ProfilePicture">
<label class="custom-file-label" for="ProfilePicture">Choose a new avatar</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-alt-primary">
<i class="fa fa-check-circle mr-1"></i> Update Profile
</button>
</div>
</div>
</div>
</form>
It seems the controller didn't recieve the BaseViewModel when do post request from view. I suggest you could use Newtonsoft’s Json.NET instead of System.Text.Json.
Step1. Install the following Nuget package
Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson
Step2.
If you are migrating from an existing project you’ll have a call to “AddMvc()” which you can then tack onto it like so :
services.AddMvc().AddNewtonsoftJson();
However in new .NET Core 3+ projects, you have a different set of calls replace MVC. So you’ll probably have one of the following :
services.AddControllers().AddNewtonsoftJson();
services.AddControllersWithViews().AddNewtonsoftJson();
services.AddRazorPages().AddNewtonsoftJson();
Then, place your breakpoint in your controller code to check the value of BaseViewModel model.
Help me to validate(client side validation) model attribute using asp.net core mvc.
Public class Ticket{
[StringLength(150), Required]
public string Name { get; set; }
[Required] [RegularExpression(#"^[\w-\.]+#([\w-]+\.)+[a-zA-Z+\.]*[a-zA-Z] {2,4}$", ErrorMessage = "Invalid {0}")]
public string Email { get; set; }
}
I am using Ticket model and binding ticket model to html.Html is as below
<label asp-for="Name">Name</label>
#(Html.Kendo().TextBoxFor(x => x.Customer.Name).HtmlAttributes(new { style = "width:100%;", #class = "form-control", }))
<span asp-validation-for="Name" class="text-danger"></span>
<label asp-for="Email">Email</label>
#(Html.Kendo().TextBoxFor(x => x.Customer.Email).HtmlAttributes(new { style = "width:100%;", #class = "form-control"}))
#*<span asp-validation-for="Email" class="text-danger"></span>*#
#Html.ValidationMessageFor(x => x.Customer.Email, "", new { #class = "text-danger" })
But here it is not validating an email input or Name input length. And in html markup their is no required attribute or max-length attributes. Below is Html Markup
<div class="form-group">
<label asp-for="Name">Name</label>
<input class="k-textbox form-control" id="Customer_Name" name="Customer.Name" style="width:100%;" value="">
<span asp-validation-for="Name" class="text-danger"></span>
</div>
Html markup is generated without any required attribute or regex pattern for email.
I am missing anything?. Help me out what i need add more for client side validation.
So I have been making a simple contact form, and have written the code, and it sends the form, I receive it but it comes blank, no subject and no body.
Reviewing similar issues here on stackoverlow I found A LOT of php solutions but no asp.net that would come helpful in my scenario.
Here is the controller:
public ActionResult Send(Contact c)
{
if (ModelState.IsValid)
{
try
{
MailAddress sender = new MailAddress(c.Email, c.Name);
MailAddress recipient = new MailAddress("mymail#hotmail.com");
MailMessage Message = new MailMessage();
Message.From = sender;
Message.To.Add(recipient);
Message.Subject = c.Subject;
Message.Body = c.Msg;
Message.IsBodyHtml = true;
SmtpClient client = new SmtpClient("smtp.gmail.com");
client.EnableSsl = true;
client.Port = 587;
client.Credentials = new NetworkCredential("gmail#gmail.com", "password");
client.Send(Message);
return Redirect("/Success.html");
}
catch (Exception)
{
return Redirect("/Error.html");
}
}
else
{
return Redirect("www.google.com");
}
}
HTML:
<body>
<form action="/Mail/Send" method="post">
<div id="glavni">
<p>
<label for="TextName">Name:</label>
<input id="TextName" type="text" name="Name" required autofocus />
</p>
<p>
<label for="TextSubject">Subject:</label>
<input id="TextSubject" type="text" name="Subject" required />
</p>
<p>
<label for="TextMail">Email:</label>
<input id="TextMail" type="email" name="Email" required />
</p>
<p>
<label for="TextMsg">Unesite Poruku:</label>
<textarea id="TextMsg" type="text" name="Msg" rows="12" cols="20" ></textarea>
</p>
<p>
<button type="submit">Send</button>
</p>
</div>
</form>
MODEL:
public class Contact
{
public string Name { get; set; }
public string Email { get; set; }
public string Subject { get; set; }
public string Msg {get; set;}
}
The Contact Models class props have the same names as html name attributes and for some reason sends empty emails.
Hope someone can shed some light!
Default all the actions in controllers are GET methods. So you have to specify the HTTP web method as POST
[HttpPost]
public ActionResult Send(Contact c)