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

Razor Pages选择Check-in时UnitOfMeasurement必填校验问题求助

问题描述

在Razor Pages项目中,当用户选择「Check-in」选项时,Unit of measurement字段会被隐藏,但提交表单时该字段触发必填校验,提示「The UnitOfMeasurement field is required」。期望此时该字段自动设为「Done」,但提交时ModelState始终无效,无法完成提交——尽管SQLite插入语句中已硬编码该值。

相关代码

.cshtml代码

@page
@model MVC.HabitTracker.Pages.CreateHabitModel
@{}

<h3>Create a habit</h3>
<br />
<p>We do have available habits for you to log, but you can add more!</p>
<br />

<form method="post">
    <div class="form-group">
        <label asp-for="HabitType.ImagePath" class="control-label">URL of your habit's icon</label>
        <input type="text" asp-for="HabitType.ImagePath" class="form-control" />
        <span asp-validation-for="HabitType.ImagePath" class="text-danger"></span>
        <small id="passwordHelpBlock" class="form-text text-muted">
            You must attach a link to the picture of your habit's icon. This site has a lot of icons you can use: <a href="https://icons8.com/icons/">https://icons8.com/icons/</a>
        </small>
    </div>
    <div class="form-group">
        <label asp-for="HabitType.Name" class="control-label"></label>
        <input type="text" asp-for="HabitType.Name" class="form-control" />
        <span asp-validation-for="HabitType.Name" class="text-danger"></span>
        <small id="passwordHelpBlock" class="form-text text-muted">
            Insert here the name of your habit (i.e.: water drinking, physical activity, coding time).
        </small>
    </div>

    <div class="form-group" id="measurability">
        <label asp-for="HabitType.Measurability" class="control-label">Select the measurability of your habit</label>
        <select asp-for="HabitType.Measurability" class="form-control" onchange="ToggleUnitOfMeasurement()">
            <option value=" ">Select an option</option>
            <option value="duration">Duration</option>
            <option value="quantifiable">Quantifiable</option>
            <option value="check-in">Check-in</option>
        </select>
    </div>

    
    <div class="form-group" id="unitofmeasurement">
        <label asp-for="HabitType.UnitOfMeasurement" class="control-label">Unit of measurement</label>
        <input type="text" asp-for="HabitType.UnitOfMeasurement" class="form-control" />
        <span asp-validation-for="HabitType.UnitOfMeasurement" class="text-danger"></span>
        <small id="passwordHelpBlock" class="form-text text-muted">
            For example, if your habit is about water consumption, you can measure it on <p>bottles, glasses, liters (l), milliliters (ml), etc.</p>
        </small>
    </div>

    <br />

    <div class="form-group">
        <input type="submit" value="Create" class="btn btn-success"/>
    </div>

    @{}
</form>

<div>
    <a asp-page="Index">Back to List</a>
</div>



@section Scripts{
    <script>
        function ToggleUnitOfMeasurement(){
            if ($("#measurability option:selected").text().includes("Check-in")){
                $("#unitofmeasurement").hide();
            }
            else{
                $("#unitofmeasurement").show();
            }
        }
    </script>
}

.cshtml.cs代码

public class CreateHabitModel : PageModel
{
    private readonly IConfiguration _configuration;

    public CreateHabitModel(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public HabitType HabitType { get; set; }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        if (HabitType.Measurability != "Check-in")
        {
            using (var connection = new SqliteConnection(_configuration.GetConnectionString("ConnectionString")))
            {
                connection.Open();
                var tableCmd = connection.CreateCommand();
                tableCmd.CommandText =
                    $@"INSERT INTO habit_types(Id, ImagePath, Name, Measurability, UnitOfMeasurement)
                VALUES(1, '{HabitType.ImagePath}', '{HabitType.Name}', '{HabitType.Measurability}', '{HabitType.UnitOfMeasurement}')";

                tableCmd.ExecuteNonQuery();
            }

            return RedirectToPage("./Index");
        }
        else
        {
            using (var connection = new SqliteConnection(_configuration.GetConnectionString("ConnectionString")))
            {
                connection.Open();
                var tableCmd = connection.CreateCommand();
                tableCmd.CommandText =
                    $@"INSERT INTO habit_types(Id, ImagePath, Name, UnitOfMeasurement)
                VALUES(1, '{HabitType.ImagePath}', '{HabitType.Name}', '{HabitType.Measurability}', 'Done')";

                tableCmd.ExecuteNonQuery();
            }

            return RedirectToPage("./Index");
        }
        
    }
}
解决方案

