Open Closed

Customized Register page is not saving the values of new fields. #4265


User avatar
0
oshabani created

Customized Register page is not saving the values of new fields.

  • ABP Framework version: v6.0.2
  • UI type: MVC
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes
  • Exception message and stack trace: No Exception
  • Steps to reproduce the issue:"

Step 1: Copied the Register.cshtml and Register.cshtml.cs to Pages \ Account folder to cusomize (AuthServer project). Register.cshtml

@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@using Volo.Abp.Account.Public.Web.Security.Recaptcha
@using Volo.Abp.Account.Settings
@using Volo.Abp.Settings
@model Sip.Iam.AuthServer.Web.Pages.Account.RegisterModel
@inject IHtmlLocalizer<AccountResource> L
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@inject ISettingProvider SettingProvider
@{
    PageLayout.Content.Title = L["Register"].Value;
    var reCaptchaVersion = await SettingProvider.GetAsync<int>(AccountSettingNames.Captcha.Version);
}

@if (!Model.LocalLoginDisabled)
{
    @section scripts
    {
        @if (Model.UseCaptcha)
        {
            if (reCaptchaVersion == 3)
            {
                <recaptcha-script-v3/>
                <recaptcha-script-v3-js action="register" callback="(function(){$('#@RecaptchaValidatorBase.RecaptchaResponseKey').val(token)})"/>
            }
            else
            {
                <recaptcha-script-v2/>
            }
        }
    }

    <div class="account-module-form">
        <form method="post">
            @if (Model.UseCaptcha)
            {
                <input type="hidden" name="@RecaptchaValidatorBase.RecaptchaResponseKey" id="@RecaptchaValidatorBase.RecaptchaResponseKey"/>
            }

            @if (!Model.IsExternalLogin)
            {
                <abp-input asp-for="Input.UserName" auto-focus="true"/>
            }

            <abp-input asp-for="Input.EmailAddress"/>

            <abp-input asp-for="Input.EmployeeNo"/>
            <abp-input asp-for="Input.CivilId"/>

            @if (!Model.IsExternalLogin)
            {
                <abp-input asp-for="Input.Password"/>
            }

            @if (reCaptchaVersion == 2)
            {
                <recaptcha-div-v2 callback="(function(){$('#@RecaptchaValidatorBase.RecaptchaResponseKey').val(token)})" />
            }

            <div class="d-grid gap-2">
                <abp-button button-type="Primary" type="submit" class="mt-2 mb-3">@L["Register"]</abp-button>
            </div>
            @L["AlreadyRegistered"] <a href="@Url.Page("./Login", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})">@L["Login"]</a>
        </form>
    </div>
}

Register.cshtml.cs

using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Owl.reCAPTCHA;
using Sip.Iam.Constants;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Public.Web.Pages.Account;
using Volo.Abp.Account.Public.Web.Security.Recaptcha;
using Volo.Abp.Account.Settings;
using Volo.Abp.Auditing;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Security.Claims;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;

namespace Sip.Iam.AuthServer.Web.Pages.Account;

public class RegisterModel : AccountPageModel
{
    [BindProperty(SupportsGet = true)]
    public string ReturnUrl { get; set; }

    [BindProperty(SupportsGet = true)]
    public string ReturnUrlHash { get; set; }

    [BindProperty]
    public PostInput Input { get; set; }

    [BindProperty(SupportsGet = true)]
    public bool IsExternalLogin { get; set; }

    public bool LocalLoginDisabled { get; set; }

    public bool UseCaptcha { get; set; }

    public virtual async Task<IActionResult> OnGetAsync()
    {
        var localLoginResult = await CheckLocalLoginAsync();
        if (localLoginResult != null)
        {
            LocalLoginDisabled = true;
            return localLoginResult;
        }

        await CheckSelfRegistrationAsync();
        await TrySetEmailAsync();
        await SetUseCaptchaAsync();

        return Page();
    }

    [UnitOfWork] //TODO: Will be removed when we implement action filter
    public virtual async Task<IActionResult> OnPostAsync()
    {
        try
        {
            await CheckSelfRegistrationAsync();
            await SetUseCaptchaAsync();

            IdentityUser user;
            if (IsExternalLogin)
            {
                var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
                if (externalLoginInfo == null)
                {
                    Logger.LogWarning("External login info is not available");
                    return RedirectToPage("./Login");
                }

                user = await RegisterExternalUserAsync(externalLoginInfo, Input.EmailAddress);
            }
            else
            {
                var localLoginResult = await CheckLocalLoginAsync();
                if (localLoginResult != null)
                {
                    LocalLoginDisabled = true;
                    return localLoginResult;
                }

                user = await RegisterLocalUserAsync();
            }

            if (await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedEmail) && !user.EmailConfirmed ||
                await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber) && !user.PhoneNumberConfirmed)
            {
                await StoreConfirmUser(user);

                return RedirectToPage("./ConfirmUser", new
                {
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash
                });
            }

