ASP.NET Core 内置的Tag Helpers

ASP.NET Core 框架内置了大量的Tag Helpers,以asp-*前缀开始,他们用于增强表单,验证消息,设计布局等,在这节中我们将继续讨论内置的Tag Helpers,例如表单控件,输入控件,选择控件,标签控件,锚点标签,文本控件,CSS,JS 和Cache

1 Form帮助标签

ASP.NET Core表单控件用来增强原始HTML表单的健壮性和高效性,当业务发生变化时这些表单的可维护性很高,这些标记为表单生成action属性以及隐藏的请求验证令牌,以防止跨站点伪造请求.

下面给与了表单常用的帮助标签

方法 描述

asp-controller

根据应用程序的路由指定目标Controller,如果省略,使用当前视图文件所在的控制器

asp-action

根据应用程序的路由指定目标Action方法,如果省略,使用视图文件当前Action方法

asp-route-*

指定url额外的段,例如 asp-route-id 使用提供id段的值

asp-route

通过指定路由的名称生成action属性

asp-area

指定目标区域的名称
asp-antiforgery
生成一个隐藏的请求验证令牌用来防止跨站点请求攻击,经常和[ValidateAntiForgeryToken]特性一起使用,[ValidateAntiForgeryToken]使用在HTTP Post方法上
如果我们的应用程序只有一个路由定义在Programe.cs类中
app.MapControllerRoute(    name: "default",    pattern: "{controller=Home}/{action=Index}/{id?}");
现在我们使用帮助标签来创建表单
<form method="post" asp-controller="Home" asp-action="Create">...</form>
在这种情况下表单的action方法将生成/Home/Create,我们检查一下表单中生成的HTML
<form method="post" action="/Home/Create">...</form>
在应用程序中的目录下Views->Home文件夹下创建一个新的视图文件,名字为Create.cshtml, 添加下面代码:
@model Product@{    ViewData["Title"] = "新增";}<form method="post" asp-controller="Home" ,asp-action="Create">    <div class="mb-3 row">        <label class="col-sm-1 control-label" for="Name">名称:</label>        <div class="col-sm-11">            <input class="form-control" name="name" />        </div>    </div>    <div class="mb-3 row">        <label class="col-sm-1 control-label" for="Price">价格</label>        <div class="col-sm-11">            <input class="form-control" name="price" />        </div>    </div>    <div class="mb-3 row">        <label class="col-sm-1 control-label" for="Quantity">数量</label>        <div class="col-sm-11">            <input class="form-control" name="quantity" />        </div>    </div>    <div class="col-sm-11 offset-sm-1">        <button type="submit" class="btn btn-primary">新增</button>    </div></form>

我们视图中创建一个form标签,使用asp-controller="Home"和asp-action="Create"帮助标签创建html表单的action属性

表单的method是post,当提交表单时Create的方法被调用,在HomeController中添加2个Create方法,一个是HTTPGet另外一个是HttpPost

这两个Action方法的代码如下:

public IActionResult Create(){    return View();}[HttpPost]public IActionResult Create(Product product){    return RedirectToAction("Index");}
我们在label元素上使用for属性,用来绑定label元素,输入控件的name属性分别被赋值为-name,price&quantity,这些名称是Product.cs类的属性,我们通过使用控件的名称来绑定action方法的参数
运行应用程序,你将会看到一个表单,在浏览器中检查表单元素并且你将看到action特性被创建为/Home/Create
图片展示如下:

ASP.NET Core 内置的Tag Helpers

提交表单新的产品将添加到repository,将会跳转到Index视图在产品列表显示所有的产品

注意:表单所在的Create视图位于HomeController中,对应的Action方法为Create,因此我们也不需要应用asp-action和asp-controller帮助标签
下面代码也能工作:
<form method="post">....</form>
1.1 asp-antiforgery
asp-antiforgery生成一个隐藏的请求验证令牌,这个令牌为antiforgerytoken,为了防止CSRF攻击,当你在你的表单上使用这个特性,ASP.NET Core做两件事:

1 在表单的隐藏域中添加一个安全的token

2 在响应添加一个cookie

如果表单中包含Cookie和隐藏的值,应用程序将处理这个请求,恶意站点不能访问,因此阻止了CSRF

为了使用这个特性,在表单元素中添加帮助标签asp-antiforgery="true", 并且在对应的请求方法上添加[ValidateAntiForgeryToken]特性
@model Product@{    ViewData["Title"] = "Create";}<form method="post" asp-controller="Home" asp-action="Create" asp-antiforgery="true">    // removed for clarity</form>
在表单的Create视图中添加asp-antiforgery属性:
[HttpPost][ValidateAntiForgeryToken]public IActionResult Create(Product product){    return RedirectToAction("Index");}
运行应用程序并且检查Create视图的HTML代码,你会在form元素内发现一个隐藏域并且它包含一个token值,如下所示:

