Net Core教程

《 Pro ASP.NET Core 6 》--- 读书随记(8)

本文主要是介绍《 Pro ASP.NET Core 6 》--- 读书随记(8),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Part 3

CHAPTER 25

内容来自书籍:

Pro ASP.NET Core 6
Develop Cloud-Ready Web Applications Using MVC, Blazor, and Razor Pages (Ninth Edition)

Author: Adam Freeman
需要该电子书的小伙伴,可以留下邮箱,有空看到就会发送的

Using Tag Helpers

Tag helpers 是在视图或页面中转换 HTML 元素的 C # 类。Tag helpers 的常见用途包括使用应用程序的路由配置为表单生成 URL,确保特定类型的元素样式一致,以及用常用的内容片段替换自定义简写元素。

Creating a Tag Helper

我通过创建和应用一个 Tag Helper 来设置 tr 元素的 Bootstrap CSS 类

<tr tr-color="primary">
    <th colspan="2">Product Summary</th>
</tr>

转换为:

<tr class="bg-primary text-white text-center">
    <th colspan="2">Product Summary</th>
</tr>

Defining the Tag Helper Class

Tag helpers 可以在项目的任何地方定义,但是将它们放在一起会有所帮助,因为它们需要注册才能使用

namespace WebApp.TagHelpers {
    public class TrTagHelper: TagHelper {
        public string BgColor { get; set; } = "dark";
        public string TextColor { get; set; } = "white";
        public override void Process(TagHelperContext context,
                TagHelperOutput output) {
            output.Attributes.SetAttribute("class",
                $"bg-{BgColor} text-center text-{TextColor}");
        }
    }
}
Receiving Context Data

Tag helpers 通过 TagHelperContext 类的实例接收有关它们正在转换的元素的信息

虽然您可以通过 AllAttributesdictionary 访问元素属性的详细信息,但是更方便的方法是定义一个名称与您感兴趣的属性相对应的属性

属性的名称会自动从默认的 HTML 样式 bg-color 转换为 C # 样式 BgColor。您可以使用任何属性前缀,除了 asp- (Microsoft 使用的)和 data-(为发送到客户端的自定义属性保留的)。示例 Tag Helper 将使用 bg-color 和 text-color 属性进行配置,这些属性将为 BgColor 和 textColor 属性提供值,并用于在 Process 方法中配置 tr 元素,如下所示:

Producing Output

Process 方法通过配置作为参数接收的 TagHelperOutput 对象来转换元素。TagHelperOut 对象首先描述出现在视图中的 HTML 元素,然后通过下图描述的属性和方法进行修改。

Registering Tag Helpers

Tag Helper 类在使用之前必须使用@addTagHelper 指令进行注册

如果想要在所有的地方都可以使用Tag Hepler,可以在_ViewImports.cshtml 全局导入

@addTagHelper *, WebApp

第一个参数的含义是指定tag Helper 的类名并且支持通配符,第二部分是指定这个类是在哪个程序集(assembly)定义的

Using a Tag Helper

<tr bg-color="info" text-color="white">

清单25-7中应用属性的 tr 元素已经被转换,但这不是图中显示的唯一更改。默认情况下,Tag helpers 应用于特定类型的所有元素,这意味着视图中的所有 tr 元素已经使用在 Tag Helper 类中定义的默认值进行了转换,因为没有定义任何属性。(有些表行不显示文本的原因是 Bootstrap table-striped类,它将不同的样式应用于替换行。)

事实上,问题更为严重,因为视图导入文件中的@addTagHelper 指令意味着示例 Tag Helper 应用于控制器和 Razor 页面呈现的任何视图中使用的所有 tr 元素。

Narrowing the Scope of a Tag Helper

可以使用 HtmlTargetElement 元素控制由 Tag Helper 转换的元素范围

namespace WebApp.TagHelpers {
    [HtmlTargetElement("tr", Attributes = "bg-color,text-color", ParentTag = "thead")]
    public class TrTagHelper : TagHelper {
        public string BgColor { get; set; } = "dark";
        public string TextColor { get; set; } = "white";
        public override void Process(TagHelperContext context,
                TagHelperOutput output) {
            output.Attributes.SetAttribute("class",
                $"bg-{BgColor} text-center text-{TextColor}");
        }
    }
}

