in an asp.net 7.0 razor pages project is it possible to create a custom validator for warning messages? Basically display a warning message without invalidating the modelstate. This would allow a message to be displayed without blocking processing (for example signaling forcing to a default value if the field has not been entered). Thank you.
For this you can use custom validation attribute and check if property be null set value for that.
public class SetDefault : ValidationAttribute
{
private readonly object? _value;
private readonly string _statue;
//Changed lines in Update
private readonly string _logMessage;
public SetDefault(object? value, string status, string logMessage)
{
_value = value;
_statue = status;
_logMessage = logMessage;
}
protected override ValidationResult? IsValid(object? value, ValidationContext context)
{
var propertyInfo = context.ObjectType.GetProperty(context.MemberName!);
if (value == null)
{
propertyInfo?.SetValue(context.ObjectInstance, _value);
var alert = context.ObjectType.GetProperty(_statue);
//! Changed lines in Update
var resultVal = alert!.GetValue(context.ObjectInstance, null) as List<Warning>;
resultVal!.Add(new Warning() { Code = context.MemberName, Description = _logMessage});
alert.SetValue(context.ObjectInstance, resultVal);
}
return ValidationResult.Success;
}
}
Edit : For your question in chat we need some changes
Note : Read again for change in SetDefault
attribute
First add Entity
to our database
and change entities like this:
User
Entity:
public class UserApp : IdentityUser
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? NationalCode { get; set;}
public List<Warning> Warnings { get; set; } = new();
}
Warning
Entity:
public class Warning
{
[Key]
public int WarningId { get; set; }
[Required]
public string? Code { get; set; }
public string? Description { get; set; }
[ForeignKey(nameof(UserApp))]
public int UserAppId { get; set; }
public UserApp? UserApp { get; set; }
}
Next add it to dbContext
then create CustomUserManger
and add below method to that
Note : If i add CustomUserManager
to this answer will be long search the net
its simple
public async Task<UserApp?> GetByEmailWithWarnings(string email)
{
return await Users.Include(i => i.Warnings).FirstOrDefaultAsync(i => i.Email == email);
}
Now can check like this in razor
page
public class Register : PageModel
{
private readonly ApplicationUserManager _userManager; //? CustomUserManager
private readonly SignInManager<UserApp> _signInManager;
public Register(ApplicationUserManager userManager, SignInManager<UserApp> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public string ReturnUrl { get; set; } = string.Empty;
public bool WarningExist { get; set; } = false;
[BindProperty]
public InputModel Input { get; set; } = new();
public void OnGet(string? returnUrl = null)
{
ReturnUrl = returnUrl ?? "/";
Page();
}
public async Task<IActionResult> OnPostRegisterAsync(string? returnUrl)
{
ReturnUrl = returnUrl ?? "/";
WarningExist = Input.Warnings.Any();
if (!ModelState.IsValid) return Page();
UserApp user = new()
{
FirstName = Input.FirstName,
LastName = Input.LastName,
Email = Input.Email,
NationalCode = Input.NationalCode,
Warnings = WarningExist ? Input.Warnings : new(),
};
var createResult = await _userManager.CreateAsync(user, Input.Password);
if (!createResult.Succeeded)
{
ModelState.AddModelError("Input.Result", "Create user broken"); //! Show error
WarningExist = false; //? Because user don't created
return Page();
}
return WarningExist ? Page() : LocalRedirect(ReturnUrl);
}
public async Task<IActionResult> OnPostWarningAsync(string? returnUrl = null)
{
ReturnUrl = returnUrl ?? "/";
if (string.IsNullOrWhiteSpace(Input.NationalCode))
{
ModelState.AddModelError("Input.NationalCodel", "Filed NationalCode is required");
WarningExist = true;
return Page();
}
var user = await _userManager.GetByEmailWithWarnings(Input.Email);
if (user != null)
{
user.NationalCode = Input.NationalCode;
user.Warnings.Remove(user.Warnings.First(i => i.Code == "Input.NationalCode" )); //! important
var resultUpdate = await _userManager.UpdateAsync(user);
if (resultUpdate.Succeeded)
{
await _signInManager.SignInAsync(user, false);
return LocalRedirect(ReturnUrl);
}
}
ModelState.AddModelError("Input.Result", "Update user broken"); //! Show error
WarningExist = true; //? Because user don't update
return Page();
}
public class InputModel
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
[EmailAddress(ErrorMessage = "Filed {0} is required")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "Field {0} is required")]
[DataType(DataType.Password)]
public string Password { get; set; } = string.Empty;
[Compare(nameof(Password), ErrorMessage = "Filed {0} is not match")]
public string ConfirmePassword { get; set; } = string.Empty;
public string Result { get; set; } = string.Empty; //? Just for return Error When Add user not complete
//! Important warning
[SetDefault("123456789", nameof(Warnings), "Your message")]
public string? NationalCode { get; set; }
public List<Warning> Warnings { get; set; } = new();
}
}
And in cshtml
use like this
@{
if(!Model.WarningExist)
{
//First form for register
}
else
{
//Second form for remove warning
}
}
Note : Search the net
for use to OnPost
page handler in razor
page
The answer is boring but It is necessary to carefully check the codes
Excuse me from users for (Sorry Answer out of boredom but) text. English is not my national language
I hope this help you
Every where is unclear can call me