六、验证

我们永远不能依赖用户输入的数据。 有时他们可能不知道应用,因此他们可能在不知情的情况下输入了不正确的数据。 在其他时候,一些恶意用户可能希望通过向应用中输入不适当的数据来破坏应用。 在这两种情况下,我们都需要在存储数据以进行进一步处理之前验证输入数据。

在本章中,你将学习以下主题:

  • 不同类型的验证
  • 服务器端验证的示例
  • 一个示例的客户端验证
  • 不显眼的 JavaScript 验证使用 jQuery 不显眼的库,在这里我们不需要为验证编写单独的代码

在理想的情况下,用户将在应用中以适当的格式输入有效的数据。 但是,正如你可能意识到的,现实世界并不是那么理想。 用户将在应用中输入不正确的数据。 作为开发人员,验证应用中的用户输入是我们的责任。 如果输入的输入无效,您需要通知用户,说明哪里出错了,以便用户可以更正输入数据并再次提交表单。

验证可以在客户端、服务器端或两端进行。 如果在将数据发送到服务器之前进行了验证,则称为客户端验证。 例如,如果用户没有在强制字段中输入任何数据,我们可以在客户端验证表单(通过查找未输入的数据)。 不需要将表单数据发送到服务器。 JavaScript 是客户端验证最常用的语言。

Validation

如果在服务器端进行验证(将表单数据发送给服务器),则称为服务器端验证。 例如,您可能希望根据数据库中的数据验证用户输入的数据。 在这种情况下,最好是进行服务器端验证,因为我们不能在客户端拥有数据库中的所有数据。

Validation

客户端和服务器端验证

在现实世界中,这不是服务器端或客户端验证的问题。 我们可以同时拥有这两种类型的验证。 实际上,建议对两端的数据进行验证,以避免不必要的处理。

Client-side and server-side validation

上图显示验证在客户端和服务器端同时执行。 如果数据没有输入到必需的字段中,我们可以在客户端本身捕获该问题。 不需要将数据发送到服务器以最终发现没有输入数据。 一旦输入了所有必需的数据,数据就会被发送回服务器,以根据某些业务逻辑验证输入的数据。 如果验证失败,表单数据将与错误消息一起再次发送到浏览器,以便用户可以再次发送数据。

我们已经讨论了关于验证需求和应用中通常使用的验证类型的足够理论。 让我们亲自动手,为我们在前一章中构建的应用添加验证。

下面的截图是我们在上一章中建立的表单。 这个表单中没有什么奇特的东西——只有三个字段。 当用户在表单中输入数据时,数据被存储在数据库中,整个员工信息被提取回来并以表格格式显示。

Client-side and server-side validation

在我们构建的现有应用中,即使用户没有在任何字段中输入任何信息并提交信息,我们也不会向用户显示任何消息。 相反,我们默默地存储字段的默认值(字符串类型为空值,十进制类型为 0.00),如下面的截图所示:

Client-side and server-side validation

但情况不应该是这样。 我们应该通知用户输入的数据是无效的,并要求用户更正输入的数据。

服务器端验证

让我们继续上一章中构建的应用。 要进行服务器端验证,我们需要做以下工作:

  1. ViewModels模型类添加 Data Annotation 属性。 根据该元数据验证输入数据,并自动更新模型状态。
  2. 更新view方法以显示每个字段的验证消息。 带有asp-validation-for属性的span标记 helper 将用于显示验证错误消息。
  3. 更新控制器动作方法以验证模型状态。 如果模型状态是有效的,我们将数据插入数据库。 否则,将更新 View 模型,并使用验证错误消息再次呈现view方法,以便用户可以更新有效的输入数据并再次提交表单。

使用 Data Annotation 属性更新视图模型

Data Annotation 属性定义了Model/ViewModel属性的验证规则。 如果输入数据与模型中的属性定义不匹配,则验证将失败,从而使关联的模型状态无效。