Attritribute 属性支持 CSS 属性选择器语法,所以[ bg-color ]匹配具有 bg-color 属性的元素,[ bg-color = primary ]匹配具有 bg-color 属性的元素,该属性的值是primary,并且[ bg-color^=p ]匹配具有 bg-color 属性的元素,该属性的值以 p 开头。上文中 Tag Helper 的属性与 tr 元素匹配,它们同时具有 bg-color 和 text-color 属性,这两个属性都是 head 元素的子元素。

Widening the Scope of a Tag Helper

HtmlTargetElement 属性还可以用来扩展 Tag Helper 的范围,使其匹配范围更广的元素。这是通过将属性的第一个参数设置为星号(* 字符)来完成的,该星号匹配任何元素

[HtmlTargetElement("*", Attributes = "bg-color,text-color")]

如果担心匹配的范围过大,这个属性可以叠加使用

[HtmlTargetElement("tr", Attributes = "bg-color,text-color")]
[HtmlTargetElement("td", Attributes = "bg-color")]

如果想对同一个元素应用不同的tag,可以设置tag之间的顺序,Order属性是继承自TagHelper,用来决定tag的执行顺序

Advanced Tag Helper Features

Creating Shorthand Elements

Tag helpers 并不局限于转换标准的 HTML 元素,亦可以用常用的内容取代自订元素。这对于使视图更加简洁和使其意图更加明显是一个有用的特性

<tablehead bg-color="dark">Product Summary</tablehead>

这个自定义标签是不会被浏览器识别的,所以我们会创建一个Tag Helper来生成具体的标准的HTML元素

namespace WebApp.TagHelpers {

    [HtmlTargetElement("tablehead")]
    public class TableHeadTagHelper: TagHelper {

    public string BgColor { get; set; } = "light";
        public override async Task ProcessAsync(TagHelperContext context,
                TagHelperOutput output) {
            output.TagName = "thead";
            output.TagMode = TagMode.StartTagAndEndTag;
            output.Attributes.SetAttribute("class",
                $"bg-{BgColor} text-white text-center");
            string content = (await output.GetChildContentAsync()).GetContent();
            output.Content
                .SetHtmlContent($"<tr><th colspan=\"2\">{content}</th></tr>");
        }
    }
}

然后上面的标签会变成:

<thead class="bg-dark text-white text-center">
    <tr>
        <th colspan="2">Product Summary</th>
    </tr>
</thead>

Creating Elements Programmatically

我们使用标准的c#中的string格式来创建我们需要的内容,但是这不安全,因为很可能会打错字。

一个更安全有效的方法是使用 TagBuilder 类,允许以更结构化的方式创建元素

            TagBuilder header = new TagBuilder("th");
            header.Attributes["colspan"] = "2";
            header.InnerHtml.Append(content);
            TagBuilder row = new TagBuilder("tr");
            row.InnerHtml.AppendHtml(header);
            output.Content.SetHtmlContent(row);

Prepending and Appending Content and Elements

TagHelperOutput 类提供了四个属性,可以很容易地将新内容注入到视图中,使其包围元素或元素的内容

Inserting Content Around the Output Element
namespace WebApp.TagHelpers {
    [HtmlTargetElement("*", Attributes = "[wrap=true]")]
    public class ContentWrapperTagHelper: TagHelper {
        public override void Process(TagHelperContext context,
                TagHelperOutput output) {
            TagBuilder elem = new TagBuilder("div");
            elem.Attributes["class"] = "bg-primary text-white p-2 m-2";
            elem.InnerHtml.AppendHtml("Wrapper");
            output.PreElement.AppendHtml(elem);
            output.PostElement.AppendHtml(elem);
        }
    }
}

使用<div class="m-2" wrap="true">Inner Content</div>

效果:

Inserting Content Inside the Output Element
namespace WebApp.TagHelpers {
    [HtmlTargetElement("*", Attributes = "[highlight=true]")]
    public class HighlightTagHelper: TagHelper {
        public override void Process(TagHelperContext context,
                TagHelperOutput output) {
            output.PreContent.SetHtmlContent("<b><i>");
            output.PostContent.SetHtmlContent("</i></b>");
        }
    }
}

使用<tr><th>Name</th><td highlight="true">@Model?.Name</td></tr>

Getting View Context Data

[ViewContext]
[HtmlAttributeNotBound]
public ViewContext Context { get; set; } = new();