ASP.NET Core 内置的Tag Helpers

现在打开Developer Tools, 进入Application页,接着在左边点击Cookies,你将会看到anti-forgery cookie,它将被一块发送到控制器

ASP.NET Core 内置的Tag Helpers

[ValidateAntiForgeryToken]特性将自动验证这个cookie和token

2 Label帮助标签

Label帮助标签用来设置HTML中的Label,我们在label上添加asp-for属性,在label标签上创建一个for属性,为了测试我们在为Create视图中的Label添加asp-for属性

运行应用程序,打开浏览器检查生成html源码

ASP.NET Core 内置的Tag Helpers

3 Input帮助标签
ASP.NET Core Input帮助标签用来将输入元素绑定到模型表达式-asp-for="expression",当我们在input输入控件中添加asp-for属性,input控件会使用模型表达式设置name,id,type和value属性的值,我们修改Create视图中的Input元素for修改为asp-for
运行应用程序并且查看HTML源码,我们发现input输入框包含了id,name,type,value四个属性

ASP.NET Core 内置的Tag Helpers

你会发现Name和Price输入框的type是Text,但是Quantity输入框的类型是number,这是因为ASP.NET Core基于模型属性类型给HTML控件提供type的值

下面表格告诉我们不同类型的值是如何对应的:

模型类型

输入框类型

byte, sbyte, int, uint, short, ushort, long, ulong

Number

float, double, decimal

text

string

text

bool

checkbox

DateTime

datetime

4 Select帮助标签

Select帮助标签用来指定模型属性并且针对下拉框元素可选

1 asp-for 用模型属性名字设置为Select元素的id和name属性

2 asp-items 给Select元素提供可选的值

我们把Create视图中quantity字段从输入框修改为选择框:

ASP.NET Core 内置的Tag Helpers

现在我们检查一下在浏览器中生成的HTML代码,我们看到把id和value的值设置为Product.cs类的Quantity属性,代码如下:

ASP.NET Core 内置的Tag Helpers

ASP.NET Core框架非常智能,选择控件能够自动选择默认值,为了了解它,添加Edit方法在Home控制器中,这个方法将从仓储中返回最后添加的记录,方法代码如下:
public ViewResult Edit() => View("Create", _repository.Products.Last());

进入编辑页面的地址https://localhost:7182/Home/Edit,我们能清楚看到最后一条记录时400被自动选择在选择控件中

ASP.NET Core 内置的Tag Helpers

检查生成的HTML源码,注意selected特性应用到了包含400值的选项,代码如下:

ASP.NET Core 内置的Tag Helpers

4.1 asp-items 特性

asp-items 特性使用指定下拉框列表中的元素,它用来帮助我们从数据源生成下拉框列表中的元素,修改Create视图下拉框代码,现在使用asp-items特性:

<select class="form-control" asp-for="Quantity" asp-items="ViewBag.Quantity">    <option disabled selected value="">Select Quantity</option></select>
我们指定ViewBag.Quantity作为asp-items特性的值,ViewBag将包含一个SelectList对象将作为下拉框中的元素呈现,现在修改Create和Edit方法
using AspNetCore.BuiltInTagHelpers.Models;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.Rendering;using System.Diagnostics;using static AspNetCore.BuiltInTagHelpers.Models.Repository;namespace AspNetCore.BuiltInTagHelpers.Controllers{    public class HomeController : Controller    {        private readonly ILogger<HomeController> _logger;
        private readonly IRepository _repository;        public HomeController(IRepository repository,                              ILogger<HomeController> logger)        {            _repository = repository;            _logger = logger;        }        public ViewResult Edit()        {            ViewBag.Quantity = new SelectList(_repository.Products.Select(p => p.Quantity).Distinct());            return View("Create", _repository.Products.Last());        }        public IActionResult Index()        {            return View(_repository.Products);        }        public IActionResult Create()        {            ViewBag.Quantity = new SelectList(_repository.Products.Select(p => p.Quantity).Distinct());            return View();        }        [HttpPost]        [ValidateAntiForgeryToken]        public IActionResult Create(Product product)        {            _repository.AddProduct(product);            return RedirectToAction("Index");        }        public IActionResult Privacy()        {            return View();        }        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]        public IActionResult Error()        {            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });        }    }}

