.NET Core第30天_Model驗證配置準備流程_各種驗證資料註解使用方式_part2(Remote,DataType,EmailAddress,Display,DisplayFormat驗證)

 

Remote

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

RemoteAttribute.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@{
    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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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函數做驗證



當改為Andy時才消失警示(這裡判斷是大小寫有差)




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

微調後的RemoteViewModel.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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 當有多個參數則可用逗號分隔。



微調後的RemoteController.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
        }
    }
}


運行效果




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




新建好DataTypeController跟相應Index檢視

Index.cshtml

1
2
3
4
5
6
7
8
@{
    ViewData["Title"] = "DataType ";
}

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


DataTypeViewModel.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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; }
    }
}



運行效果

可以看到原生html input的type我沒設置但已經被轉為datetime-local的型態
換言之,往後我們可以直接在ViewModel的屬性藉由DataType來抉擇對應要呈現捨麼樣子的html 。




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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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相應的模板可用

這裡用@Html.EditorForModel()




運行效果



EmailAddress

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


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

Index.cshtml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@{
    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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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; }
    }
}


運行效果




Display

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


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

Index檢視

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@{
    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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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選單)
默認情況下編輯模板會依照屬性由上至下依序排列。



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

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

DisplayFormatViewModel.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@{ 
    ViewData["Title"] = "DisplayFormat";
}

@model DisplayFormatViewModel

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


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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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; }
    }
}

再次運行即可

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@{ 
    ViewData["Title"] = "DisplayFormat";
}

@model DisplayFormatViewModel

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



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

這裡擴充一個string屬性Name

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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; }
    }
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
@{ 
    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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
        }
    }
}

運行效果

若這裡不去對屬性Name寫[DisplayFormat(ConvertEmptyStringToNull =false)]
或者寫[DisplayFormat(ConvertEmptyStringToNull =true)]

則Server-Side接收到會是null

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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; }
    }
}











留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header