有几个 Data Annotation 属性可用来验证数据。 以下是最常用的数据注释属性:

  • Required:该属性表示该属性是必需的。
  • Range:该属性定义最小和最大约束。
  • MinLength:这定义了一个属性成功验证所必须具有的最小长度。
  • MaxLength:顾名思义,该属性定义了属性的最大长度。 如果属性值的长度超过最大长度,则验证将失败。
  • regulareexpression:如果我们使用这个属性,我们可以使用一个正则表达式来验证数据。

由于 Data Annotation 属性在System.ComponentModel.DataAnnotations名称空间中可用,所以我们需要包含这个名称空间。 以下是更新后的视图模型代码:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Linq; 
using System.Threading.Tasks; 
using Validation.Models;

namespace Validation.ViewModels 
{ 
    public class EmployeeAddViewModel 
    { 
        public List<Employee> EmployeesList { get; set; } 
        [Required(ErrorMessage ="Employee Name is required")] 
        public string Name { get; set; } 

        [Required(ErrorMessage ="Employee Designation is required")] 
        [MinLength(5, ErrorMessage = "Minimum length of designation should be 5 characters")] 
        public string Designation { get; set; } 

        [Required] 
        [Range(1000,9999.99)] 
        public decimal Salary { get; set; } 
    } 
} 

我们为所有三个属性——NameDesignationSalary添加了 Data Annotation 属性。

ErrorMessage属性在验证失败时显示一条消息。 如果验证失败,并且没有提到ErrorMessage,则会显示默认的错误消息。

更新 View 模型以显示验证错误消息

对于每个字段,我们都添加了一个span标记,当验证失败时,错误消息将以红色显示。 当验证成功时,将不会显示错误消息。 asp-validation-for的属性值表示必须为其显示验证错误消息的字段名。 例如,我们使用带有asp-validation-for属性和Name值的span标记,它告诉 ASP.NET MVC 显示Name字段的验证错误消息:

<form asp-controller="Employee" asp-action="Index"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 
        </table> 
    </form> 

更新控制器动作方法,验证模型状态

模型状态会根据在 View 模型和输入数据上指定的 Data Annotation 属性自动更新。 我们在下面的Index方法中验证模型状态是否有效,这是一个POST动作方法。 如果模型状态是有效的(验证成功时),我们将输入的数据保存到数据库中。 如果验证失败,则将ModelState自动设置为invalid。 然后,我们将用输入的数据填充ViewModel并再次呈现View方法,以便用户可以更正输入数据并重新提交数据:

[HttpPost] 
    public IActionResult Index(EmployeeAddViewModel employeeAddViewModel) 
{ 
        if (ModelState.IsValid) 
        { 
            using (var db = new EmployeeDbContext()) 
            { 
                Employee newEmployee = new Employee 
                { 
                    Name = employeeAddViewModel.Name, 
                    Designation = employeeAddViewModel.Designation, 
                    Salary = employeeAddViewModel.Salary 
                }; 
                db.Employees.Add(newEmployee); 
                db.SaveChanges(); 
                //Redirect to get Index GET method 
                return RedirectToAction("Index"); 
            } 
        } 
        using (var db = new EmployeeDbContext()) 
        { 
            employeeAddViewModel.EmployeesList = db.Employees.ToList(); 
        } 
        return View(employeeAddViewModel); 
} 

当您在进行上述更改后运行应用并提交表单而不输入值时,错误消息将显示在如下截图所示的字段旁边。 请注意,即使在验证错误的情况下,我们也会在下表中显示员工的数据,这是通过使用前面代码片段中的代码块实现的。

Updating the controller  method to verify the model state