ViewBag.Quantity 属性设置为SelectList 对象,使用仓储Quantity值来填充该对象,经过这么一番修改,我们实现了动态选择

SelectList对象位于Microsoft.AspNetCore.Mvc.Rendering命名空间
运行应用程序,请求 https://localhost:7076/Home/Create 或者https://localhost:7076/Home/Edit 地址,这次你会发现可选元素被创建在ViewBag.Quantity数据源

5 Cache帮助标签

Cache帮助标签使用缓存内容用来帮助提高应用程序的性能,你可以将内容添加到缓存标签内部:
<cache> Some Content </cache>
进入_Layout.cshtml文件并且添加cache元素用来缓存当前时间:
    <div class="container">        <main role="main" class="pb-3">            <div class="bg-info text-warning">                <cache>                    Date Time: @DateTime.Now.ToString("HH:mm:ss")                </cache>            </div>            @RenderBody()        </main>    </div>
现在运行应用程序,你将会看到当前时间显示在页面顶部,如下图所示:

ASP.NET Core 内置的Tag Helpers

现在刷新页面并且我们将看到时间在小时,分钟,秒没有任何变化,这是因为时间被缓存了

下面缓存标签比较重要的属性:

名称

描述

expires-on

给缓存指定一个过期的绝对时间

expires-after

指定一个缓存过期的相对时间,TimeSpan值

expires-sliding

滑动时间,最后一次使用时间短, 它指定TimeSpan值中缓存过期的滑动时间

vary-by-query

一个查询字符串的键,被用来管理不同版本的缓存

vary-by-cookie

一个cookie名称用来管理不同版本的缓存

vary-by

指定一个key用来管理不同版本的缓存

5.1 expires-after

在_layout.cshtml文件中为缓存代码添加expires-after特性:

<cache expires-after="@TimeSpan.FromSeconds(20)">    Date Time: @DateTime.Now.ToString("HH:mm:ss")</cache>
这意味着20秒之后缓存将失效

5.2 expires-on

在_layout.cshtml文件中为缓存代码添加expires-on特性:

<cache expires-on="@DateTime.Parse("2050-01-01")">   Date Time: @DateTime.Now.ToString("HH:mm:ss")</cache>

时间被缓存直到2025年

5.3 expires-sliding
在_Layout.cshmtl视图为缓存代码添加expires-sliding特性
<cache expires-sliding="@TimeSpan.FromSeconds(20)" >    Date Time: @DateTime.Now.ToString("HH:mm:ss")</cache>

这个缓存将在距离上次使用的20s后过期

为了测试,每两秒加载一次页面,你将会看到相同的时间,等待20秒之后重新加载页面,这时你将看到一个新的时间,这时因为这个缓存在最后一次加载时间20s之后过期

5.4 vary-by

vary-by特性使用指定一个key来管理不同版本的缓存,在_Layout.cshtml视图修改cache元素:

<cache expires-sliding="@TimeSpan.FromSeconds(20)" vary-by="@ViewContext.RouteData.Values["action"]">    Date Time: @DateTime.Now.ToString("HH:mm:ss")</cache>

这意味着cache是基于当前action,我们有三个actions-Index,Edit和Create,因此3个版本的缓存将被创建(每个action方法都有一个),expire-sliding特性为每个版本设置滑动时间

6 Anchor帮助标签

Anchor帮助标签使用最广泛的内置帮助标签在ASP.NET Core框架,通过添加新的特性来加强anchor标签,这个特性基于应用程序路由构建anchor标签href

下面表格列举出重要的标签被使用通过anchor 帮助标签

名字

描述

asp-controller

指定url将被定位到的控制器 

asp-action

指定url将被定位到的action

asp-area

指定url将被定位到的area

asp-fragment

