iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
自我挑戰組

.NET Core MVC網頁應用開發系列 第 29

.NET Core第29天_Model驗證配置準備流程_各種驗證資料註解使用方式

新建一個.net core mvc專案後

預設每一個檢視之所以都能套用版面配置頁
主要是在於藉由.\Views_ViewStart.cshtml進行的全域預設版面配置設置
https://ithelp.ithome.com.tw/upload/images/20210930/20107452N5PgQhdAak.png
而預設專案微軟驗證腳本採用的是
.\Views\Shared_ValidationScriptsPartial.cshtml當中的jquery兩個腳本

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

進行client驗證機制。

預設版面配置頁.\Views\Shared_Layout.cshtml,而針對腳本引入是沒有特別去添加,因此需再各自檢視中做引入。

@await RenderSectionAsync("Scripts", required: false)

在版面配置頁面中,以非同步方式轉譯名為之區段的內容

	
public System.Threading.Tasks.Task<Microsoft.AspNetCore.Html.HtmlString> RenderSectionAsync (string name, bool required);

參數
name (String)
要呈現的區段。

required(Boolean)
指出 name 必須使用頁面中的) (註冊區段 @section 。

https://ithelp.ithome.com.tw/upload/images/20210930/20107452tQXRX7MI1q.png

新增一控制器名ValidDemoController.cs跟相應Index檢視
Index檢視中去進行非同步引入驗證腳本隸屬的部分檢視

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

在ViewModel中要進行驗證相關的資料註解使用時
必須引用using System.ComponentModel.DataAnnotations;
各項註解用中括號包覆

預設各個衍生的驗證資料註解都是繼承自ValidationAttribute

#region 組件 System.ComponentModel.Annotations, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\5.0.0\ref\net5.0\System.ComponentModel.Annotations.dll
#endregion

#nullable enable


namespace System.ComponentModel.DataAnnotations
{
    //
    // 摘要:
    //     Serves as the base class for all validation attributes.
    public abstract class ValidationAttribute : Attribute
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.ValidationAttribute
        //     class.
        protected ValidationAttribute();
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.ValidationAttribute
        //     class by using the function that enables access to validation resources.
        //
        // 參數:
        //   errorMessageAccessor:
        //     The function that enables access to validation resources.
        //
        // 例外狀況:
        //   T:System.ArgumentNullException:
        //     errorMessageAccessor is null.
        protected ValidationAttribute(Func<string> errorMessageAccessor);
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.ValidationAttribute
        //     class by using the error message to associate with a validation control.
        //
        // 參數:
        //   errorMessage:
        //     The error message to associate with a validation control.
        protected ValidationAttribute(string errorMessage);

        //
        // 摘要:
        //     Gets or sets an error message to associate with a validation control if validation
        //     fails.
        //
        // 傳回:
        //     The error message that is associated with the validation control.
        public string? ErrorMessage { get; set; }
        //
        // 摘要:
        //     Gets or sets the error message resource name to use in order to look up the System.ComponentModel.DataAnnotations.ValidationAttribute.ErrorMessageResourceType
        //     property value if validation fails.
        //
        // 傳回:
        //     The error message resource that is associated with a validation control.
        public string? ErrorMessageResourceName { get; set; }
        //
        // 摘要:
        //     Gets or sets the resource type to use for error-message lookup if validation
        //     fails.
        //
        // 傳回:
        //     The type of error message that is associated with a validation control.
        public Type? ErrorMessageResourceType { get; set; }
        //
        // 摘要:
        //     Gets a value that indicates whether the attribute requires validation context.
        //
        // 傳回:
        //     true if the attribute requires validation context; otherwise, false.
        public virtual bool RequiresValidationContext { get; }
        //
        // 摘要:
        //     Gets the localized validation error message.
        //
        // 傳回:
        //     The localized validation error message.
        protected string ErrorMessageString { get; }

        //
        // 摘要:
        //     Applies formatting to an error message, based on the data field where the error
        //     occurred.
        //
        // 參數:
        //   name:
        //     The name to include in the formatted message.
        //
        // 傳回:
        //     An instance of the formatted error message.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        public virtual string FormatErrorMessage(string name);
        //
        // 摘要:
        //     Checks whether the specified value is valid with respect to the current validation
        //     attribute.
        //
        // 參數:
        //   value:
        //     The value to validate.
        //
        //   validationContext:
        //     The context information about the validation operation.
        //
        // 傳回:
        //     An instance of the System.ComponentModel.DataAnnotations.ValidationResult class.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        //
        //   T:System.ArgumentNullException:
        //     validationContext is null.
        //
        //   T:System.NotImplementedException:
        //     System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(System.Object,System.ComponentModel.DataAnnotations.ValidationContext)
        //     has not been implemented by a derived class.
        public ValidationResult? GetValidationResult(object? value, ValidationContext validationContext);
        //
        // 摘要:
        //     Determines whether the specified value of the object is valid.
        //
        // 參數:
        //   value:
        //     The value of the object to validate.
        //
        // 傳回:
        //     true if the specified value is valid; otherwise, false.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        //
        //   T:System.NotImplementedException:
        //     Neither overload of IsValid has been implemented by a derived class.
        public virtual bool IsValid(object? value);
        //
        // 摘要:
        //     Validates the specified object.
        //
        // 參數:
        //   value:
        //     The object to validate.
        //
        //   validationContext:
        //     The System.ComponentModel.DataAnnotations.ValidationContext object that describes
        //     the context where the validation checks are performed. This parameter cannot
        //     be null.
        //
        // 例外狀況:
        //   T:System.ComponentModel.DataAnnotations.ValidationException:
        //     Validation failed.
        //
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        //
        //   T:System.NotImplementedException:
        //     System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(System.Object,System.ComponentModel.DataAnnotations.ValidationContext)
        //     has not been implemented by a derived class.
        public void Validate(object? value, ValidationContext validationContext);
        //
        // 摘要:
        //     Validates the specified object.
        //
        // 參數:
        //   value:
        //     The value of the object to validate.
        //
        //   name:
        //     The name to include in the error message.
        //
        // 例外狀況:
        //   T:System.ComponentModel.DataAnnotations.ValidationException:
        //     value is not valid.
        //
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        public void Validate(object? value, string name);
        //
        // 摘要:
        //     Validates the specified value with respect to the current validation attribute.
        //
        // 參數:
        //   value:
        //     The value to validate.
        //
        //   validationContext:
        //     The context information about the validation operation.
        //
        // 傳回:
        //     An instance of the System.ComponentModel.DataAnnotations.ValidationResult class.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is malformed.
        //
        //   T:System.NotImplementedException:
        //     System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(System.Object,System.ComponentModel.DataAnnotations.ValidationContext)
        //     has not been implemented by a derived class.
        protected virtual ValidationResult? IsValid(object? value, ValidationContext validationContext);
    }
}