ViewContext 属性表示,当创建Tag Helper对象类的新实例时,该属性的值应该被赋予一个 ViewContext,该实例提供正在呈现的视图的详细信息,包括路由数据

如果在 div 元素上定义了匹配的属性,则 HtmlAttributeNotBound 属性阻止将值分配给此属性。这是一个很好的实践,特别是当你正在为其他开发者写 Tag helpers 的时候。

Suppressing the Output Element

Tag helpers 可以通过调用作为 Process 方法参数接收的 TagHelperOutput 对象上的 SuppressOut 方法来防止元素包含在 HTML 响应中

<div show-when-gt="500" for="Price">
    <h5 class="bg-danger text-white text-center p-2">
        Warning: Expensive Item
    </h5>
</div>
namespace WebApp.TagHelpers {
    [HtmlTargetElement("div", Attributes = "show-when-gt, for")]
    public class SelectiveTagHelper : TagHelper {
        public decimal ShowWhenGt { get; set; }
        public ModelExpression? For { get; set; }
        public override void Process(TagHelperContext context,
                TagHelperOutput output) {
            if (For?.Model.GetType() == typeof(decimal)
                    && (decimal)For.Model <= ShowWhenGt) {
                output.SuppressOutput();
            }
        }
    }
}

Tag Helper 使用模型表达式访问属性,并调用 SuppressOutput 方法,除非超过阈值

CHAPTER 26

Using the Built-in Tag Helpers

Enabling the Built-in Tag Helpers

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Transforming Anchor Elements

A 元素是在应用程序周围导航并向应用程序发送 GET 请求的基本工具。AnchorTagHelper 类用于转换元素的 href 属性,这样它们可以针对使用路由系统生成的 URL,这意味着不需要硬编码的 URL,路由配置的变化将自动反映在应用程序的锚元素中

AnchorTagHelper 简单且可预测,使得在使用应用程序路由配置的元素中生成 URL 变得非常容易。

<td>
    <a asp-action="index" asp-controller="home" asp-route-id="@Model?.ProductId"
          class="btn btn-sm btn-info text-white">
            Select
    </a>
</td>

Asp-action 和 asp-controller 属性指定操作方法的名称和定义它的控制器。段变量的值是使用 asp-path-[ name ]属性定义的,例如 asp-path-id 属性为 id 段变量提供一个值,用于为 asp-action 属性选择的 action 方法提供一个参数。

上面最终返回的html是

<a class="btn btn-sm btn-info text-white" href="/Home/index/3">Select</a>

Using Anchor Elements for Razor Pages

Asp-Page 属性用于指定一个 Razor 页面作为锚元素 href 属性的目标。页面的路径以/字符为前缀,@page 指令定义的路由段的值使用 asp-path-[ name ]属性定义。

<a asp-page="/suppliers/list" class="btn btn-secondary">Suppliers</a>

Using the JavaScript and CSS Tag Helpers

ASP.NET Core 提供了一些 Tag helpers,用于通过 script 和 link 元素管理 JavaScript 文件和 CSS 样式表。这些 Tag helpers 强大而灵活,但需要密切关注,以避免产生意想不到的结果。

Managing JavaScript Files

ScriptTagHelper 类是 script 元素的内置 Tag Helper,用于管理/n包含 JavaScript 文件

Selecting JavaScript Files

Asp-src-include 属性用于在使用 globbing 模式的视图中包含 JavaScript 文件。Globbing 模式支持一组用于匹配文件的通配符

Globbing 是确保视图包含应用程序需要的 JavaScript 文件的一种有用方法,即使文件的确切路径发生了变化,这种情况通常发生在文件名中包含版本号或包添加额外文件时。

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

Managing CSS Stylesheets

Selecting Stylesheets

LinkTagHelper 与 ScriptTagHelper 共享许多特性,包括支持 globbing 模式来选择或排除 CSS 文件,这样就不必单独指定它们。能够准确地选择 CSS 文件和 JavaScript 文件一样重要,因为样式表可以有常规和缩小的版本,并且支持源地图

<link asp-href-include="/lib/bootstrap/css/*.min.css"
          asp-href-exclude="**/*-reboot*,**/*-grid*,**/*-utilities*, **/*.rtl.*"
          rel="stylesheet" />

Using the Data Cache

