如何在页面加载时显示验证错误?

How to display validation errors on page load?

我正在我的应用程序中创建一项功能来处理上传的 CSV 文件,其中包含要导入的多条记录。数据需要验证,我想在单击 Import 按钮之前显示任何验证错误。高级计划:

这是我所拥有的简化版本:

用户视图模型

public class UserViewModel
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; }

    [Required]
    [StringLength(150)]
    public string Email { get; set; }

    [Required]
    [StringLength(10)]
    public string Phone { get; set; }
}

文件上传操作Post

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(HttpPostedFileBase csvFile)
{
    // var csvRecords = do stuff to retrieve data from CSV file
    var newUsersToCreate = new List<UserViewModel>();

    foreach (var csvRecord in csvRecords)
    {
        newUsersToCreate.Add(new UserViewModel
        {
            Name = csvRecord.Name,
            Email = csvRecord.Email,
            Phone = csvRecord.Phone
        });
    }
    return View("ImportPreview", newUsersToCreate);
}

查看ImportPreview.cshtml

@model IEnumerable<App.ViewModels.UserViewModel>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true, "", new { @class = "alert alert-danger", role = "alert" })

    <table>
        <thead>
            <tr>
                <th>Name</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Validation Errors</th>
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(model => model)
        </tbody>
    </table>

    <button type="submit">Import</button>
}

UserViewModel.cshtml

的编辑器模板
@model App.ViewModels.UserViewModel

<tr>
    <td>
        @Html.HiddenFor(model => model.Name)
        @Html.DisplayFor(model => model.Name)
    </td>
    <td>
        @Html.HiddenFor(model => model.Email)
        @Html.DisplayFor(model => model.Email)
    </td>
    <td>
        @Html.HiddenFor(model => model.Phone)
        @Html.DisplayFor(model => model.Phone)
    </td>
    <td>
        @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" })
        @Html.ValidationMessageFor(model => model.Phone, "", new { @class = "text-danger" })
    </td>
</tr>

问题

虽然这会生成一个很好的 "preview" table,所有准备好的 User 记录基本上都是准备好隐藏字段的行,但问题是它不会显示验证错误,直到单击 导入 按钮。

如何让它在 return View('ImportPreview', newUsersToCreate) 返回视图后立即在每一行中显示每个字段的验证错误?

您可以通过检查 $.validator 是否有效在视图中执行此操作。由于默认情况下不验证隐藏输入,因此您还需要覆盖验证器。在 jquery-{version}.jsjquery.validate.jsjquery.validate.unobtrusive.js 脚本之后添加以下内容(但不在 $(document).ready() 中)

<script>
    // override validator to include hidden inputs
    $.validator.setDefaults({ 
        ignore: [] 
    });
    // validate form and display errors
    $('form').valid();
</script>

请注意,您可能包含一个(比如)<p id="error" style="display:none;"> 标签,其中包含一个 'general' 错误消息,表明数据无效并使用

if ($('form').valid()) {
    $('#error').show();
}

缺点是您需要包含 jQuery 否则不需要的脚本。

另一个选项是在控制器中对集合中的每个项目使用 TryValidateObject 进行验证,并将任何错误添加到 ModelState,这将显示在您的 ValidationMessageFor() 占位符中。请注意以下假设 csvRecords 实现 IList<T> 以便您可以使用 for 循环。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(HttpPostedFileBase csvFile)
{
    // var csvRecords = do stuff to retrieve data from CSV file
    var newUsersToCreate = new List<UserViewModel>();
    for (int i = 0; i < csvRecords.Count; i++)
    {
        UserViewModel model = new UserViewModel
        {
            Name = csvRecords[i].Name,
            ....
        };
        newUsersToCreate.Add(model);
        // validate the model and include the collection indexer
        bool isValid = ValidateModel(model, i));   
    }
    return View("ImportPreview", newUsersToCreate);
}

private bool ValidateModel(object model, int index) 
{ 
    var validationResults = new List<ValidationResult>(); 
    var context = new ValidationContext(model); 
    if (!Validator.TryValidateObject(model, context, validationResults, true)) 
    { 
        foreach (var error in validationResults) 
        { 
            string propertyName = $"[{index}].{error.MemberNames.First()}"; 
            ModelState.AddModelError(propertyName, error.ErrorMessage); 
        } 
        return false; 
    } 
    return true; 
}

控制器代码的优点是您可以向视图模型添加额外的 属性(例如 bool IsValid)并将其用于 table 行的条件样式,并且您可以决定如果存在 'too many' 错误,您可以只显示不同的视图,而不是呈现整个 table 并显示可能重复出现的数百条错误消息