Required

RequiredAttribute.cs

#region 組件 System.ComponentModel.Annotations, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\5.0.0\ref\net5.0\System.ComponentModel.Annotations.dll
#endregion

#nullable enable


namespace System.ComponentModel.DataAnnotations
{
    //
    // 摘要:
    //     Specifies that a data field value is required.
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class RequiredAttribute : ValidationAttribute
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.RequiredAttribute
        //     class.
        public RequiredAttribute();

        //
        // 摘要:
        //     Gets or sets a value that indicates whether an empty string is allowed.
        //
        // 傳回:
        //     true if an empty string is allowed; otherwise, false. The default value is false.
        public bool AllowEmptyStrings { get; set; }

        //
        // 摘要:
        //     Checks that the value of the required data field is not empty.
        //
        // 參數:
        //   value:
        //     The data field value to validate.
        //
        // 傳回:
        //     true if validation is successful; otherwise, false.
        //
        // 例外狀況:
        //   T:System.ComponentModel.DataAnnotations.ValidationException:
        //     The data field value was null.
        public override bool IsValid(object? value);
    }
}

Required中還可以特別去設置
AllowEmptyStrings的特性,該特性只有在Server-Side下才有作用。
代表是否遠許一個空字串""(雙引號包覆的空字串),空null。
在進行模型物件綁定輸入框時,我們在輸入框輸入的空字串會被自動轉為null因此AllowEmptyStrings=true會沒有效果,不會去驗證是否為空字串("")。
需要額外配置另一個特性DisplayFormat,去禁止將空字串("")轉為null,也就是ConvertEmptyStringToNull屬性設置為false。

新建好一個RequeredController.cs

using DotNet5App7.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class RequiredController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Index(RequiredViewModel model)
        {

            return View(model);
        }
    }
}

RequiredViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace DotNet5App7.Models
{
    public class RequiredViewModel
    {
        [Display(Name="姓名")]
        [Required(AllowEmptyStrings = true, ErrorMessage = "{0}不可為空!")]
        [DisplayFormat(ConvertEmptyStringToNull = false)]
        public string Name { get; set; }
        [Display(Name ="年齡")]
        [Required(ErrorMessage ="{0}不可為空!")]
        public int Age { get; set; }
    }
}

