Home > Mobile >  Razor pages identity profile image
Razor pages identity profile image

Time:08-31

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.

CodePudding user response:

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 >
        <div >
            <div asp-validation-summary="ModelOnly" ></div>
            <div >
                <label asp-for="FirstName"></label>
                <input asp-for="FirstName"  />
                <span asp-validation-for="FirstName" ></span>
            </div>
            <div >
                <label asp-for="LastName"></label>
                <input asp-for="LastName"  />
                <span asp-validation-for="LastName" ></span>
            </div>
            <div >
                <label asp-for="Username"></label>
                <input asp-for="Username"  />
                <span asp-validation-for="Username" ></span>
            </div>
            <div >
                <label asp-for="PhoneNumber"></label>
                <input asp-for="PhoneNumber"  />
                <span asp-validation-for="PhoneNumber" ></span>
            </div>
            <button id="update-profile-button" type="submit" >Save</button>
        </div>
        <div >
            <div asp-validation-summary="ModelOnly" ></div>
            <div >
                <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"
                       
                       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" ></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 >
    <table>
        <tr>
            <td>
                <a asp-action="Index" >Create New</a>

            </td>
            <form method="get" action="#">

            <td style="padding-right:760px">
            </td>
            <td>
                <input  type="text" placeholder="Search for..." name="SearchString" value="@ViewData["CurrentFilter"]" aria-label="Search" aria-describedby="btnNavbarSearch" />
            </td>
            <td>
                <input type="submit" value="Search"  />
            </td>
            </form>
        </tr>
    </table>
</div>


<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"
                     
                     height="50" width="75"
                     style="border:1px"
                     asp-append-version="true" accept="image/*" />
                   
                </td>
                <td>
                    <a asp-action="EditMember"  asp-route-memberId="@item.Id">Details</a> |  <a asp-action="EditMember"  asp-route-memberId="@item.Id">Edit</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Output: enter image description here

Database:

enter image description here

Note: If you need any further assistance on this please check this GitHub repository.

CodePudding user response:

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 >
<div >
    <form id="registerForm"  asp-route-returnUrl="@Model.ReturnUrl" method="post" enctype="multipart/form-data">
        <h2>Create a new account.</h2>
        <hr />
        <div asp-validation-summary="ModelOnly" ></div>

        <div >
            <input asp-for="Input.Email"  aria-required="true" />
            <label asp-for="Input.Email"></label>
            <span asp-validation-for="Input.Email" ></span>
        </div>

        <div >
            <input asp-for="Input.Name"  aria-required="true" />
            <label asp-for="Input.Name"></label>
            <span asp-validation-for="Input.Name" ></span>
        </div>
       
        <div >
            <input asp-for="Input.StreetAddress"  />
            <label asp-for="Input.StreetAddress"></label>
            <span asp-validation-for="Input.StreetAddress" ></span>
        </div>
        <div >
            <input asp-for="Input.City"  />
            <label asp-for="Input.City"></label>
            <span asp-validation-for="Input.City" ></span>
        </div>
        <div >
            <input asp-for="Input.PostalCode"  />
            <label asp-for="Input.PostalCode"></label>
            <span asp-validation-for="Input.PostalCode" ></span>
        </div>
        <div >
            <input asp-for="Input.Password"  aria-required="true" />
            <label asp-for="Input.Password"></label>
            <span asp-validation-for="Input.Password" ></span>
        </div>
        <div >
            <input asp-for="Input.ConfirmPassword"  aria-required="true" />
            <label asp-for="Input.ConfirmPassword"></label>
            <span asp-validation-for="Input.ConfirmPassword" ></span>
        </div>
        <div >
            <select asp-for="Input.Role" asp-items="@Model.Input.RoleList" class=form-select>
                <option disabled selected>-Select Role-</option>
            </select>

        </div>
        <div >
            <select asp-for="Input.CompanyId" asp-items="@Model.Input.CompanyList" class=form-select>
                <option disabled selected>-Select Company-</option>
            </select>

        </div>
        <div >
            <label asp-for="Input.ImageUrl"></label>
            <input asp-for="Input.ImageUrl" type="file" id="uploadBox" name="file"  />
        </div>
        <button id="registerSubmit" type="submit" >Register</button>
    </form>
</div>
<div >
    <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" >
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                            {
                                    <button type="submit"  name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                            }
                            </p>
                        </div>
                    </form>
            }
        }
    </section>
</div>
  • Related