Search code examples
asp.net-mvcasp.net-corerazorrazor-pagesidentity

Razor pages identity profile image


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.


Solution

  • 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>