【问题标题】:NET Core 3.1 MVC Input Validation does not workNET Core 3.1 MVC 输入验证不起作用
【发布时间】:2020-09-24 16:35:09
【问题描述】:

在某些视图模型类中,[Required] 属性按预期工作。提交表单时,error spans在字段下方显示错误消息,如果必填字段不符合注释要求,则不会提交表单。

但在下面分享的示例中,即使必填字段为空(例如 CompanyName),表单也会提交,我还没有弄清楚原因,请提供一些帮助。

Startup.cs

...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage(); // throw a classic error page is evironment is development
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");          
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
}
...

编辑公司虚拟机。

public class EditCompanyVM
    {
        public EditCompanyVM()
        {
            Contact = new Contact();
            Locations = new List<CompanyLocation>();
        }

        public int CompanyID { get; set; }

        [Required(ErrorMessage = "Company name is required.")]
        public string  CompanyName { get; set; }

        [Required(ErrorMessage = "Company description is required.")]
        public string CompanyDescription { get; set; }

        [Url]
        public string CompanyWebsite { get; set; }

        public Contact Contact { get; set; }

        public List<CompanyLocation> Locations { get; set; }

    }

EditCompany.cshtml

@model MyApp.Models.ViewModels.EditCompanyVM


@{ 
    ViewBag.Title = "Edit Company";
}

<div class="col-md-8">

    <h4 style="margin:0 0 30px 0;">Edit Company</h4>

    <form method="post">

        <div class="form-group">
            <label asp-for="CompanyID" class="control-label">Company ID</label>
            <input asp-for="CompanyID" disabled class="form-control" />
            <span asp-validation-for="CompanyID" class="text-danger"></span>
        </div>


        <div class="form-group">
            <label asp-for="CompanyName" class="control-label"></label>
            <input asp-for="CompanyName" class="form-control" type="text" />
            <span asp-validation-for="CompanyName" class="text-danger"></span>
        </div>

        <div class="form-group">
            <label asp-for="CompanyDescription" class="control-label">Company Description</label>
            <input asp-for="CompanyDescription" class="form-control" />
            <span asp-validation-for="CompanyDescription" class="text-danger"></span>
        </div>


        <div class="form-group">
            <label asp-for="CompanyWebsite" class="control-label">Company Website</label>
            <input asp-for="CompanyWebsite" class="form-control" />
            <span asp-validation-for="CompanyWebsite" class="text-danger"></span>
        </div>

        <div id="contactSection">
            <h4 style="margin:0 0 20px 0;">Contact Section</h4>

            @if (Model.Contact != null)
            {
                <div class="form-group">
                    <label asp-for="Contact.ContactID" class="control-label">Contact ID</label>
                    <input asp-for="Contact.ContactID" disabled class="form-control" />
                    <span asp-validation-for="Contact.ContactID" class="text-danger"></span>
                </div>


                <div class="form-group">
                    <label asp-for="Contact.FirstName" class="control-label">First Name</label>
                    <input asp-for="Contact.FirstName" class="form-control" />
                    <span asp-validation-for="Contact.FirstName" class="text-danger"></span>
                </div>

                <div class="form-group">
                    <label asp-for="Contact.LastName" class="control-label">Last Name</label>
                    <input asp-for="Contact.LastName" class="form-control" />
                    <span asp-validation-for="Contact.LastName" class="text-danger"></span>
                </div>

                <div class="form-group">
                    <label asp-for="Contact.PhoneNumber" class="control-label">Phone Number</label>
                    <input asp-for="Contact.PhoneNumber" class="form-control" />
                    <span asp-validation-for="Contact.PhoneNumber" class="text-danger"></span>
                </div>

                <div class="form-group">
                    <label asp-for="Contact.Email" class="control-label">Email</label>
                    <input asp-for="Contact.Email" class="form-control" />
                    <span asp-validation-for="Contact.Email" class="text-danger"></span>
                </div>
            }
            else
            {
                <a asp-action="AddContact" asp-route-userId="@Model.CompanyID" style="width:auto" class="btn btn-outline-primary">
                    Add Contact
                </a>
            }


        </div>


        <div id="locationsSection">
            <h4 style="margin:20px 0 20px 0;">Company Locations</h4>

            @if (Model.Locations.Any())
            {
                int counter = 1;
                for (int i = 0; i < Model.Locations.Count; i++)
                {
                    <h3>Location @counter</h3>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].Street" class="control-label">Street</label>
                        <input asp-for="@Model.Locations[i].Street" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].Street" class="text-danger"></span>
                    </div>


                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].City" class="control-label">City</label>
                        <input asp-for="@Model.Locations[i].City" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].City" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].Province" class="control-label">Province</label>
                        <input asp-for="@Model.Locations[i].Province" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].Province" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].Country" class="control-label">Country</label>
                        <input asp-for="@Model.Locations[i].Country" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].Country" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].ZipCode" class="control-label">ZipCode</label>
                        <input asp-for="@Model.Locations[i].ZipCode" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].ZipCode" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].PhoneNumber" class="control-label">Location Phonenumber</label>
                        <input asp-for="@Model.Locations[i].PhoneNumber" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].PhoneNumber" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].Email" class="control-label">Branch Email</label>
                        <input asp-for="@Model.Locations[i].Email" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].Email" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="@Model.Locations[i].Manager" class="control-label">Manager Name</label>
                        <input asp-for="@Model.Locations[i].Manager" class="form-control" />
                        <span asp-validation-for="@Model.Locations[i].Manager" class="text-danger"></span>
                    </div>

                    counter++;
                }
            }
            else
            {

            <a asp-action="AddLocation" asp-route-id="@Model.CompanyID" style="width:auto;" class="btn btn-outline-primary">
                Add Location
            </a>
        </div>
          }




        <div class="form-group row" style="margin-top:80px;">
            <div class="col-sm-10">
                <button type="submit" class="btn btn-outline-primary">Update</button>
                <a asp-action="ListCompanies" class="btn btn-outline-primary">Cancel</a>
            </div>
        </div>


    </form>


