Search code examples
asp.net-corevalidationrazor-pages

custom validator for warning messages in asp.net 7.0 razor pages project


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.


Solution

  • 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