在之前的验证和它的错误消息中有一些事情需要注意:

  • 如果验证失败,将按预期显示错误消息。
  • 如果一个字段有多个验证,它将一次显示一条错误消息。 例如,我们对名称字段进行了两个验证——RequiredMinLength属性。 如果没有为该字段输入数据,则只显示必需的字段错误消息。 只有当必需的字段错误得到解决时(通过在字段中输入一些字符),才会显示第二个验证错误消息。
  • If no error message is available and if the validation fails, the default error message is displayed. We have not given any error message for the Salary field. So, when the validation fails for that field, ASP.NET MVC displays the default error message based on the field name and the type of validation failure.

    Updating the controller  method to verify the model state

上图描述了服务器端验证中事件的高级序列,描述如下:

  1. 用户输入无效数据。
  2. 基于 View 模型中的 Data Annotations 属性,模型状态会自动更新。 这发生在模型绑定过程中,其中view方法中的数据被映射到模型或 View 模型中的数据。
  3. 在控制器的动作方法中,我们验证了模型的状态。
  4. 如果模型状态是有效的,我们将输入的数据保存到数据库中。
  5. 如果模型状态无效,我们将使用验证错误消息再次分解 View 模型,以便用户可以更正输入数据并再次提交表单,并使用有效的输入数据。

客户端验证

在有些情况下,我们不需要访问服务器来验证输入数据。 在前面的服务器端验证示例中,我们不需要访问服务器来验证用户是否为Name字段输入了数据。 我们可以在客户端进行验证。 这可以防止往返于服务器,并减少服务器负载。

我们将使用 JavaScript 验证来自客户端的数据。 JavaScript 是一种高级的解释性语言,主要用于客户端编程。

注意事项

现在,JavaScript 也作为 Node.js 的一部分在服务器端使用。

我们将在 View 模型(Index.cshtml文件)中做一些更改,以在客户端验证表单:

  1. 表单的改变:将id属性添加到所有的span标签中,这样我们就可以访问这个 HTML 元素来显示 HTML 错误消息。 提交表单时,调用一个 JavaScript 函数来验证输入数据。
  2. 添加脚本 HTML 元素并创建一个 JavaScript 函数来验证输入数据。

在下面的代码中,我们将在表单提交时调用validateFormJavaScript 函数。 如果validateForm函数返回true,则数据将被发送到服务器。 否则,将不会发送数据。 我们为所有的span标签添加了id属性,这样我们就可以识别span标签并在那里显示验证错误消息:

<form asp-controller="Employee" asp-action="Index" onsubmit="return validateForm()"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span id="validationName" asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span id="validationDesignation" asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span id="validationSalary" asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 
        </table> 
</form> 

我们已经添加了 JavaScript 函数来验证所有三个字段。 我们得到所有三个字段的值,并将它们存储在单独的变量中。 然后验证每个变量的值是空还是空。 如果该值为空,则获取相应字段的span元素,并使用验证错误消息设置文本上下文:

<script type="text/javascript"> 
        function validateForm() { 
            var isValidForm = true; 
            var nameValue = document.getElementById("Name").value; 
            var designationValue = document.getElementById("Designation").value; 
            var salaryValue = document.getElementById("Salary").value; 

            //Validate the name field 
            if (nameValue == null || nameValue == "") { 
                document.getElementById("validationName").textContent = "Employee Name is required - from client side"; 
                isValidForm = false; 
            } 

            //validate the designation field 
            if (designationValue == null || designationValue == "") { 
                document.getElementById("validationDesignation").textContent = "Employee Designation is required - from client side"; 
                isValidForm = false; 
            } 

            //validate the salary field - if it is empty 
            if (salaryValue == null || salaryValue == "") { 
                document.getElementById("validationSalary").textContent = "Employee Salary is required - from client side"; 
                isValidForm = false; 
            }else if (Number(salaryValue) == NaN || Number(salaryValue)<=0.0) { 
                document.getElementById("validationSalary").textContent = "Please enter valid number for salary field - from client side"; 
                isValidForm = false; 
            } 

            return isValidForm; 

        } 

</script> 

当您运行应用并提交表单而不输入数据时,您将获得从客户端本身生成的错误消息,而无需访问服务器。