            await SignInManager.SignInAsync(user, isPersistent: true);

            return Redirect(ReturnUrl ?? "/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
        }
        catch (BusinessException e)
        {
            Alerts.Danger(GetLocalizeExceptionMessage(e));
            return Page();
        }
    }

    protected virtual async Task<IdentityUser> RegisterLocalUserAsync()
    {
        ValidateModel();

        var captchaResponse = string.Empty;
        if (UseCaptcha)
        {
            captchaResponse = HttpContext.Request.Form[RecaptchaValidatorBase.RecaptchaResponseKey];
        }
        var userDto = await AccountAppService.RegisterAsync(
            new RegisterDto
            {
                AppName = "MVC",
                EmailAddress = Input.EmailAddress,
                Password = Input.Password,
                UserName = Input.UserName,
                ReturnUrl = ReturnUrl,
                ReturnUrlHash = ReturnUrlHash,
                CaptchaResponse = captchaResponse,
                //EmployeeNo = Input.EmployeeNo,
                //CivilId = Input.CivilId
            }
        );

        return await UserManager.GetByIdAsync(userDto.Id);
    }

    protected virtual async Task<IdentityUser> RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string emailAddress)
    {
        await IdentityOptions.SetAsync();

        var user = new IdentityUser(GuidGenerator.Create(), emailAddress, emailAddress, CurrentTenant.Id);

        (await UserManager.CreateAsync(user)).CheckErrors();
        (await UserManager.AddDefaultRolesAsync(user)).CheckErrors();

        if (!user.EmailConfirmed)
        {
            await AccountAppService.SendEmailConfirmationTokenAsync(
                new SendEmailConfirmationTokenDto
                {
                    AppName = "MVC",
                    UserId = user.Id,
                    ReturnUrl = ReturnUrl,
                    ReturnUrlHash = ReturnUrlHash
                }
            );
        }

        var userLoginAlreadyExists = user.Logins.Any(x =>
            x.TenantId == user.TenantId &&
            x.LoginProvider == externalLoginInfo.LoginProvider &&
            x.ProviderKey == externalLoginInfo.ProviderKey);

        if (!userLoginAlreadyExists)
        {
            user.AddLogin(new UserLoginInfo(
                    externalLoginInfo.LoginProvider,
                    externalLoginInfo.ProviderKey,
                    externalLoginInfo.ProviderDisplayName
                )
            );

            (await UserManager.UpdateAsync(user)).CheckErrors();
        }

        return user;
    }

    protected virtual async Task CheckSelfRegistrationAsync()
    {
        if (!await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled) ||
            !await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin))
        {
            throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]);
        }
    }

    protected virtual async Task SetUseCaptchaAsync()
    {
        UseCaptcha = !IsExternalLogin && await SettingProvider.IsTrueAsync(AccountSettingNames.Captcha.UseCaptchaOnRegistration);
        if (UseCaptcha)
        {
            var reCaptchaVersion = await SettingProvider.GetAsync<int>(AccountSettingNames.Captcha.Version);
            await ReCaptchaOptions.SetAsync(reCaptchaVersion == 3 ? reCAPTCHAConsts.V3 : reCAPTCHAConsts.V2);
        }
    }

    protected virtual async Task StoreConfirmUser(IdentityUser user)
    {
        var identity = new ClaimsIdentity(ConfirmUserModel.ConfirmUserScheme);
        identity.AddClaim(new Claim(AbpClaimTypes.UserId, user.Id.ToString()));
        if (user.TenantId.HasValue)
        {
            identity.AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.ToString()));
        }
        await HttpContext.SignInAsync(ConfirmUserModel.ConfirmUserScheme, new ClaimsPrincipal(identity));
    }

    private async Task TrySetEmailAsync()
    {
        if (IsExternalLogin)
        {
            var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
            if (externalLoginInfo == null)
            {
                return;
            }

            if (!externalLoginInfo.Principal.Identities.Any())
            {
                return;
            }

            var identity = externalLoginInfo.Principal.Identities.First();
            var emailClaim = identity.FindFirst(ClaimTypes.Email);

            if (emailClaim == null)
            {
                return;
            }

            Input = new PostInput { EmailAddress = emailClaim.Value };
        }
    }

    public class PostInput
    {
        [Required]
        [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxUserNameLength))]
        public string UserName { get; set; }

        [Required]
        [EmailAddress]
        [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
        public string EmailAddress { get; set; }

        [Required]
        [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
        [DataType(DataType.Password)]
        [DisableAuditing]
        public string Password { get; set; }

        [Required]
        //[DynamicMaxLength(typeof(CustomUserConsts),nameof(CustomUserConsts.MaxEmployeeNoLength))]
        public int EmployeeNo { get; set; }

        [Required]
        //[DynamicMaxLength(typeof(CustomUserConsts), nameof(CustomUserConsts.MaxCivilIdLength))]
        public long CivilId { get; set; }
    }
}