CacheTagHelper 类允许缓存内容片段,以加速视图或页面的呈现。要缓存的内容使用 cache 元素表示

<h6 class="bg-primary text-white m-2 p-2">
            Uncached timestamp: @DateTime.Now.ToLongTimeString()
</h6>
<cache>
    <h6 class="bg-primary text-white m-2 p-2">
        Cached timestamp: @DateTime.Now.ToLongTimeString()
    </h6>
</cache>

CacheTagHelper 类使用的缓存是基于内存的,这意味着它的容量受到可用 RAM 的限制,并且每个应用服务器维护一个单独的缓存。当可用容量不足时,内容将从缓存中弹出,当应用程序停止或重新启动时,整个内容将丢失。

Setting Cache Expiry

<cache expires-after="@TimeSpan.FromSeconds(15)">

Setting a Fixed Expiry Point

<cache expires-on="@DateTime.Parse("2100-01-01")">

我已经指定该数据应该缓存到2100年。这不是一个有用的缓存策略,因为应用程序很可能会在下个世纪开始之前重新启动,但它确实说明了如何在将来指定一个固定点,而不是表示相对于内容被缓存的时刻的到期点。

Setting a Last-Used Expiry Period

<cache expires-sliding="@TimeSpan.FromSeconds(10)">

expires-sliding属性用于指定内容没有从缓存中检索到的过期时间

如果在10秒内重新加载页面,将使用缓存的内容。如果等待重新加载页超过10秒,那么缓存的内容将被丢弃,视图组件将用于生成新内容,进程将重新开始。

Using Cache Variations

<cache expires-sliding="@TimeSpan.FromSeconds(10)" vary-by-route="action">

默认情况下,所有请求都接收相同的缓存内容。CacheTagHelper 类可以维护缓存内容的不同版本,并使用它们来满足不同类型的 HTTP 请求,这些请求是使用名称开头为 vary-by 的属性之一指定的

您将看到,每个窗口都会收到自己的缓存内容和自己的到期日期,因为每个请求都会产生不同的action路由值。

Using the Hosting Environment Tag Helper

Environment TagHelper 类应用于自定义环境元素,并根据宿主环境确定在发送到浏览器的 HTML 中是否包含内容区域

        <environment names="development">
            <h2 class="bg-info text-white m-2 p-2">This is Development</h2>
        </environment>
        <environment names="production">
            <h2 class="bg-danger text-white m-2 p-2">This is Production</h2>
        </environment>

CHAPTER 27

Using the Forms Tag Helpers

这章节描述了用于创建 HTML 表单的内置 Tag helpers。这些 Tag helpers 确保表单提交给正确的操作或页面处理程序方法,并且元素准确地表示特定的模型属性

Understanding the Form Handling Pattern

首先,浏览器发送一个 HTTP GET 请求,这将导致包含表单的 HTML 响应,从而使用户可以向应用程序提供数据。用户单击一个按钮,该按钮通过 HTTP POST 请求提交表单数据,这允许应用程序接收和处理用户的数据。处理完数据后,将发送响应,该响应将浏览器重定向到确认用户操作的 URL。

这就是所谓的 POST/Redirect/Get 模式,重定向非常重要,因为它意味着用户可以单击浏览器的重载按钮而无需发送另一个 POST 请求,这可能会导致无意中重复某个操作。

Creating a Controller to Handle Forms

        public async Task<IActionResult> Index(long id = 1) {
            return View("Form", await context.Products.FindAsync(id));
        }

        [HttpPost]
        public IActionResult SubmitForm() {
            foreach (string key in Request.Form.Keys
                    .Where(k => !k.StartsWith("_"))) {
                TempData[key] = string.Join(", ", Request.Form[key]);
            }
            return RedirectToAction(nameof(Results));
        }

        public IActionResult Results() {
            return View();
        }

Using Tag Helpers to Improve HTML Forms

上一节的例子展示了处理 HTML 表单的基本机制,但是 ASP.NET Core 包含了转换表单元素的 Tag helpers

Working with Form Elements

FormTagHelper 类是表单元素的内置 Tag Helper,用于管理 HTML 表单的配置,以便它们针对正确的操作或页面处理程序,而不需要对 URL 进行硬编码

<form asp-action="submitform" method="post">

<form asp-page="FormHandler" method="post">