./Views/Required/Index.cshtml

@{
    ViewData["Title"] = "非空驗證";
}

@model RequiredViewModel
<form method="post">
    <div>
        <label asp-for="Name"></label>
        <input type="text" asp-for="Name" />
        <span asp-validation-for="Name"></span>
    </div>
    <div>
        <label asp-for="Age"></label>
        <input type="text" asp-for="Age" />
        <span asp-validation-for="Age"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>


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

原先設置運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452J3AS4pKzIk.png

調整之後
https://ithelp.ithome.com.tw/upload/images/20210930/20107452l3XSXGLuGX.png

**StringLength **
主要可以設置最小跟最大字元長度,若只設定一個參數時代表最大長度。
長度是以字元個數來算,一個中文字跟英文字母都算一個字元。
相關的屬性還有MaxLength跟MinLength,當同時有使用到MaxLength跟MinLength屬性時,也相當於使用StringLength屬性。

MaxLength
指定最大允許輸入字元數

MinLength
指定最少輸入字元數

LengthViewModel.cs範本

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

namespace DotNet5App7.Models
{
    public class LengthViewModel
    {
        [Display(Name = "用户名")]
        //[MinLength(6, ErrorMessage = "{0}最小字元數是{1}。")]
        //[MaxLength(10,ErrorMessage ="{0}最大字字元是{1}。")]
        [StringLength(10, MinimumLength = 6, ErrorMessage = "{0}的最小字元數是{2},最大字元數是{1}。")]
        [Required(ErrorMessage = "{0}不可為空。")]
        public string UserName { get; set; }
    }
}

LengthController.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class LengthController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

./Views/Length/Index.cshtml

@{
    ViewData["Title"] = "字元長度驗證";
}
@model LengthViewModel

<form method="post">
    <div>
        <label asp-for="UserName"></label>
        <input type="text" asp-for="UserName" />
        <span asp-validation-for="UserName"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>
@section scripts{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452aFnjvqywu1.png

RegularExpression

正規表示式匹配驗證,比方email 、電話、年齡。
RegularExpressionAttribute.cs

#region 組件 System.ComponentModel.Annotations, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\5.0.0\ref\net5.0\System.ComponentModel.Annotations.dll
#endregion

#nullable enable


namespace System.ComponentModel.DataAnnotations
{
    //
    // 摘要:
    //     Specifies that a data field value in ASP.NET Dynamic Data must match the specified
    //     regular expression.
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class RegularExpressionAttribute : ValidationAttribute
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.RegularExpressionAttribute
        //     class.
        //
        // 參數:
        //   pattern:
        //     The regular expression that is used to validate the data field value.
        //
        // 例外狀況:
        //   T:System.ArgumentNullException:
        //     pattern is null.
        public RegularExpressionAttribute(string pattern);

        //
        // 摘要:
        //     Gets or sets the amount of time in milliseconds to execute a single matching
        //     operation before the operation times out.
        //
        // 傳回:
        //     The amount of time in milliseconds to execute a single matching operation.
        public int MatchTimeoutInMilliseconds { get; set; }
        //
        // 摘要:
        //     Gets the regular expression pattern.
        //
        // 傳回:
        //     The pattern to match.
        public string Pattern { get; }

        //
        // 摘要:
        //     Formats the error message to display if the regular expression validation fails.
        //
        // 參數:
        //   name:
        //     The name of the field that caused the validation failure.
        //
        // 傳回:
        //     The formatted error message.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is ill-formed.
        //
        //   T:System.ArgumentException:
        //     The System.ComponentModel.DataAnnotations.RegularExpressionAttribute.Pattern
        //     is not a valid regular expression.
        public override string FormatErrorMessage(string name);
        //
        // 摘要:
        //     Checks whether the value entered by the user matches the regular expression pattern.
        //
        // 參數:
        //   value:
        //     The data field value to validate.
        //
        // 傳回:
        //     true if validation is successful; otherwise, false.
        //
        // 例外狀況:
        //   T:System.ComponentModel.DataAnnotations.ValidationException:
        //     The data field value did not match the regular expression pattern.
        //
        //   T:System.InvalidOperationException:
        //     The current attribute is ill-formed.
        //
        //   T:System.ArgumentException:
        //     System.ComponentModel.DataAnnotations.RegularExpressionAttribute.Pattern is not
        //     a valid regular expression.
        public override bool IsValid(object? value);
    }
}

新建控制器和相應檢視
RegularController.cs 就用預設回傳Index,不動

./Views/Regular/Index.cshtml

