Razor pages identity profile image - asp.net-mvc

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>

Related

Creating a comment-like box in ASP.NET Core

I'm trying to create a comment and like box like the one shown in this tutorial what are the main changes I need to make in order to make it work with ASP.NET Core 3.1
https://www.c-sharpcorner.com/blogs/how-to-create-a-comment-box-session-as-like-facebook-comment-in-asp-net
To create a Facebook-like comment box in ASP.NET Core, I do a demo to make it, referring to official documentation to configure database:https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/adding-model?view=aspnetcore-5.0&tabs=visual-studio
1.To Create the comment box, we need to create User, Reply, Comment models to add some tables ,and add some ViewModels.
User.cs:
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Email { get; set; }
public string ImageUrl { get; set; }
public DateTime? CreateOn { get; set; }
}
Reply.cs:
public class Reply
{
[Key]
public int Id { get; set; }
[Required]
public string Text { get; set; }
public DateTime? CreateOn { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
public int CommentId { get; set; }
[ForeignKey("CommentId")]
public virtual Comment Comment{get;set;}
}
Comment.cs:
public class Comment
{
[Key]
public int Id { get; set; }
[Required]
public string Text { get; set; }
public DateTime? CreateOn { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
public ICollection<Reply> Replies { get; set; }
public ICollection<User> Users { get; set; }
}
LoginVM.cs:
public class LoginVM
{
[Required]
[Key]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
RegisterVM.cs:
public class RegisterVM
{
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
[Required]
public string Email { get; set; }
}
ReplyVM.cs:
public class ReplyVM
{
public string Reply { get; set; }
public int CID { get; set; }
}
Installs the EF Core package
From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for solution.
[Note]My core3.1 installed 3.1.21 version
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Microsoft.EntityFrameworkCore.Proxies
Microsoft.Extensions.Configuration.JSON
creates a database context class: Models/ApplicationDbContext.cs
public class ApplicationDbContext: DbContext
{
public ApplicationDbContext(DbContextOptions options) :base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Reply> Replies { get; set; }
public DbSet<commentbox.ViewModels.LoginVM> LoginVM { get; set; }
}
4.Add codes in Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddControllersWithViews();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext")));
}
5.Add app.UseSession(); in Startup.Configure
6.Add a connection string to the appsettings.json file:
"ConnectionStrings": {
"ApplicationDbContext": "Server=(localdb)\\mssqllocaldb;Database=ApplicationDbContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"}
7.In the Package Manager Console (PMC), enter the following commands separately:
Add-Migration InitialCreate
Update-Database
8.Add HomeController to set userId=0 when people first Login without register, add AccountController to manage user login or not, add ChatRoomController to manage user post comments or reply.
HomeController.cs:
public class HomeController : Controller
{ ....
public IActionResult Index()
{
ISession session = HttpContext.Session;
session.SetInt32("UserId", 0);
return View();
}
...
}
AccountController.cs:
public class AccountController : Controller
{
private readonly ApplicationDbContext db;
public AccountController(ApplicationDbContext context)
{
db = context;
}
//GET:Account/Register
[HttpGet]
public IActionResult Register()
{
return View();
}
//GET:Account/Register
[HttpPost]
public IActionResult Register(RegisterVM obj)
{
bool UserExistis=db.Users.Any(x=>x.UserName==obj.UserName);
if(UserExistis)
{
ViewBag.UserNameMessage = "This UserName is already in use, try another";
return View();
}
bool EmailExistis = db.Users.Any(y => y.Email == obj.Email);
if (EmailExistis)
{
ViewBag.EmailMessage = "This Email is already in use, try another";
return View();
}
//if username and email is unique, then we save or register the user
User u = new User();
u.UserName = obj.UserName;
u.Password = obj.Password;
u.Email = obj.Email;
u.ImageUrl = "";
u.CreateOn = DateTime.Now;
db.Users.Add(u);
db.SaveChanges();
return RedirectToAction("Index","ChatRoom");
}
//GET:Account/Login
[HttpGet]
public IActionResult Login()
{
return View();
}
//GET:Account/Login
[HttpPost]
public IActionResult Login(LoginVM obj)
{
bool existis = db.Users.Any(u => u.UserName == obj.UserName&&u.Password==obj.Password);
if(existis)
{
ISession session = HttpContext.Session;
session.SetInt32("UserId", db.Users.Single(x => x.UserName == obj.UserName).Id);
return RedirectToAction("Index","ChatRoom");
}
//if invalid credentials
ViewBag.Message = "Invalid Credentials!";
return View();
}
}
ChatRoomController.cs:
public class ChatRoomController: Controller
{
private readonly ApplicationDbContext db;
public ChatRoomController(ApplicationDbContext context)
{
db = context;
}
public IActionResult Index()
{
var comments = db.Comments.Include(x => x.Replies).ThenInclude(x=>x.User).OrderByDescending(x => x.CreateOn)
.ToList();
return View(comments);
}
//Post:ChatRoom/PostReply
[HttpPost]
public ActionResult PostReply(ReplyVM obj)
{
ISession session = HttpContext.Session;
int userId =(int)session.GetInt32("UserId");
if (userId==0)
{
return RedirectToAction("Login", "Account");
}
Reply r = new Reply();
r.Text = obj.Reply;
r.CommentId = obj.CID;
r.UserId =userId;
r.CreateOn = DateTime.Now;
db.Replies.Add(r);
db.SaveChanges();
return RedirectToAction("Index");
}
//Post:ChatRoom/PostComment
[HttpPost]
public ActionResult PostComment(string CommentText)
{
ISession session = HttpContext.Session;
int userId = (int)session.GetInt32("UserId");
if (userId == 0)
{
return RedirectToAction("Login", "Account");
}
Comment c = new Comment();
c.Text = CommentText;
c.CreateOn = DateTime.Now;
c.UserId = userId;
db.Comments.Add(c);
db.SaveChanges();
return RedirectToAction("Index");
}
}
9.Add views: Login.cshtml(for Login action in AccountController), Register.cshtml(for Register action in AccountController), index.cshtml(for Index action in ChatRoomController).
Login.cshtml:
#model commentbox.ViewModels.LoginVM
#{
ViewData["Title"] = "Login";
}
<h1>Login</h1>
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-4">
<form asp-action="Login">
<h4 class="text-danger">#ViewBag.Message</h4>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName" class="control-label"></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="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</form>
</div>
</div>
Register.cshtml:
#model commentbox.ViewModels.RegisterVM
#{
ViewData["Title"] = "Register";
}
<h1 class="text-success">Register User</h1>
<div class="row">
<div class="col-md-4">
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="UserName" class="control-label"></label>
<input asp-for="UserName" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
<p class="text-danger">#ViewBag.UserNameMessage </p>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
<p class="text-danger">#ViewBag.EmailMessage </p>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary" style="border-radius:20px"/>
</div>
</form>
</div>
</div>
Index.cshtml:
#model IEnumerable<commentbox.Models.Comment>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<h2 class="text-success text-center"> someone's post or some other things</h2>
#using (Html.BeginForm("PostComment", "ChatRoom", FormMethod.Post))
{
<input type="text" name="CommentText" placeholder="Type new comment..."
style="width:700px; height:60px; font-size:20px; margin-top:10px" />
<br />
<input type="submit" value="Post Comment" class="btn btn-success "
style="margin-top: 10px;margin-bottom: 10px " />
}
<table class="table text-center">
<tbody>
#foreach (var comment in Model)
{
<tr style="border:1px solid black;">
<td>
<span style="margin-right:15px;font-size:16px;color:green">
#Html.DisplayFor(modelItem => comment.User.UserName)
</span>
<span style="font-size:20px">
#Html.DisplayFor(modelItem => comment.Text)
</span>
<span style="margin-left:10px">
#Html.DisplayFor(modelItem => comment.CreateOn)
</span>
#foreach (var reply in comment.Replies)
{
<br />
<span style="margin-right:15px;font-size:16px;color:blue">
#Html.DisplayFor(modelItem => reply.User.UserName)
</span>
<span style="font-size:19px">
#reply.Text
</span>
<span style="margin-left:10px">
#reply.CreateOn
</span>
}
<br />
#using (Html.BeginForm("PostReply", "ChatRoom", FormMethod.Post))
{<input type="text" name="Reply" placeholder="Type reply..."
style="width:100%; height:60px; font-size:20px; margin-top:10px" />
<br />
<input type="hidden" name="CID" value="#comment.Id" />
<input type="submit" value="Post Reply" class="btn btn-success" style="margin-top :10px" />
}
</td>
</tr>
}
</tbody>
</table>
10.Add link in _Layout.cshtml.
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a>
</li>