Working with input Elements

Input 元素是 HTML 表单的骨干,为用户提供应用程序的主要非结构化数据。InputTagHelper 类用于转换输入元素,以便它们反映用于收集的视图模型属性的数据类型和格式

<input class="form-control" asp-for="Name" />

Applying Formatting via the Model Class

[DisplayFormat(DataFormatString = "{0:c2}", ApplyFormatInEditMode = true)]
public decimal Price { get; set; }

Working with label Elements

<label asp-for="ProductId"></label>

Working with Select and Option Elements

<select class="form-control" asp-for="CategoryId">
    <option value="1">Watersports</option>
    <option value="2">Soccer</option>
    <option value="3">Chess</option>
</select>
<select class="form-control" asp-for="CategoryId" asp-items="@ViewBag.Categories">
</select>

Working with Text Areas

Textarea 元素用于从用户那里获取大量的文本,通常用于非结构化数据,例如注释或观察。

<textarea class="form-control" asp-for="Supplier.Name"></textarea>

Using the Anti-forgery Feature

结果中显示的 _ RequestVerificationToken 表单值是 FormTagHelper 应用于防止跨站请求伪造的安全特性。跨站请求伪造(CSRF)利用用户请求通常被认证的方式来开发 web 应用程序。大多数 Web 应用程序ーー包括那些使用 ASP.NET Core 创建的应用程序ーー都使用 Cookie 来识别哪些请求与某个特定会话相关联,而用户标识通常与这些请求相关联。

CSRF ーー也称为 XSRF ーー依赖于用户在使用 Web 应用程序后访问恶意网站,而且没有明确结束会话。应用程序仍将用户的会话视为处于活动状态,并且浏览器存储的 Cookie 尚未过期。恶意站点包含 JavaScript 代码,该代码向应用程序发送表单请求,以便在未经用户同意的情况下执行操作ーー操作的确切性质将取决于被攻击的应用程序。由于 JavaScript 代码是由用户的浏览器执行的,因此对应用程序的请求包括会话 cookie,应用程序在用户不知情或未经用户同意的情况下执行操作。

如果表单元素不包含 action 属性ーー因为它是由具有 asp-controller、 asp-action 和 asp-page 属性的路由系统生成的ーー那么 FormTagHelper 类将自动启用反 CSRF 特性,即将安全令牌作为 cookie 添加到响应中。将包含相同安全令牌的隐藏输入元素添加到 HTML 表单中

Enabling the Anti-forgery Feature in a Controller

默认情况下,控制器接受 POST 请求,即使它们不包含所需的安全令牌

[AutoValidateAntiforgeryToken]
public class FormController : Controller

Enabling the Anti-forgery Feature in a Razor Page

防伪功能在 Razor 中默认启用

Using Anti-forgery Tokens with JavaScript Clients

默认情况下,防伪特性依赖于 ASP.NET Core 应用程序能够在 HTML 表单中包含一个元素,当表单提交时,浏览器会发回该元素。这对 JavaScript 客户端不起作用,因为 ASP.NET Core 应用程序提供数据而不是 HTML,所以没有办法插入隐藏元素并在将来的请求中接收它。

对于 Web 服务,防伪令牌可以作为 JavaScript 可读 cookie 发送,JavaScript 客户机代码读取该 cookie 并将其作为 POST 请求中的头文件包含在内

builder.Services.Configure<AntiforgeryOptions>(opts => {
    opts.HeaderName = "X-XSRF-TOKEN";
});

var app = builder.Build();

IAntiforgery antiforgery = app.Services.GetRequiredService<IAntiforgery>();
app.Use(async (context, next) => {
    if (!context.Request.Path.StartsWithSegments("/api")) {
        string? token = antiforgery.GetAndStoreTokens(context).RequestToken;
        if (token != null) {
            context.Response.Cookies.Append("XSRF-TOKEN",
               token,
               new CookieOptions { HttpOnly = false });
        }
    }
    await next();
});

需要一个自定义中间件组件来设置 cookie,在这个示例中,cookie 被命名为 XSRF-TOKEN。Cookie 的值是通过 IAntiForgery 服务获得的,必须将 HttpOnly 选项设置为 false,以便浏览器允许 JavaScript 代码读取 Cookie。

这篇关于《 Pro ASP.NET Core 6 》--- 读书随记(8)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!