@{
    ViewData["Title"] = "正規表示式匹配驗證";
}
@model RegularViewModel
<form>
    <div>
        <label asp-for="Age"></label>
        <input type="text" asp-for="Age" />
        <span asp-validation-for="Age"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>
@section scripts{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

RegularViewModel.cs

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

namespace DotNet5App7.Models
{
    public class RegularViewModel
    {
        [Display(Name = "年齡")]
        [RegularExpression("[1-9][0-9]", ErrorMessage = "{0}限定輸入兩位數字,開頭不可用0。")]
        public int Age { get; set; }
    }
}

運行效果

https://ithelp.ithome.com.tw/upload/images/20210930/20107452O19vuw8bAm.png

Range

Range屬性接受int , double的參數,可用於驗證是否坐落特定數值範圍。
該屬性中可以用Type資料型態作為參數,用於接收傳入參數為string的可用typeof()來進行型態轉換。Range屬性中最小值和最大值,也是包括在範圍內的。

RangeAttribute.cs

#region 組件 System.ComponentModel.Annotations, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\5.0.0\ref\net5.0\System.ComponentModel.Annotations.dll
#endregion

#nullable enable


namespace System.ComponentModel.DataAnnotations
{
    //
    // 摘要:
    //     Specifies the numeric range constraints for the value of a data field.
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class RangeAttribute : ValidationAttribute
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.RangeAttribute
        //     class by using the specified minimum and maximum values.
        //
        // 參數:
        //   minimum:
        //     Specifies the minimum value allowed for the data field value.
        //
        //   maximum:
        //     Specifies the maximum value allowed for the data field value.
        public RangeAttribute(double minimum, double maximum);
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.RangeAttribute
        //     class by using the specified minimum and maximum values.
        //
        // 參數:
        //   minimum:
        //     Specifies the minimum value allowed for the data field value.
        //
        //   maximum:
        //     Specifies the maximum value allowed for the data field value.
        public RangeAttribute(int minimum, int maximum);
        //
        // 摘要:
        //     Initializes a new instance of the System.ComponentModel.DataAnnotations.RangeAttribute
        //     class by using the specified minimum and maximum values and the specific type.
        //
        // 參數:
        //   type:
        //     Specifies the type of the object to test.
        //
        //   minimum:
        //     Specifies the minimum value allowed for the data field value.
        //
        //   maximum:
        //     Specifies the maximum value allowed for the data field value.
        //
        // 例外狀況:
        //   T:System.ArgumentNullException:
        //     type is null.
        public RangeAttribute(Type type, string minimum, string maximum);

        //
        // 摘要:
        //     Gets or sets a value that determines whether any conversions of the value being
        //     validated to System.ComponentModel.DataAnnotations.RangeAttribute.OperandType
        //     as set by the type parameter of the System.ComponentModel.DataAnnotations.RangeAttribute.#ctor(System.Type,System.String,System.String)
        //     constructor use the invariant culture or the current culture.
        //
        // 傳回:
        //     true to use the invariant culture for any conversions; false to use the culture
        //     that is current at the time of the validation.
        public bool ConvertValueInInvariantCulture { get; set; }
        //
        // 摘要:
        //     Gets the maximum allowed field value.
        //
        // 傳回:
        //     The maximum value that is allowed for the data field.
        public object Maximum { get; }
        //
        // 摘要:
        //     Gets the minimum allowed field value.
        //
        // 傳回:
        //     The minimum value that is allowed for the data field.
        public object Minimum { get; }
        //
        // 摘要:
        //     Gets the type of the data field whose value must be validated.
        //
        // 傳回:
        //     The type of the data field whose value must be validated.
        public Type OperandType { get; }
        //
        // 摘要:
        //     Gets or sets a value that determines whether string values for System.ComponentModel.DataAnnotations.RangeAttribute.Minimum
        //     and System.ComponentModel.DataAnnotations.RangeAttribute.Maximum are parsed using
        //     the invariant culture rather than the current culture.
        public bool ParseLimitsInInvariantCulture { get; set; }

        //
        // 摘要:
        //     Formats the error message that is displayed when range validation fails.
        //
        // 參數:
        //   name:
        //     The name of the field that caused the validation failure.
        //
        // 傳回:
        //     The formatted error message.
        //
        // 例外狀況:
        //   T:System.InvalidOperationException:
        //     The current attribute is ill-formed.
        public override string FormatErrorMessage(string name);
        //
        // 摘要:
        //     Checks that the value of the data field is in the specified range.
        //
        // 參數:
        //   value:
        //     The data field value to validate.
        //
        // 傳回:
        //     true if the specified value is in the range; otherwise, false.
        //
        // 例外狀況:
        //   T:System.ComponentModel.DataAnnotations.ValidationException:
        //     The data field value was outside the allowed range.
        //
        //   T:System.InvalidOperationException:
        //     The current attribute is ill-formed.
        public override bool IsValid(object? value);
    }
}

