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
相关产品推荐
相关产品推荐