ModelState校验发生在SQL插入操作之前,因此即使你在SQL中硬编码了值,只要绑定的HabitType.UnitOfMeasurement为空,ModelState就会判定无效。以下是针对性的修复方案:

1. 后端提前赋值并修正ModelState

在ModelState校验前,针对「Check-in」场景给字段赋值,同时清除该字段的校验错误:

public IActionResult OnPost()
{
    // 针对Check-in场景提前赋值UnitOfMeasurement
    if (HabitType.Measurability == "check-in")
    {
        HabitType.UnitOfMeasurement = "Done";
        // 移除该字段的ModelState错误
        ModelState.Remove(nameof(HabitType.UnitOfMeasurement));
        // 重新验证模型
        TryValidateModel(HabitType);
    }

    if (!ModelState.IsValid)
    {
        return Page();
    }

    // 统一SQL插入逻辑,改用参数化查询避免注入风险
    using (var connection = new SqliteConnection(_configuration.GetConnectionString("ConnectionString")))
    {
        connection.Open();
        var tableCmd = connection.CreateCommand();
        tableCmd.CommandText =
            @"INSERT INTO habit_types(Id, ImagePath, Name, Measurability, UnitOfMeasurement)
              VALUES(1, @ImagePath, @Name, @Measurability, @UnitOfMeasurement)";
        
        tableCmd.Parameters.AddWithValue("@ImagePath", HabitType.ImagePath);
        tableCmd.Parameters.AddWithValue("@Name", HabitType.Name);
        tableCmd.Parameters.AddWithValue("@Measurability", HabitType.Measurability);
        tableCmd.Parameters.AddWithValue("@UnitOfMeasurement", HabitType.UnitOfMeasurement);

        tableCmd.ExecuteNonQuery();
    }

    return RedirectToPage("./Index");
}

2. 前端优化:隐藏字段时自动赋值

在前端隐藏字段的同时,给输入框赋值「Done」并设置为只读,确保表单提交时该字段有值:

@section Scripts{
    <script>
        function ToggleUnitOfMeasurement(){
            var unitField = $("#unitofmeasurement");
            var unitInput = $("#@Html.IdFor(m => m.HabitType.UnitOfMeasurement)");
            
            if ($("#measurability option:selected").text().includes("Check-in")){
                unitField.hide();
                unitInput.val("Done").prop("readonly", true);
            }
            else{
                unitField.show();
                unitInput.val("").prop("readonly", false);
            }
        }

        // 页面加载时初始化字段状态
        $(document).ready(function(){
            ToggleUnitOfMeasurement();
        });
    </script>
}

3. 模型条件校验(可选)

如果HabitType模型中UnitOfMeasurement标记了[Required],可以添加自定义条件校验属性,仅当Measurability为「duration」或「quantifiable」时才要求该字段必填:

public class RequiredIfAttribute : ValidationAttribute
{
    private readonly string _propertyName;
    private readonly object _targetValue;

    public RequiredIfAttribute(string propertyName, object targetValue)
    {
        _propertyName = propertyName;
        _targetValue = targetValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_propertyName);
        if (property == null)
            return new ValidationResult($"Unknown property: {_propertyName}");

        var propertyValue = property.GetValue(validationContext.ObjectInstance);
        if (Equals(propertyValue, _targetValue) && string.IsNullOrEmpty(value?.ToString()))
            return new ValidationResult(ErrorMessage ?? $"The {validationContext.DisplayName} field is required.");

        return ValidationResult.Success;
    }
}

在模型中使用:

public class HabitType
{
    // 其他属性...
    public string Measurability { get; set; }

    [RequiredIf("Measurability", "duration", ErrorMessage = "UnitOfMeasurement is required for duration type")]
    [RequiredIf("Measurability", "quantifiable", ErrorMessage = "UnitOfMeasurement is required for quantifiable type")]
    public string UnitOfMeasurement { get; set; }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 12:19:53