Client-side validation

在真实的应用中,我们不会在 JavaScript 中手工编写验证代码。 相反,大多数应用使用不引人注目的验证,其中我们不编写 JavaScript 代码来验证每个字段。 只需添加相应的 JavaScript 库就可以了。

您可能想知道在不编写代码的情况下如何验证字段。 其神奇之处在于基于 Data Annotation 属性添加到输入 HTML 元素的data-属性。 这个 jQuery 不显眼的库获得一个字段列表,为其添加了data-属性,并对其进行验证。

运行应用并按Ctrl+U查看源代码。 源代码看起来如下所示:

Client-side validation

不同的属性将被添加到不同类型的 Data Annotation 属性中。 对于要验证的字段,将data-val属性设置为true。 对于 View 模型中标记为必需的属性,data-val-required属性将具有相关属性的错误消息的值。

实现

会有一个布局文件(_Layout.cshtml)来定义您的 web 应用的布局结构。 由于 JavaScript 库将在所有页面中使用,所以这里是添加普通功能(如不引人注目的验证)的合适位置。 只需将 JavaScript 库(以粗体突出显示)添加到布局文件(_Layout.cshtml)中,这样它们就可以用于所有View文件:

<!DOCTYPE html> 
<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>@ViewBag.Title</title> 
</head> 
<body> 
    <div> 
        @RenderBody() 
    </div> 

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>

</body> 
</html> 

除了删除了我们之前为验证字段而编写的 JavaScript 函数外,对 View 模型没有任何更改。 视图的完整代码如下所示:

@model Validation.ViewModels.EmployeeAddViewModel 

<div> 

    <form asp-controller="Employee" asp-action="Index" method="post" role="form"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span id="validationName" asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span id="validationDesignation" asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span id="validationSalary" asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 

        </table> 
    </form> 

</div> 

<br /><br /> <br /> 

<b> List of employees:</b> <br /> 
<div> 
    <table border="1"> 
        <tr> 
            <th> ID </th> 
            <th> Name </th> 
            <th> Designation </th> 
            <th> Salary </th> 
        </tr> 
        @foreach (var employee in Model.EmployeesList) 
        { 
            <tr> 
                <td>@employee.EmployeeId</td> 
                <td>@employee.Name</td> 
                <td>@employee.Designation</td> 
                <td>@employee.Salary</td> 
            </tr> 
        } 
    </table> 

</div> 

Implementation

上图描述了不显眼的客户端验证过程:

  1. Model/ViewModels添加数据注解。
  2. 视图取Model/ViewModels并生成 HTML。
  3. 从 View 模型中生成的 HTML 包含data-*属性:
    • 对于设置了Required属性的字段,将以错误消息作为其值创建data-val-required属性。
    • 对于带有MinLengthData Annotation 属性的字段,将data-val-minlength属性设置为错误消息值。
    • 对于范围 Data Annotation,将data-val-range属性设置为错误消息值。 data-val-range-max表示范围中的最大值,data-val-range-min属性表示范围中的最小值。
  4. jQuery 不引人注目的验证库使用data-*属性读取这些元素,并执行客户端验证。 这允许开发人员不用使用 JavaScript 编写分离验证代码,因为一切都是由配置本身解决的。

总结

在本章中,我们了解了验证的需求以及可用的不同类型的验证。 我们甚至讨论了客户端和服务器端验证的工作方式,以及每种验证类型的优缺点。 稍后,我们对代码进行了更改,以在服务器端验证输入数据。 然后,我们使用 JavaScript 在客户端本身验证输入数据。 最后,我们使用 jQuery 不显眼的库进行客户端验证,而无需编写 JavaScript 代码在客户端验证输入数据。

在下一章中,我们将讨论路由原理以及如何定制它。 在前面的一章中,我们看到了在 ASP 中路由的基础知识。 净 5 应用。 现在我们要深入探讨这个问题。