When updating user information viewmodel is null

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.

validation error duplication in both forms

I've got two partial views, each contains a form. When i submit either of them, validation error appears as a duplicate in both partials. What am I missing, or probably have a double of.
I had to move each form in a separate partial view in order to give each of them its own Model.
My main view:
#inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
#model LoginRegisterViewModel
#{
Layout = "_Layout_";
}
#if (Model.LoginViewModel.EnableLocalLogin)
{
<div class="loginRegister">
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header loginpage">
<main class="fullHeight">
<div class="flexValign" id="overview">
<section class="section--center mdl-grid mdl-grid--no-spacing mdl-shadow--8dp roundedWrapper">
#Html.Partial("_LoginPartial", Model.LoginViewModel)
#Html.Partial("_RegisterPartial", Model.RegisterViewModel)
</section>
</div>
</main>
</div>
</div>
}
my partials:
#inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
#model RegisterViewModel
<div class="mdl-card mdl-cell mdl-cell--6-col-desktop mdl-cell--6-col-tablet mdl-cell--4-col-phone darkBckrnd">
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
<div class="fixedHeight mdl-grid mdl-card__supporting-text mdl-typography--text-center mdl-color-text--white">
<div class="innerWrapper">
#await Html.PartialAsync("_InputValidation")
<input type="hidden" asp-for="RegisterReturnUrl" />
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input type="password" class="mdl-textfield__input" asp-for="NewPassword" autocomplete="new-password">
<label class="mdl-textfield__label mdl-color-text--white" for="Password">#Localizer["password"]
&ast;</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input type="password" autocomplete="off" class="mdl-textfield__input" asp-for="NewPasswordAgain">
<label class="mdl-textfield__label mdl-color-text--white"
for="PasswordAgain">#Localizer["repeatPassword"]
&ast;</label>
</div>
</div>
</div>
<div class="btnContainer">
<button type="submit" value="register" name="button"
class="general mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored mdl-color--cyan-500 mdl-shadow--8dp"
id="RegisterButton">#Localizer["createAccount"]</button>
</div>
}
the other partial:
#inject Microsoft.Extensions.Localization.IStringLocalizer Localizer
#model LoginViewModel
<div class="mdl-card mdl-cell mdl-cell--6-col-desktop mdl-cell--6-col-tablet mdl-cell--4-col-phone white">
#using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
<div
class="fixedHeight mdl-grid mdl-card__supporting-text mdl-typography--text-center mdl-color-text--blue-grey-900">
<div class="innerWrapper">
<partial name="_InputValidation" />
<input type="hidden" asp-for="ReturnUrl" />
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" type="text" asp-for="Username">
<label class="mdl-textfield__label mdl-color-text--blue-grey-600"
for="Username">#Localizer["userNameOrEmail"]</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input type="password" class="mdl-textfield__input" asp-for="Password" autocomplete="off">
<label class="mdl-textfield__label mdl-color-text--blue-grey-600"
for="Password">#Localizer["password"]</label>
</div>
</div>
</div>
<div class="btnContainer">
<button type="submit" value="login" name="button"
class="general mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored mdl-color--cyan-500">
#Localizer["login"]</button>
</div>
}
Models:
using System;
using System.Collections.Generic;
using System.Linq;
namespace IdentityServer4.Quickstart.UI
{
public class LoginViewModel : LoginInputModel
{
public bool AllowRememberLogin { get; set; } = true;
public bool EnableLocalLogin { get; set; } = true;
public string LoginPage { get; set; } = "Login";
public IEnumerable<ExternalProvider> ExternalProviders { get; set; } = Enumerable.Empty<ExternalProvider>();
public IEnumerable<ExternalProvider> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
}
}
second model
using IdentityServer.DTOs;
using IdentityServer.Models;
namespace IdentityServer4.Quickstart.UI
{
public class RegisterViewModel
{
public string Email { get; set; }
public string NewPassword { get; set; }
public string NewPasswordAgain { get; set; }
public string RegisterReturnUrl { get; set; }
}
then two Models united. I have united these two models to cover them both in controller.
using System;
using System.Collections.Generic;
using System.Linq;
namespace IdentityServer4.Quickstart.UI
{
public class LoginRegisterViewModel
{
public LoginViewModel LoginViewModel { get; set; }
public RegisterViewModel RegisterViewModel { get; set; }
}
}
and controller:
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Username);
if (await _userManager.CheckPasswordAsync(user, model.Password))
{
var performAdditionalCheck = true;
// If the user has unknown provider let him pass
if (user.Provider == UserAuthentificationProvider.Unknown) performAdditionalCheck = false;
if (context != null)
{
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
if (client.Properties["forceAuthentification"] == "false") performAdditionalCheck = false;
}
if (performAdditionalCheck)
{
return await RedirectToAuth(model);
}
else
{
return await SignInUserWithModel(user, model);
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginRegisterViewModelAsync(model);
return View(vm.LoginViewModel.LoginPage, vm);
}
I've got two partial views, each contains a form. When i submit either of them, validation error appears as a duplicate in both partials.
You can refer to the following sample code snippet and this doc about "Client-side validation" to valid user inputs within multiple partial views.
Main View
#model LoginRegisterViewModel
#{
ViewData["Title"] = "LoginRegister";
}
<h1>LoginRegister</h1>
#await Html.PartialAsync("_LoginPartial", Model.LoginViewModel)
#await Html.PartialAsync("_RegisterPartial", Model.RegisterViewModel)
#section scripts{
<partial name="_ValidationScriptsPartial" />
}
_LoginPartial.cshtml
#model LoginViewModel
#using (Html.BeginForm("Login", "Account", FormMethod.Post))
{
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username" class="control-label"></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="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="login" class="btn btn-primary" />
</div>
}
_RegisterPartial.cshtml
#model RegisterViewModel
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NewPassword" class="control-label"></label>
<input asp-for="NewPassword" class="form-control" />
<span asp-validation-for="NewPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="NewPasswordAgain" class="control-label"></label>
<input asp-for="NewPasswordAgain" class="form-control" />
<span asp-validation-for="NewPasswordAgain" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="CreateAccount" class="btn btn-primary" />
</div>
}
Test Model Classes
public class LoginViewModel
{
[Required]
public string Username { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
}
public class RegisterViewModel
{
[Required]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string NewPassword { get; set; }
[Compare("NewPassword")]
[DataType(DataType.Password)]
public string NewPasswordAgain { get; set; }
}
Test Result

How to post several photos and several text related to each other to the controller

enter image description hereI want to post several photos and several texts related to each other to the controller.what should I do? My form is an Edit Form and I stored all the pictures and taxes separately. but now in Edit Form, I have to send them both together to the controller.
EditTempleViewModel
public class EditTempleViewModel
{
public string Date { get; set; }
public string Title { get; set; }
public string KeyWord { get; set; }
public int Pattern { get; set; }
public int Category { get; set; }
}
SectionViewModel
public class SectionViewModel
{
public int Id { get; set; }
public int PostId { get; set; }
public string Title { get; set; }
public List<string> Text { get; set; }
public IFormFileCollection Image { get; set; }
public string Pic { get; set; }
}
Form
#using Blogger.Models.BlogViewModels
#model EditTempleViewModel
<form id="editForm" enctype="multipart/form-data">
<div class="form-group">
<input asp-for="Title" type="text" class="form-control formClass" id="Title" placeholder="">
</div>
<div class="form-group">
<select asp-for="Category" asp-items="#ViewBag.Category" class="form-control formClass" id="Category">
<option value="" disabled selected>Select group</option>
</select>
</div>
<div class="form-group">
<label asp-for="Pattern"></label>
<select asp-for="Pattern" asp-items="#ViewBag.Pattern" class="form-control formClass ">
<option value="" disabled selected>select</option>
</select>
</div>
<div class="form-group div-textarea" id="inputTextarea">
<label asp-for="KeyWord"></label>
<textarea asp-for="KeyWord" class="form-control formClass" id="KeyWord" rows="1"></textarea>
</div>
<div id="c">
#foreach (var item in Model.sections)
{
<div class="form-group div-textarea" id="inputTextarea">
<label asp-for="#item.Text"></label>
<textarea name="Text" class="form-control formClass txtaraeClass" rows="3">#item.Text</textarea>
</div>
<div class="form-group div-img" id="inputImg">
<div class="custom-file">
<label class="custom-file-label" for="Image">Upload</label>
<input name="Image" type="file" class="custom-file-input formClass fileU" id="Image" multiple>
</div>
</div>
}
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btnEdit">submit</button>
</div>
</form>
Script
$(".btnEdit").click(function (evt) {
evt.preventDefault();
var fileupload = $(".fileU").get();
var files = fileupload;
let myfiles = []
for (var i = 0; i < files.length; i++) {
myfiles.push(files[i]);
}
var data = new FormData();
data.append("Title", $('#Title').val());
data.append("Category", $('#Category').val());
data.append("Pattern", $('#Pattern').val());
data.append("KeyWord", $('#KeyWord').val());
data.append("files", myfiles)
let json =
{
"Title": $('#Title').val(),
"Category": $('#Category').val(),
"Pattern": $('#Pattern').val(),
"KeyWord": $('#KeyWord').val(),
files: data,
images: data
}
var frm = $('#editForm').serialize();
$.ajax({
type: "post",
url: "/Home/Uploadfilesajax",
contentType :false,
processData: false,
data: data,
success: function (message) {
alert(message);
},
error: function () {
alert("there was error uploading files!");
}
});
});
Cotroller
[HttpPost]
public async Task<IActionResult> UploadFilesAjax(EditTemple model, IFormCollection files)
{
}
I have Null data in both model and files
enter image description here

MVC Url.Action causes NullReferenceException in post

I'm trying to have a button on my form page that takes me back to the previous view that requires a parameter, I'm passing a ViewModel into the view with the assigned value I'm using. If the button is un-commented the button works fine but the forms post sends a NullReferenceException, and if the button is commented the form works exactly as I want.
The button that breaks the form
<button type="button" onclick="location.href='#Url.Action("Assignments","Session", new { Model.CourseName })'">Go Back</button>
The Controller Code
public IActionResult CreateAssignment(string courseName)
{
CreateAssignmentModel assignmentModel = new CreateAssignmentModel();
assignmentModel.CourseName = courseName;
return View(assignmentModel);
}
[HttpPost]
public IActionResult CreateAssignment(CreateAssignmentModel assignment)
{
if (ModelState.IsValid)
{
ModelState.Clear();
return View(assignment.CourseName);
}
else
{
return View(assignment.CourseName);
}
}
public IActionResult Assignments(string courseName)
{
var assignments = storedProcedure.getAssignments(User.Identity.Name, courseName);
var AssignmentsView = new AssignmentsViewModel{CourseName = courseName};
foreach (var Assignment in assignments.ToList())
{
AssignmentsView.Assignments.Add(Assignment);
}
return View(AssignmentsView);
}
The Model Code
public class CreateAssignmentModel
{
public string UserName { get; set; }
public string CourseName { get; set; }
[Required]
public string AssignmentName { get; set; }
[Required]
public string AssignmentDescription { get; set; }
[Required]
public int TotalPoints { get; set; }
[Required]
public DateTime DueDate { get; set; }
}
The Form with Button
<button type="button" onclick="location.href='#Url.Action("Assignments","Session", new { Model.CourseName })'">Go Back</button>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="#ViewData["ReturnUrl"]" method="post">
<h4>Create an Assignment</h4>
<hr />
<div class="form-group">
<label asp-for="AssignmentName" class="control-label">Assignment Name</label>
<input asp-for="AssignmentName" class="form-control" placeholder="Assignment Name" />
<span asp-validation-for="AssignmentName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AssignmentDescription" class=" control-label">Assignment Description</label>
<input asp-for="AssignmentDescription" class="form-control" placeholder="Assignment Description" />
<span asp-validation-for="AssignmentDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="TotalPoints" class=" control-label">Total Points</label>
<input asp-for="TotalPoints" class="form-control" placeholder="Points" />
<span asp-validation-for="TotalPoints" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DueDate" class=" control-label">Due Date</label>
<input asp-for="DueDate" class="form-control" placeholder="Due Date" />
<span asp-validation-for="DueDate" class="text-danger"></span>
</div>
<br />
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
</div>
Sorry for the lack of brevity, I've looked at NullReferenceException solutions for my problem but none have worked.

Resources