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)
-
0
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);
-
0
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);
-
0
Can you share the project? liming.ma@volosoft.com
-
0
I have sent you the project link to your email by mks1704@gmail.com
-
0
hi
The project you shared is not a complete solution. Can you create a new template project and reproduce it?
-
0
Please check your email again
-
0
ok
-
0
-
0
It is saving values now.
Thanks