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");


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);
    
  • 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);
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

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

  • User Avatar
    0
    oshabani created

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

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

  • User Avatar
    0
    oshabani created

    Please check your email again

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok

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

  • User Avatar
    0
    oshabani created

    It is saving values now.

    Thanks

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.0.0-preview. Updated on June 20, 2025, 11:20