新建好RangeController.cs跟相應Index檢視

Index.cshtml

@{
    ViewData["Title"] = "範圍驗證";
}
@model RangeViewModel
<form>
    <div>
        <label asp-for="Price"></label>
        <input type="text" asp-for="Price" />
        <span asp-validation-for="Price"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>

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

RangeViewModel.cs

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

namespace DotNet5App7.Models
{
    public class RangeViewModel
    {
        [Display(Name = "價格")]
        //[Range(10.1,30.5,ErrorMessage ="{0}範圍是{1}-{2}。")]
        [Range(typeof(string), "10.1", "30.5", ErrorMessage = "{0}範圍是{1}-{2}。")]
        public double Price { get; set; }
    }
}

運行效果

https://ithelp.ithome.com.tw/upload/images/20210930/20107452aIWjKNNEkt.png

Compare

兩次輸入是否一致的驗證
比方密碼、驗證密碼

新建好CompareController.cs跟相應Index檢視

Index.cshtml
(這裡密碼暫時用明碼顯示方式便於查看)

@{
    ViewData["Title"] = "比較驗證";
}
@model CompareViewModel
<form>
    <div>
        <label asp-for="Password"></label>
        <input type="text" asp-for="Password" />
        <span asp-validation-for="Password"></span>
    </div>
    <div>
        <label asp-for="ConfirmPassword"></label>
        <input type="text" asp-for="ConfirmPassword" />
        <span asp-validation-for="ConfirmPassword"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>
@section scripts{
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

CompareViewModel.cs

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

namespace DotNet5App7.Models
{
    public class CompareViewModel
    {
        [Display(Name = "密碼")]
        [Required]
        public string Password { get; set; }

        [Display(Name = "確認密碼")]
        [Compare("Password", ErrorMessage = "{0}和密碼不一致。")]
        [Required]
        public string ConfirmPassword { get; set; }
    }
}

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452h9Hakj7Cr9.png

Remote

可用來實踐利用Server-Side的 call back函數執行client-side驗證。
用法[Remote("動作方法名","隸屬控制器名")],其隸屬於Microsoft.AspNetCore.Mvc該命名空間。

RemoteAttribute.cs

#region 組件 Microsoft.AspNetCore.Mvc.ViewFeatures, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\5.0.0\ref\net5.0\Microsoft.AspNetCore.Mvc.ViewFeatures.dll
#endregion

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;

namespace Microsoft.AspNetCore.Mvc
{
    //
    // 摘要:
    //     A Microsoft.AspNetCore.Mvc.RemoteAttributeBase for controllers which configures
    //     Unobtrusive validation to send an Ajax request to the web site. The invoked action
    //     should return JSON indicating whether the value is valid.
    //
    // 備註:
    //     Does no server-side validation of the final form submission.
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class RemoteAttribute : RemoteAttributeBase
    {
        //
        // 摘要:
        //     Initializes a new instance of the Microsoft.AspNetCore.Mvc.RemoteAttribute class.
        //
        // 參數:
        //   routeName:
        //     The route name used when generating the URL where client should send a validation
        //     request.
        //
        // 備註:
        //     Finds the routeName in any area of the application.
        public RemoteAttribute(string routeName);
        //
        // 摘要:
        //     Initializes a new instance of the Microsoft.AspNetCore.Mvc.RemoteAttribute class.
        //
        // 參數:
        //   action:
        //     The action name used when generating the URL where client should send a validation
        //     request.
        //
        //   controller:
        //     The controller name used when generating the URL where client should send a validation
        //     request.
        //
        // 備註:
        //     If either action or controller is null, uses the corresponding ambient value.
        //     Finds the controller in the current area.
        public RemoteAttribute(string action, string controller);
        //
        // 摘要:
        //     Initializes a new instance of the Microsoft.AspNetCore.Mvc.RemoteAttribute class.
        //
        // 參數:
        //   action:
        //     The action name used when generating the URL where client should send a validation
        //     request.
        //
        //   controller:
        //     The controller name used when generating the URL where client should send a validation
        //     request.
        //
        //   areaName:
        //     The name of the area containing the controller.
        //
        // 備註:
        //     If either action or controller is null, uses the corresponding ambient value.
        //     If areaName is null, finds the controller in the root area. Use the Microsoft.AspNetCore.Mvc.RemoteAttribute.#ctor(System.String,System.String)
        //     overload find the controller in the current area. Or explicitly pass the current
        //     area's name as the areaName argument to this overload.
        public RemoteAttribute(string action, string controller, string areaName);
        //
        // 摘要:
        //     Initializes a new instance of the Microsoft.AspNetCore.Mvc.RemoteAttribute class.
        //
        // 備註:
        //     Intended for subclasses that support URL generation with no route, action, or
        //     controller names.
        protected RemoteAttribute();

        //
        // 摘要:
        //     Gets or sets the route name used when generating the URL where client should
        //     send a validation request.
        protected string RouteName { get; set; }

        protected override string GetUrl(ClientModelValidationContext context);
    }
}

新建好RemoteController跟相應Index檢視

RemoteController.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class RemoteController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// Call Back Function for Remote Validation
        /// </summary>
        /// <param name="name"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public JsonResult CheckName(string name)
        {
            bool b = false;
            if (name.Equals("Andy"))
            {
                b = true;
            }
            return Json(b);
        }
    }
}