指定url段(在#字符后面)

asp-route

指定url将被定位到的路由名称

asp-route-*

指定额外的值针对url 例如asp-route-id="10"为路由段提供了一个id为10的值

进入Index视图并且创建一个链接定位到Create视图:
@{    ViewData["Title"] = "Home Page";}@model IEnumerable<Product><div class="container">    <div class="row mb-3">        <div class="col-sm-3">            <a class="btn btn-primary" asp-action="Create">新增</a>        </div>        <div class="col-sm-3"></div>        <div class="col-sm-3"></div>        <div class="col-sm-3"></div>    </div>    <div class="row mb-3">        <div class="col-sm">            <table class="table table-bordered align-middle">                <thead>                    <tr>                        <th>名称</th>                        <th>价格</th>                        <td>数量</td>                    </tr>                </thead>                <tbody>                    @foreach (var product in Model)                    {                        <tr>                            <td>@product.Name</td>                            <td>@product.Price</td>                            <td>@product.Quantity</td>                        </tr>                    }                </tbody>            </table>        </div>    </div></div>
这将生成如下链接:
<a class="btn btn-primary" href="/Home/Create">新增</a>
如下图所示

ASP.NET Core 内置的Tag Helpers

我们没有提供asp-controller帮助标签,因为使用了相同的控制器(Index和Create方法在相同的控制器),如果我们使用asp-route-* 特性在anchor帮助标签,如下代码:

<a class="btn btn-primary" asp-action="Create">新增</a>
生成的连接将包含路由id作为url的第三个段
<a class="btn btn-dark" href="/Home/Create/20">新增</a>

7 Javascript帮助标签

JavaScript 帮助标签管理包含和排除Javascript文件从视图文件,使用JavaScript帮助标签使用重要属性:

名称

描述

asp-src-include

指定在视图中包含的JavaScript文件

asp-src-exclude

指定在视图中排除的JavaScript文件

asp-append-version

指定是否把文件版本附加到连接地址,使用 Cache Busting

asp-fallback-src-include

如果使用的CDN出现一些问题时,指定备用的JavaScript文件

asp-fallback-src-exclude

如果使用CDN出现一些问题,指定排除的JavaScript文件

asp-fallback-test

JavaScript代码段,用来测试能否正确加载CDN

我们也可以使用一些列通配符来创建匹配的文件,这些通配符可以配合asp-src-include, asp-src-exclude, asp-fallback-src-include和asp-fallback-src-exclude帮助标签一块使用

常用模式

模式

例子

描述

?

JS/myscript?.js

匹配单个模式排除'/',左边的例子匹配 ‘JS/myscript1.js’, ‘JS/myscriptZ.js’, ‘JS/myscripts.js’等

*

JS/*.js

匹配任何期望的字符/除外,左边给与的例子匹配‘JS/myscript.js’, ‘JS/validate.js’, ‘JS/js123.js’等

**

JS/**.js

匹配任何数量的字符包括/,左边给与的例子匹配‘JS/Validate/myscript.js’,‘JS/jQuery/validate.js’, ‘JS/js123.js’等

这种模式非常有用,可以提供给我们一种基于客户自定义逻辑的方式创建匹配规则

7.1 asp-src-include例子

我们在_Layout.cshmtl中包含bootstrap.js文件,文件位于wwwroot/lib/bootstrap/dist/js/bootstrap.js

如下图所示

ASP.NET Core 内置的Tag Helpers

我们注意到5个JS文件在相同的文件夹
bootstrap.jsbootstrap.bundle.jsbootstrap.esm.jsbootstrap.js.mapbootstrap.min.js
现在进入_Layout.cshtml 文件并且添加script标签使用asp-src-include属性

ASP.NET Core 内置的Tag Helpers

运行应用程序并且检查生成的HTML源代码,你将会发现下面JS文件被添加到head节点

ASP.NET Core 内置的Tag Helpers

因为我们在asp-src-include属性中通过使用通配符来包含这些文件,我们可以使用下面代码用来只包含bootstrap.bundle.js文件

<script asp-src-include="lib/bootstrap/**/*bundle.js"></script>

7.2 asp-src-exclude例子

使用asp-src-exclude属性来排除view中的js文件,这次我们在layout文件中使用下面代码,这将从视图中移除所有的‘slim’, ‘min’ & ‘bundle’ 的JavaScript文件
<script asp-src-include="/lib/bootstrap/**/b*.js" asp-src-exclude="**.slim.*,**.min.*,**.bundle.*"></script>

运行应用程序检查生成的HTML源代码,你将发现只有bootstrap.js 和bootstrap.esm.js 

<script src="/lib/bootstrap/dist/js/bootstrap.js"></script><script src="/lib/bootstrap/dist/js/bootstrap.esm.js"></script>

7.3 Busting asp-fragment Attribute

浏览器每次只下载一次图片,css文件和JS文件,并且将他们缓存在本地,当用户访问浏览器,我们将从缓存中读取这些文件,这样避免从服务器端加载文件从而使页面加载更快

假设我们在服务器修改JavaScript文件,客户端仍然缓存老的JS文件,新的JavaScript文件没有加载,因此用户没有看到改变

如何处理这个问题,有一些方法告诉浏览器下载文件当js文件修改完,我们可以使用Cache Busting来解决这个问题

可以使用asp-fragment特性设置Cache Busting,它在JS文件的URL中添加一个checksum查询字符串作为版本号,如果修改JS文件,checksum将会改变,并且浏览器知道现在需要下载最新的js文件从服务器端来重新加载缓存

<script asp-src-include="/lib/bootstrap/**/b*.js"            asp-src-exclude="**.slim.*,**.min.*,**.bundle.*"            asp-append-version="true"></script>

生成的HTML包含如下

ASP.NET Core 内置的Tag Helpers

当浏览器向服务器发出下一个请求时,如果发现服务器上js文件的checksum已经更改

因此,它从服务器下载该文件的最新版本。然后缓存新下载的文件,并在“?”字符后添加新的checksome

8 CDN帮助标签

CDN包含了大量数据服务分部在全球,所有这些数据都保存在网站服务器的静态文件下,如图像、CSS、JS,当用户在浏览器上打开网站时,浏览器不是从网站的服务器请求静态文件,而是从离用户位置最近的数据服务器请求

这减少了网站的加载时间也节省了宽带,如果网站比较小建议使用CDN

asp-fallback-src-include,asp-fallback-src-exclude和asp-fallback-test 指定CDN, 假如有别的因素导致CDN加载jquery失败,在这种情况下我们的网站本地加载JQuery,更新layout文件,添加如下脚本
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"            asp-fallback-src-include="/lib/jquery/**/*.min.js"            asp-fallback-src-exclude="**.slim.**"            asp-fallback-test="window.jQuery"></script>

src指定从哪里加载jQuery,在CDN加载失败的情况下,asp-fallback-src-include和asp-fallback-src-exclude 使用本地备用的JQuery以及排除本地jQuery文件,asp-fallback-test帮助标签定义JavaScript代码(‘window.jQuery’) ,用来测试是否jQuery从CDN加载成功

如果代码执行失败,那么我们可以确信CDN已经无法使用,因此asp-fallback-src-include和asp-fallbback-src-exclude将会从网站服务器加载

9 CSS帮助标签

CSS帮助标签是用来管理包含和排除CSS文件从视图,下面显示了各种特性:

名字

描述

asp-href-include

指定视图中包含的CSS文件

asp-href-exclude

指定视图中排除的CSS文件

asp-append-version

指定css文件查询的版本号,用作Cache Busting

asp-fallback-href-include

指定包含的CSS文件,在CDN出现问题时

asp-fallback-href-exclude

指定排除的CSS文件,在CDN出现问题时

asp-fallback-href-test-class

指定一个CSS文件类测试CDN是否可用

asp-fallback-href-test-property

使用CSS类的属性测试CDN

asp-fallback-href-test-value

指定一个测试值,用来确定CDN是否可用

9.1 asp-href-include例子

Bootstrap文件位于wwwroot/lib/bootstrap/dist/css目录,如下图所示:

ASP.NET Core 内置的Tag Helpers

在head节点下添加如下特性,包含所有的bootstrap的css文件
<link rel="stylesheet" asp-href-include="/lib/bootstrap/**/*min.css" />

我们针对CSS文件使用 (**和*) 匹配模式

** – 匹配任何数量的字符包含'/'

*  –  匹配任何数量的字符,排除'/'

这将添加文件名称已.css结尾,因此将包含下列文件

ASP.NET Core 内置的Tag Helpers

添加了大量的css文件像grid,reboot,rtl ,我们可以使用asp-href-exclude来移除它

9.2 asp-href-exclude例子

asp-href-exclude属性用来从视图中排除CSS文件,现在使用它来移除reboot和grid CSS文件. 如下代码所示:

ASP.NET Core 内置的Tag Helpers

现在,值包含1个CSS文件:

ASP.NET Core 内置的Tag Helpers

10 从CDN加载CSS

我们可以从CDN中加载CSS文件,在layout页面添加下面代码:

ASP.NET Core 内置的Tag Helpers

href属性指定CDN的地址,asp-fallback-href-include 和 asp-fallback-href-exclude 表示如果CDN不可用,选择本地站点加载CSS文件
下面3个帮助标签使用测试是否bootstrap文件能够从CDN下载成功:
1. asp-fallback-test-class
2. asp-fallback-test-property
3. asp-fallback-test-value

工作方式是将一个元素添加到的文档中, 这个元素在标记asp-fallback-test-class中定义

如下图所示

ASP.NET Core 内置的Tag Helpers

asp-fallback-test-property指定CSS属性,asp-fallback-test-value设置属性的值,这些属性用来判断CDN是否正确加载,如果失败fallback的文件将被加载
源代码地址
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.TagHelpers/AspNetCore.BuiltInTagHelpers