Step 2: Added 2 new properties EmployeeNo and CivilId in EfCoreEntityExtensionMappings (EntityFrameworkCore project)

            ObjectExtensionManager.Instance
                         .MapEfCoreProperty<IdentityUser, int>(
                             "EmployeeNo",
                             (entityBuilder, propertyBuilder) =>
                             {
                                 propertyBuilder.IsRequired();
                                 propertyBuilder.HasMaxLength(15);
                             }
                         )
                         .MapEfCoreProperty<IdentityUser, long>(
                             "CivilId",
                             (entityBuilder, propertyBuilder) =>
                             {
                                 propertyBuilder.IsRequired();
                                 //propertyBuilder.HasMaxLength(10);
                             }
                         );

Step 3: Added 2 new properties EmployeeNo and CivilId in ModileExtensionConfigurator (Domain.Shared project)

    ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
        {
            identity.ConfigureUser(user =>
            {
                user.AddOrUpdateProperty<int>(
                    CustomUserConsts.EmployeeNoPropertyName,
                    options =>
                    {
                        options.Attributes.Add(new RequiredAttribute());
                        //options.DefaultValue = 0;
                        //options.Attributes.Add(
                        //    new StringLengthAttribute(CustomUserConsts.MaxEmployeeNoLength)
                        //);
                    }
                );
                user.AddOrUpdateProperty<long>(
                    CustomUserConsts.CivilIdPropertyName,
                    options =>
                    {
                        //options.DefaultValue = 0;
                        options.Attributes.Add(new RequiredAttribute());
                        //options.Attributes.Add(
                        //    new StringLengthAttribute(CustomUserConsts.MaxCivilIdLength)
                        //);
                    }
                );
            });

**Step4: Added new Properties to DtoExtensions**
            ObjectExtensionManager.Instance
                .AddOrUpdateProperty<int>(
                    new[] {
                        typeof(RegisterDto),
                        typeof(IdentityUserDto), 
                        typeof(IdentityUserCreateDto) 
                    }, "EmployeeNo")
                .AddOrUpdateProperty<long>(
                    new[] {
                        typeof(RegisterDto),
                        typeof(IdentityUserDto),
                        typeof(IdentityUserCreateDto)
                    }, "CivilId");

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

9 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try that?

    var registerDto = new RegisterDto
    {
        AppName = "MVC",
        EmailAddress = Input.EmailAddress,
        Password = Input.Password,
        UserName = Input.UserName,
        ReturnUrl = ReturnUrl,
        ReturnUrlHash = ReturnUrlHash,
        CaptchaResponse = captchaResponse
    }
    registerDto.SetProperty("EmployeeNo", Input.EmployeeNo).SetProperty("CivilId", Input.CivilId);
    
    var userDto = await AccountAppService.RegisterAsync(registerDto);
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    oshabani created

    I have tried the following code accordingly but still its not saving values.

    var registerDto = new RegisterDto
        {
            AppName = "MVC",
            EmailAddress = Input.EmailAddress,
            Password = Input.Password,
            UserName = Input.UserName,
            ReturnUrl = ReturnUrl,
            ReturnUrlHash = ReturnUrlHash,
            CaptchaResponse = captchaResponse
        };
    
    registerDto.SetProperty("EmployeeNo", Input.EmployeeNo).SetProperty("CivilId", Input.CivilId);
    
    var userDto = await AccountAppService.RegisterAsync(registerDto);
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Can you share the project? liming.ma@volosoft.com

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    oshabani created

    I have sent you the project link to your email by mks1704@gmail.com

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    The project you shared is not a complete solution. Can you create a new template project and reproduce it?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    oshabani created

    Please check your email again

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Your IamDtoExtensions call on IamApplicationContractsModule, but the Sip.Iam.AuthServer not using the IamApplicationContractsModule

    Please move the code to IamAuthServerModule

    example:

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    oshabani created

    It is saving values now.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.