Index.cshtml

@{
    ViewData["Title"] = "Remote驗證";
}
@model RemoteViewModel
<form>
    <div>
        <label asp-for="Name"></label>
        <input type="text" asp-for="Name" />
        <span asp-validation-for="Name"></span>
    </div>
    <div>
        <label asp-for="Age"></label>
        <input type="text" asp-for="Age" />
        <span asp-validation-for="Age"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>

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

RemoteViewModel.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Models
{
    public class RemoteViewModel
    {
        [Display(Name = "姓名")]
        [Remote("CheckName", "Remote")]
        public string Name { get; set; }

        [Display(Name = "年齡")]
        public int Age { get; set; }
    }
}

這裡可以觀察運行結果
當輸入一個隨意的名稱時會自動到server-side我們指定的call back函數做驗證
https://ithelp.ithome.com.tw/upload/images/20210930/20107452Ylc1rK4pdp.png

當改為Andy時才消失警示(這裡判斷是大小寫有差)
https://ithelp.ithome.com.tw/upload/images/20210930/20107452RVy4JZ4Rm7.png

AdditionalFields屬性於Remote下可以在多透過設置它來附加額外要驗證的參數欄位。
使call back函數可以傳進更多參數

微調後的RemoteViewModel.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Models
{
    public class RemoteViewModel
    {
        [Display(Name = "姓名")]
        [Remote("CheckName", "Remote", AdditionalFields = "Age", HttpMethod = "GET", ErrorMessage = "請輸入正確的內容。")]
        public string Name { get; set; }

        [Display(Name = "年齡")]
        public int Age { get; set; }
    }
}

這裡還可以透過HttpMethod設置要採用POST或GET(默認)
AdditionalFields 當有多個參數則可用逗號分隔。

https://ithelp.ithome.com.tw/upload/images/20210930/20107452JexucVG3z8.png

微調後的RemoteController.cs

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class RemoteController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// Call Back Function for Remote Validation
        /// </summary>
        /// <param name="name"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public JsonResult CheckName(string name,int age)
        {
            bool b = false;
            if (name.Equals("Andy") && age > 30)
            {
                b = true;
            }
            return Json(b);
        }
    }
}

運行效果

https://ithelp.ithome.com.tw/upload/images/20210930/20107452BY94iR4W6b.png

https://ithelp.ithome.com.tw/upload/images/20210930/20107452jvLe98TYqa.png

DataType
用於設定使用顯示/編輯模板呈現的模型物件屬性轉換的HTML控件類型。
以下這些enum選項都可以對應HTML呈現

https://ithelp.ithome.com.tw/upload/images/20210930/20107452ZtXGJpPI0a.png

新建好DataTypeController跟相應Index檢視

Index.cshtml

@{
    ViewData["Title"] = "DataType ";
}

@model DataTypeViewModel
<div>
    <input asp-for="Title" />
</div>

DataTypeViewModel.cs

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

namespace DotNet5App7.Models
{
    public class DataTypeViewModel
    {
        [DataType(DataType.DateTime)]
        public string Title { get; set; }
    }
}

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452R4fXhlPOWh.png
可以看到原生html input的type我沒設置但已經被轉為datetime-local的型態
換言之,往後我們可以直接在ViewModel的屬性藉由DataType來抉擇對應要呈現捨麼樣子的html 。

