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

C# 如何在页面加载时显示验证错误?,c#,asp.net-mvc,validation,unobtrusive-validation,C#,Asp.net Mvc,Validation,Unobtrusive Validation,我正在我的应用程序中创建一个功能,用于处理上载的CSV文件,其中包含要导入的多条记录。数据需要验证,我想在单击导入按钮之前显示所有验证错误。高级别计划: 步骤1:上传CSV文件 步骤2:显示CSV文件中的所有记录以及每个记录旁边的任何验证错误(缺少必填字段等) 第三步:点击“导入”,实际导入有效记录 以下是我所拥有的内容的简化版本: 用户视图模型 文件上传操作帖子 [HttpPost] [ValidateAntiForgeryToken] 公共操作结果上载(HttpPostedFileBas

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

  • 步骤1:上传CSV文件
  • 步骤2:显示CSV文件中的所有记录以及每个记录旁边的任何验证错误(缺少必填字段等)
  • 第三步:点击“导入”,实际导入有效记录
以下是我所拥有的内容的简化版本:

用户视图模型 文件上传操作帖子
[HttpPost]
[ValidateAntiForgeryToken]
公共操作结果上载(HttpPostedFileBase csvFile)
{
//var csvRecords=do stuff从CSV文件检索数据
var newUsersToCreate=newlist();
foreach(csvRecord中的var csvRecord)
{
newUsersToCreate.Add(新用户视图模型
{
Name=csvRecord.Name,
Email=csvRecord.Email,
电话=csvRecord.Phone
});
}
返回视图(“ImportPreview”,newUsersToCreate);
}
查看ImportPreview.cshtml
@model IEnumerable
@使用(Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true,“,new{@class=“alert-alert-danger”,role=“alert”})
名称
电子邮件
电话
验证错误
@EditorFor(model=>model)
进口
}
UserViewModel.cshtml的编辑器模板
@model App.ViewModels.UserViewModel
@Html.HiddenFor(model=>model.Name)
@DisplayFor(model=>model.Name)
@Html.HiddenFor(model=>model.Email)
@DisplayFor(model=>model.Email)
@Html.HiddenFor(model=>model.Phone)
@DisplayFor(model=>model.Phone)
@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”})
问题 虽然这会生成一个很好的“预览”表,其中所有准备好的
User
记录基本上都是隐藏字段的行,但问题是,在单击Import按钮之前,它不会显示验证错误


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

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

缺点是需要包含jQuery脚本,否则就不需要这些脚本

另一个选项是在控制器中对集合中的每个项目使用
TryValidateObject
进行验证,并将所有错误添加到
ModelState
,这些错误将显示在
ValidationMessageFor()
占位符中。注意:以下假设
csvRecords
实现了
IList
,因此您可以使用
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; 
}
[HttpPost]
[ValidateAntiForgeryToken]
公共操作结果上载(HttpPostedFileBase csvFile)
{
//var csvRecords=do stuff从CSV文件检索数据
var newUsersToCreate=newlist();
对于(int i=0;i

控制器代码的优点是,您可以向视图模型中添加一个附加属性(例如
bool IsValid
),并将其用于表行的条件样式设置,并且您可以确定如果存在“太多”错误,您可以只显示一个不同的视图,而不是呈现整个表并显示可能数百条重复的错误消息

一个选项是在加载页面时添加一个脚本来验证表单-
$('form')。valid()
您还可以使用
TryValidateModel(newUsersToCreate);在填充控制器以添加
modelstatererrors`(未测试)后,我已经尝试了这两种方法,但没有成功。不确定是否与这些隐藏字段有关。抱歉,我没有注意到。默认情况下,隐藏的输入不会在视图中验证,但您可以覆盖该输入,请参见示例。但由于它们是隐藏的,用户无法编辑它们,因此不确定显示错误会有什么帮助这正是我所需要的。我选择了第二种方法,因为在我的用例中,在控制器中处理验证并将预验证的模型交付给视图似乎更好。谢谢
[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);
}
@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>
}
@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>
<script>
    // override validator to include hidden inputs
    $.validator.setDefaults({ 
        ignore: [] 
    });
    // validate form and display errors
    $('form').valid();
</script>
if ($('form').valid()) {
    $('#error').show();
}
[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; 
}