</div>

CompanyController.cs

[HttpPost]
    public async Task<IActionResult> EditCompany (EditCompanyVM model)
    {
        // model is submitted with null fields, even though warnings should trigger in the view 
        // by asp-validation-for
        return RedirectToAction("ListCompanies");
    }

【问题讨论】:

  • 您检查浏览器控制台是否有错误? P.S 在客户端您使用不显眼的验证来验证表单,在服务器端您使用model state,例如:If(!ModelState.IsValid){ return View(); }
  • @HMZ 是的,刚刚做了,没有任何显示。只需向控制器提交一个空模型。
  • @HMZ 当我编辑“公司”时,我删除了现有名称然后尝试提交,不知道这是否与它有关。
  • 只需添加它,您无需执行任何操作。它专为 asp.net 核心设计,它会自行获取您的服务器端验证属性。
  • @CamiloTerevinto if (!ModelState.IsValid) { return View(model); } 这是用于服务器端验证检查。它与客户端验证检查无关

标签: c# asp.net-core asp.net-core-mvc


【解决方案1】:

在cmets上讨论了很多,所以我想澄清一些事情。

客户端验证发生在用户的浏览器中,并且需要执行脚本。如果表单无效,脚本可以阻止提交表单,并显示错误消息供用户更正。这是对用户和服务器的增强,因为当用户可以在浏览器中处理这些无效请求时,它会阻止大多数无效请求被发送到服务器。

除了客户端验证之外,还会进行服务器端验证 - 客户端验证是可选的附加功能,而不是相反,因为不能相信用户是非恶意的。 ASP.NET Core 中的服务器端验证使用数据注释和验证器的组合进行,最终形成ModelState。服务器端验证不会阻止表单在用户浏览器中提交,但可以仍然通过ModelState 将错误发送回浏览器并返回您当前的模型:

[HttpPost]
public async Task<IActionResult> EditCompany(EditCompanyVM model)
{
    // This property indicates if the model's data, 
    // based on the data annotations it's
    // decorated with, is valid.
    if (!ModelState.IsValid)
    {
        // Returning the same instance of the model back
        // to the view causes Razor to use the `ModelState`'s
        // data to render errors via `asp-validation-for`.
        return View(model);
    }

    // If no errors, redirect.
    return RedirectToAction("Index");
}

现在,如果您的问题是为什么您能够在没有首先进行验证的情况下提交表单,然后防止提交无效数据,这完全是因为客户端验证 - 它与你的控制器;这完全取决于您的看法。

如果您查看~Views/Shared 文件夹,您应该会看到一个名为_ValidationScriptsPartial.cshtml 的视图。以_ 为前缀的默认视图旨在用作部分视图,尽管这是您不必遵循自己的视图的约定。如果你打开它,你会看到它包含以下内容:

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

这些是用于客户端验证的脚本,但默认情况下不包括在内。因此,要解决您的问题,只需在 EditCompany.cshtml 视图的底部添加以下内容:

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

这会将这些脚本呈现给浏览器,加载客户端验证。现在尝试提交表单将触发客户端验证,如果数据无效,不会访问服务器。

【讨论】:

  • 惊人的解释@JohnH。这就是我在评论中提到的,没有加载不显眼的 javascript,客户端验证与模型状态无关。
  • @CamiloTerevinto 感谢您的反馈。我已经进行了修改以更改它。
猜你喜欢
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
  • 2021-10-22
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-19
相关资源
最近更新 更多