https://ithelp.ithome.com.tw/upload/images/20210930/20107452tDf6Menljm.png

https://ithelp.ithome.com.tw/upload/images/20210930/20107452CVXGNt78Or.png

那前面還有提到所謂可用於設定使用顯示/編輯模板呈現
我們改套用編輯模板

這裡我們修改多個屬性的ViewModel

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

namespace DotNet5App7.Models
{
    public class DataTypeViewModel
    {
        [DataType(DataType.EmailAddress)]
        public string Title { get; set; }

        [DataType(DataType.Currency)]
        public int Age { get; set; }
    }
}

Index.cshml部分我們只需要一句就能取代很多行input tag綁定,套用編輯模板。

預設都在HtmlHelper底下很多種和Model相應的模板可用
https://ithelp.ithome.com.tw/upload/images/20210930/201074521X9RZy3hQF.png

這裡用@Html.EditorForModel()
https://ithelp.ithome.com.tw/upload/images/20210930/201074522YbcJYplAO.png

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452gyLvM8XJWn.png

EmailAddress

可將指定該特性的ViewModel屬性轉為type為email的input tag,可自動套用HTML5郵件格式的驗證。

這裡新增一個EmailAddressController和相應的Index檢視

Index.cshtml

@{
    ViewData["Title"] = "EmailAddress";
}
@model EmailAddressViewModel
<form>
    <div>
        <label asp-for="Email"></label>
        <input asp-for="Email" />
        <span asp-validation-for="Email"></span>
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>

EmailAddressViewModel.cs

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

namespace DotNet5App7.Models
{
    public class EmailAddressViewModel
    {
        [Display(Name = "郵件")]
        [EmailAddress()]
        public string Email { get; set; }
    }
}

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452zWFRJHMzfI.png

Display

前面一直用的主要就是用於做客製化label顯示
其包含order屬性,默認是採用升冪(序)由小到大排列,對於Display特性的Order屬性,可對ViewModel中屬性於編輯模板顯示順序進行排列(只限於編輯模板或顯示模板)。

新建一個Display控制器和相應Index檢視以及DisplayViewModel

Index檢視

@{
    ViewData["Title"] = "Display";
}

@model DisplayViewModel
<form>
    <div>
        @Html.EditorForModel()

        @*<label asp-for="Id"></label>
        <input type="text" asp-for="Id" />
        <br />
        <label asp-for="Name"></label>
        <input type="text" asp-for="Name" />
        <br />
        <label asp-for="Sex"></label>
        <input type="checkbox" asp-for="Sex" />*@

    </div>
</form>

DisplayViewModel

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

namespace DotNet5App7.Models
{
    public class DisplayViewModel
    {
        [Display(Name="ID",Order =3)]
        public int Id { get; set; }
        [Display(Name="姓名",Order =1)]
        public string Name { get; set; }
        [Display(Name="性別",Order =2)]
        public bool Sex { get; set; }
    }
}

運行效果(預設bool會產生checkbox , ID為數字則呈現number選單)
https://ithelp.ithome.com.tw/upload/images/20210930/201074529DAE38FDyG.png

默認情況下編輯模板會依照屬性由上至下依序排列。
https://ithelp.ithome.com.tw/upload/images/20210930/20107452CVO9mqa09o.png

DisplayFormat
該屬性可用於對模型物件中屬性值進行格式化(Ex:貨幣、日期時間),藉由
DataFormatString ="格式類型"語法來指定要顯示的格式。

新建DisplayFormatController跟相應的Index檢視
還有對應ViewModel

DisplayFormatViewModel.cs

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

namespace DotNet5App7.Models
{
    public class DisplayFormatViewModel
    {
        [Display(Name = "價格")]
        [DisplayFormat(DataFormatString = "{0:C}")]
        public double Price { get; set; }
    }
}

DisplayFormatController.cs

using DotNet5App7.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class DisplayFormatController : Controller
    {
        public IActionResult Index()
        {
            DisplayFormatViewModel model = new DisplayFormatViewModel()
            {
                Price = 45.58
            };
            return View(model);
        }
    }
}

這裡用[DisplayFormat(DataFormatString = "{0:C}")]去進行對應金錢格式設置,
該屬性可以去判定隸屬Server所在國家地理位置來做相應貨幣格式變換。

Index.cshtml

@{ 
    ViewData["Title"] = "DisplayFormat";
}

@model DisplayFormatViewModel

<div>
    <label asp-for="Price"></label>
    <input type="text" asp-for="Price" />
</div>

當運行時會發現沒有套用轉為金錢格式的樣子

