You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Razor页面服务器端验证时强制设置字段默认值问题

问题:Razor页面自定义验证属性设置默认值后页面不显示

我想在Razor页面的服务器端验证过程中,当Genre字段为空时,通过SetDefaultValueWhenEmpty特性为其设置默认值。编写了如下自定义验证属性及Movie模型代码:

自定义验证属性代码:

public class SetDefaultValueWhenEmptyAttribute : ValidationAttribute
{
    private string defaultValue;

    public SetDefaultValueWhenEmptyAttribute(string defaultValue)
    {
        this.defaultValue = defaultValue;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var instObj = validationContext.ObjectInstance;
        var instType = instObj.GetType();

        if (value == null)
        {
            var currProp = instType.GetProperty(validationContext.MemberName);

            if (currProp != null)
            {
                Type currType = Nullable.GetUnderlyingType(currProp.PropertyType) ?? currProp.PropertyType;
                object safeValue = Convert.ChangeType(defaultValue, currType);
                currProp.SetValue(instObj, safeValue);
            }

            return new ValidationResult(ErrorMessage);
        }

        return ValidationResult.Success;
    }
}

Movie模型代码:

public class Movie
{
    public int Id { get; set; }

    [Required(ErrorMessageResourceName = nameof(ValidationMessages.Required), ErrorMessageResourceType = typeof(ValidationMessages))]
    public string Title { get; set; } = null!;

    [StringLength(250, MinimumLength = 5)]
    public string Description { get; set; } = null!;

    [Display(Name = "Genere film")]
    //[Required(ErrorMessage = "Required")]
    [SetDefaultValueWhenEmpty("Example", ErrorMessage = "Value forced")]
    public string? Genre { get; set; }

    [Display(Name = "Year go")]
    [Required(ErrorMessage = "Required")]
    [Range(2000, 2100)]
    public int YearGo { get; set; }

    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
}

当字段为空时,调试能看到Genre属性已被赋值为默认值,但页面上的字段仍为空,返回错误提示的同时页面不显示设置的默认值,请问哪里出错了?


原因分析

问题核心在Razor页面的表单渲染逻辑和ModelState的优先级:

  1. 你在IsValid方法中修改模型属性值时,表单绑定流程已完成,页面渲染会优先读取ModelState中存储的原始提交值,而非你修改后的模型属性。
  2. 返回ValidationResult错误会让ModelState标记该字段为无效,框架会保留用户最初提交的空值在ModelState中,直接覆盖你对模型的修改。

解决方法

方法1:同步更新ModelState(保留错误提示的场景)

在设置模型属性值后,同步修改ModelState中对应字段的值,确保页面渲染时读取更新后的数据:

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    var instObj = validationContext.ObjectInstance;
    var instType = instObj.GetType();

    if (value == null)
    {
        var currProp = instType.GetProperty(validationContext.MemberName);

        if (currProp != null)
        {
            Type currType = Nullable.GetUnderlyingType(currProp.PropertyType) ?? currProp.PropertyType;
            object safeValue = Convert.ChangeType(defaultValue, currType);
            currProp.SetValue(instObj, safeValue);
            
            // 关键步骤:同步更新ModelState中的值
            var modelState = validationContext.GetService<IModelStateDictionary>();
            modelState?.SetModelValue(validationContext.MemberName, safeValue, safeValue.ToString());
        }

        return new ValidationResult(ErrorMessage);
    }

    return ValidationResult.Success;
}

方法2:取消错误返回(仅需设置默认值的场景)

如果不需要显示"Value forced"的错误提示,直接设置默认值后返回ValidationResult.Success,让ModelState认可该值,页面会自动渲染修改后的模型属性:

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    var instObj = validationContext.ObjectInstance;
    var instType = instObj.GetType();

    if (value == null)
    {
        var currProp = instType.GetProperty(validationContext.MemberName);

        if (currProp != null)
        {
            Type currType = Nullable.GetUnderlyingType(currProp.PropertyType) ?? currProp.PropertyType;
            object safeValue = Convert.ChangeType(defaultValue, currType);
            currProp.SetValue(instObj, safeValue);
        }

        // 返回成功,让ModelState标记字段为有效
        return ValidationResult.Success;
    }

    return ValidationResult.Success;
}

额外说明

Razor页面的表单渲染逻辑优先使用ModelState的值,这是为了保留用户的原始输入痕迹,即使模型属性被后台修改,只要ModelState未更新,页面仍会显示最初提交的内容。

内容的提问来源于stack exchange,提问作者steve fax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 12:50:25