https://ithelp.ithome.com.tw/upload/images/20210930/20107452P5uVtmv0pd.png

原因在於我們目前處於編輯文字輸入框的模式
要在對ViewModel中屬性做額外ApplyFormatInEditMode為true的設置

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

namespace DotNet5App7.Models
{
    public class DisplayFormatViewModel
    {
        [Display(Name = "價格")]
        [DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = true)]
        public double Price { get; set; }
    }
}

再次運行即可
https://ithelp.ithome.com.tw/upload/images/20210930/201074526HCcROmXc0.png

編輯模式在此也可用HtmlHelper提供方法搭配lambda語法來取代

@{ 
    ViewData["Title"] = "DisplayFormat";
}

@model DisplayFormatViewModel

<div>
    <label asp-for="Price"></label>
    <input type="text" asp-for="Price" />
    @Html.EditorFor(m=>m.Price)
</div>

https://ithelp.ithome.com.tw/upload/images/20210930/2010745219Tn0bhPDC.png

DisplayFormat中的ConvertEmptyStringToNull默認為true,可將模型的屬性值中若是空字串會被轉為null。

這裡擴充一個string屬性Name

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

namespace DotNet5App7.Models
{
    public class DisplayFormatViewModel
    {
        [Display(Name = "價格")]
        [DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = true)]
        public double Price { get; set; }
        [DisplayFormat(ConvertEmptyStringToNull =false)]
        public string Name { get; set; }
    }
}

在檢視我們調整成一個表單提交的畫面

@{ 
    ViewData["Title"] = "DisplayFormat";
}

@model DisplayFormatViewModel

<form method="post">
    <div>
        <label asp-for="Price"></label>
        <input type="text" asp-for="Price" />
        @Html.EditorFor(m => m.Price)
    </div>
    <div>
        <label asp-for="Name"></label>
        <input type="text" asp-for="Name" />
        @Html.DisplayFor(m => m.Name)
    </div>
    <div>
        <input type="submit" value="提交" />
    </div>
</form>

DisplayFormatController中調整從View傳進來模型接收的POST action method

using DotNet5App7.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DotNet5App7.Controllers
{
    public class DisplayFormatController : Controller
    {
        public IActionResult Index()
        {
            DisplayFormatViewModel model = new DisplayFormatViewModel()
            {
                Price = 45.58
            };
            return View(model);
        }

        [HttpPost]
        public IActionResult Index(DisplayFormatViewModel model)
        {
            return View(model);
        }
    }
}

運行效果
https://ithelp.ithome.com.tw/upload/images/20210930/20107452VPZefUEOWf.png

若這裡不去對屬性Name寫[DisplayFormat(ConvertEmptyStringToNull =false)]
或者寫[DisplayFormat(ConvertEmptyStringToNull =true)]
https://ithelp.ithome.com.tw/upload/images/20210930/20107452EYNRDaUjeE.png

則Server-Side接收到會是null

在DisplayFormat中HtmlEncode屬性可協助我們是否要開啟或關閉要採用HTML來編碼
默認為true啟用的。

而NullDisplayText屬性則是當屬性值為null或空字串("")時,可設置預設為空要顯示的默認字串內容。

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

namespace DotNet5App7.Models
{
    public class DisplayFormatViewModel
    {
        [Display(Name = "價格")]
        [DisplayFormat(DataFormatString = "{0:C}", ApplyFormatInEditMode = true)]
        public double Price { get; set; }
        [DisplayFormat(NullDisplayText ="為空時的預設值")]
        public string Name { get; set; }
    }
}

https://ithelp.ithome.com.tw/upload/images/20210930/20107452UA9S60i1wS.png

本文同步發表至個人部落格(分兩篇->因為鐵人賽規劃30天原先blog分兩篇章)
.NET Core第29天_Model驗證配置準備流程_各種驗證資料註解使用方式_part1(Required,StringLength,MaxLength,MinLength,RegularExpression,Range,Compare)
https://coolmandiary.blogspot.com/2021/08/net-core29modelpart1.html

.NET Core第30天_Model驗證配置準備流程_各種驗證資料註解使用方式_part2(Remote,DataType,EmailAddress,Display,DisplayFormat驗證)
https://coolmandiary.blogspot.com/2021/08/net-core30modelpart2remotedatatypeemail.html


上一篇
.NET Core第28天_ValidationMessageTagHelper和ValidationSummaryTagHelper的使用
下一篇
(完結).NET Core第30天_Controller Action的各種不同回傳
系列文
.NET Core MVC網頁應用開發30

尚未有邦友留言

立即登入留言