{"id":607,"date":"2025-04-05T11:41:54","date_gmt":"2025-04-05T03:41:54","guid":{"rendered":"https:\/\/www.hyy.net\/?p=607"},"modified":"2025-04-05T11:41:54","modified_gmt":"2025-04-05T03:41:54","slug":"asp-net-core-in-action-18-building-forms-with-tag-helpers","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=607","title":{"rendered":"ASP.NET Core in Action 18 Building forms with Tag Helpers"},"content":{"rendered":"<p>18 Building forms with Tag Helpers<br \/>\n18 \u4f7f\u7528\u6807\u8bb0\u8f85\u52a9\u5bf9\u8c61\u6784\u5efa\u8868\u5355<\/p>\n<p>This chapter covers<br \/>\n\u672c\u7ae0\u6db5\u76d6<br \/>\n\u2022  Building forms easily with Tag Helpers<br \/>\n\u4f7f\u7528\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u8f7b\u677e\u6784\u5efa\u8868\u5355<br \/>\n\u2022  Generating URLs with the Anchor Tag Helper<br \/>\n\u4f7f\u7528\u951a\u70b9\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u751f\u6210 URL<br \/>\n\u2022  Using Tag Helpers to add functionality to Razor<br \/>\n\u4f7f\u7528\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u5411 Razor \u6dfb\u52a0\u529f\u80fd<\/p>\n<p>In chapter 17 you learned about Razor templates and how to use them to generate the views for your application. By mixing HTML and C#, you can create dynamic applications that can display different data based on the request, the logged-in user, or any other data you can access.<br \/>\n\u5728\u7b2c 17 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86 Razor \u6a21\u677f\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u5b83\u4eec\u4e3a\u5e94\u7528\u7a0b\u5e8f\u751f\u6210\u89c6\u56fe\u3002\u901a\u8fc7\u6df7\u5408\u4f7f\u7528 HTML \u548c C#\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u52a8\u6001\u5e94\u7528\u7a0b\u5e8f\uff0c\u8fd9\u4e9b\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u6839\u636e\u8bf7\u6c42\u3001\u767b\u5f55\u7528\u6237\u6216\u60a8\u53ef\u4ee5\u8bbf\u95ee\u7684\u4efb\u4f55\u5176\u4ed6\u6570\u636e\u663e\u793a\u4e0d\u540c\u7684\u6570\u636e\u3002<\/p>\n<p>Displaying dynamic data is an important aspect of many web applications, but it\u2019s typically only half of the story. As well as needing to displaying data to the user, you often need the user to be able to submit data back to your application. You can use data to customize the view or to update the application model by saving it to a database, for example. For traditional web applications, this data is usually submitted using an HTML form.<br \/>\n\u663e\u793a\u52a8\u6001\u6570\u636e\u662f\u8bb8\u591a Web \u5e94\u7528\u7a0b\u5e8f\u7684\u4e00\u4e2a\u91cd\u8981\u65b9\u9762\uff0c\u4f46\u901a\u5e38\u53ea\u662f\u5176\u4e2d\u7684\u4e00\u534a\u3002\u9664\u4e86\u9700\u8981\u5411\u7528\u6237\u663e\u793a\u6570\u636e\u5916\uff0c\u60a8\u8fd8\u7ecf\u5e38\u9700\u8981\u7528\u6237\u80fd\u591f\u5c06\u6570\u636e\u63d0\u4ea4\u56de\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 data \u6765\u81ea\u5b9a\u4e49\u89c6\u56fe\u6216\u901a\u8fc7\u5c06\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u6765\u66f4\u65b0\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u3002\u5bf9\u4e8e\u4f20\u7edf\u7684 Web \u5e94\u7528\u7a0b\u5e8f\uff0c\u6b64\u6570\u636e\u901a\u5e38\u4f7f\u7528 HTML \u8868\u5355\u63d0\u4ea4\u3002<\/p>\n<p>In chapter 16 you learned about model binding, which is how you accept the data sent by a user in a request and convert it to C# objects that you can use in your Razor Pages. You also learned about validation and how important it is to validate the data sent in a request. You used DataAnnotations attributes to define the rules associated with your models, as well as associated metadata like the display name for a property.<br \/>\n\u5728\u7b2c 16 \u7ae0\u4e2d\uff0c\u4f60\u4e86\u89e3\u4e86\u6a21\u578b\u7ed1\u5b9a\uff0c\u5373\u5982\u4f55\u63a5\u53d7\u7528\u6237\u5728\u8bf7\u6c42\u4e2d\u53d1\u9001\u7684\u6570\u636e\uff0c\u5e76\u5c06\u5176\u8f6c\u6362\u4e3a\u53ef\u5728 Razor Pages \u4e2d\u4f7f\u7528\u7684 C# \u5bf9\u8c61\u3002\u60a8\u8fd8\u4e86\u89e3\u4e86\u9a8c\u8bc1\u4ee5\u53ca\u9a8c\u8bc1\u8bf7\u6c42\u4e2d\u53d1\u9001\u7684\u6570\u636e\u7684\u91cd\u8981\u6027\u3002\u60a8\u4f7f\u7528 DataAnnotations \u5c5e\u6027\u6765\u5b9a\u4e49\u4e0e\u6a21\u578b\u5173\u8054\u7684\u89c4\u5219\uff0c\u4ee5\u53ca\u5173\u8054\u7684\u5143\u6570\u636e\uff0c\u4f8b\u5982\u5c5e\u6027\u7684\u663e\u793a\u540d\u79f0\u3002<\/p>\n<p>The final aspect we haven\u2019t yet looked at is how to build the HTML forms that users use to send this data in a request. Forms are one of the key ways users will interact with your application in the browser, so it\u2019s important they\u2019re both correctly defined for your application and user-friendly. ASP.NET Core provides a feature to achieve this, called Tag Helpers.<br \/>\n\u6211\u4eec\u8fd8\u6ca1\u6709\u7814\u7a76\u7684\u6700\u540e\u4e00\u4e2a\u65b9\u9762\u662f\u5982\u4f55\u6784\u5efa\u7528\u6237\u7528\u6765\u5728\u8bf7\u6c42\u4e2d\u53d1\u9001\u8fd9\u4e9b\u6570\u636e\u7684 HTML \u8868\u5355\u3002\u8868\u5355\u662f\u7528\u6237\u5728\u6d4f\u89c8\u5668\u4e2d\u4e0e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ea4\u4e92\u7684\u5173\u952e\u65b9\u5f0f\u4e4b\u4e00\uff0c\u56e0\u6b64\u5b83\u4eec\u5fc5\u987b\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6b63\u786e\u5b9a\u4e49\u5e76\u4e14\u5bf9\u7528\u6237\u53cb\u597d\u3002ASP.NET Core \u63d0\u4f9b\u4e86\u4e00\u9879\u529f\u80fd\u6765\u5b9e\u73b0\u6b64\u76ee\u7684\uff0c\u79f0\u4e3a Tag Helpers\u3002<\/p>\n<p>Tag Helpers are additions to Razor syntax that you use to customize the HTML generated in your templates. Tag Helpers can be added to an otherwise-standard HTML element, such as an <code>&lt;input&gt;<\/code>, to customize its attributes based on your C# model, saving you from having to write boilerplate code. Tag Helpers can also be standalone elements and can be used to generate completely customized HTML.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u662f Razor \u8bed\u6cd5\u7684\u65b0\u589e\u529f\u80fd\uff0c\u7528\u4e8e\u81ea\u5b9a\u4e49\u6a21\u677f\u4e2d\u751f\u6210\u7684 HTML\u3002\u53ef\u4ee5\u5c06\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u6dfb\u52a0\u5230\u5176\u4ed6\u6807\u51c6\u7684 HTML \u5143\u7d20\uff08\u5982 <code>&lt;input&gt;<\/code>\uff09\u4ee5\u57fa\u4e8e C# \u6a21\u578b\u81ea\u5b9a\u4e49\u5176\u5c5e\u6027\uff0c\u4ece\u800c\u4f7f\u60a8\u4e0d\u5fc5\u7f16\u5199\u6837\u677f\u4ee3\u7801\u3002\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4e5f\u53ef\u4ee5\u662f\u72ec\u7acb\u5143\u7d20\uff0c\u53ef\u7528\u4e8e\u751f\u6210\u5b8c\u5168\u81ea\u5b9a\u4e49\u7684 HTML\u3002<\/p>\n<p><b>NOTE<\/b> Remember that Razor, and therefore Tag Helpers, are for server-side HTML rendering. You can\u2019t use Tag Helpers directly in frontend frameworks like Angular and React.<br \/>\n\u6ce8\u610f:\u8bf7\u8bb0\u4f4f\uff0cRazor \u4ee5\u53ca\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u7528\u4e8e\u670d\u52a1\u5668\u7aef HTML \u5448\u73b0\u3002\u4f60\u4e0d\u80fd\u76f4\u63a5\u5728 Angular \u548c React \u7b49\u524d\u7aef\u6846\u67b6\u4e2d\u4f7f\u7528 Tag Helpers\u3002<\/p>\n<p>If you\u2019ve used legacy (.NET Framework) ASP.NET before, Tag Helpers may sound reminiscent of HTML Helpers, which could also be used to generate HTML based on your C# classes. Tag Helpers are the logical successor to HTML Helpers, as they provide a more streamlined syntax than the previous, C#-focused helpers. HTML Helpers are still available in ASP.NET Core, so if you\u2019re converting some old templates to ASP.NET Core, you can still use them. But if you\u2019re writing new Razor templates, I recommend using only Tag Helpers, as they should cover everything you need. I don\u2019t cover HTML Helpers in this book.<br \/>\n\u5982\u679c\u60a8\u4ee5\u524d\u4f7f\u7528\u8fc7\u65e7\u7248 \uff08.NET Framework\uff09 ASP.NET\uff0c\u5219\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u542c\u8d77\u6765\u53ef\u80fd\u4f1a\u8ba9\u4eba\u60f3\u8d77 HTML \u5e2e\u52a9\u7a0b\u5e8f\uff0c\u540e\u8005\u4e5f\u53ef\u7528\u4e8e\u57fa\u4e8e C# \u7c7b\u751f\u6210 HTML\u3002\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u662f HTML \u5e2e\u52a9\u7a0b\u5e8f\u7684\u903b\u8f91\u7ee7\u627f\u7a0b\u5e8f\uff0c\u56e0\u4e3a\u5b83\u4eec\u63d0\u4f9b\u7684\u8bed\u6cd5\u6bd4\u4ee5\u524d\u4ee5 C# \u4e3a\u4e2d\u5fc3\u7684\u5e2e\u52a9\u7a0b\u5e8f\u66f4\u7b80\u5316\u3002HTML \u5e2e\u52a9\u7a0b\u5e8f\u5728 ASP.NET Core \u4e2d\u4ecd\u7136\u53ef\u7528\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u8981\u5c06\u4e00\u4e9b\u65e7\u6a21\u677f\u8f6c\u6362\u4e3a ASP.NET Core\uff0c\u60a8\u4ecd\u7136\u53ef\u4ee5\u4f7f\u7528\u5b83\u4eec\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u6b63\u5728\u7f16\u5199\u65b0\u7684 Razor \u6a21\u677f\uff0c\u6211\u5efa\u8bae\u4ec5\u4f7f\u7528 Tag Helpers\uff0c\u56e0\u4e3a\u5b83\u4eec\u5e94\u8be5\u6db5\u76d6\u60a8\u9700\u8981\u7684\u6240\u6709\u5185\u5bb9\u3002\u5728\u672c\u4e66\u4e2d\uff0c\u6211\u4e0d\u6d89\u53ca HTML Helpers\u3002<\/p>\n<p>In this chapter you\u2019ll primarily learn how to use Tag Helpers when building forms. They simplify the process of generating correct element names and IDs so that model binding can occur seamlessly when the form is sent back to your application. To put them into context, you\u2019re going to carry on building the currency converter application that you\u2019ve seen in previous chapters. You\u2019ll add the ability to submit currency exchange requests to it, validate the data, and redisplay errors on the form using Tag Helpers to do the legwork for you, as shown in figure 18.1.<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e3b\u8981\u5b66\u4e60\u5982\u4f55\u5728\u6784\u5efa\u8868\u5355\u65f6\u4f7f\u7528 Tag Helpers\u3002\u5b83\u4eec\u7b80\u5316\u4e86\u751f\u6210\u6b63\u786e\u5143\u7d20\u540d\u79f0\u548c ID \u7684\u8fc7\u7a0b\uff0c\u4ee5\u4fbf\u5728\u5c06\u8868\u5355\u53d1\u9001\u56de\u5e94\u7528\u7a0b\u5e8f\u65f6\u53ef\u4ee5\u65e0\u7f1d\u5730\u8fdb\u884c\u6a21\u578b\u7ed1\u5b9a\u3002\u4e3a\u4e86\u5c06\u5b83\u4eec\u653e\u5728\u4e0a\u4e0b\u6587\u4e2d\uff0c\u60a8\u5c06\u7ee7\u7eed\u6784\u5efa\u60a8\u5728\u524d\u51e0\u7ae0\u4e2d\u770b\u5230\u7684\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u3002\u60a8\u5c06\u6dfb\u52a0\u5411\u5176\u63d0\u4ea4\u8d27\u5e01\u5151\u6362\u8bf7\u6c42\u3001\u9a8c\u8bc1\u6570\u636e\u4ee5\u53ca\u4f7f\u7528 Tag Helpers \u5728\u8868\u5355\u4e0a\u91cd\u65b0\u663e\u793a\u9519\u8bef\u7684\u529f\u80fd\uff0c\u4ee5\u4e3a\u60a8\u5b8c\u6210\u8dd1\u817f\u5de5\u4f5c\uff0c\u5982\u56fe 18.1 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1801.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.1 The currency converter application forms, built using Tag Helpers. The labels, drop-down lists, input elements, and validation messages are all generated using Tag Helpers.<br \/>\n\u56fe 18.1 \u4f7f\u7528 Tag Helper \u6784\u5efa\u7684\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u8868\u5355\u3002\u6807\u7b7e\u3001\u4e0b\u62c9\u5217\u8868\u3001input \u5143\u7d20\u548c\u9a8c\u8bc1\u6d88\u606f\u90fd\u662f\u4f7f\u7528 Tag Helper \u751f\u6210\u7684\u3002<\/p>\n<p>As you develop the application, you\u2019ll meet the most common Tag Helpers you\u2019ll encounter when working with forms. You\u2019ll also see how you can use Tag Helpers to simplify other common tasks, such as generating links, conditionally displaying data in your application, and ensuring that users see the latest version of an image file when they refresh their browser.<br \/>\n\u5728\u5f00\u53d1\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u60a8\u5c06\u9047\u5230\u5728\u4f7f\u7528\u8868\u5355\u65f6\u9047\u5230\u7684\u6700\u5e38\u89c1\u7684\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u3002\u60a8\u8fd8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u6765\u7b80\u5316\u5176\u4ed6\u5e38\u89c1\u4efb\u52a1\uff0c\u4f8b\u5982\u751f\u6210\u94fe\u63a5\u3001\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6709\u6761\u4ef6\u5730\u663e\u793a\u6570\u636e\uff0c\u4ee5\u53ca\u786e\u4fdd\u7528\u6237\u5728\u5237\u65b0\u6d4f\u89c8\u5668\u65f6\u770b\u5230\u6700\u65b0\u7248\u672c\u7684\u56fe\u50cf\u6587\u4ef6\u3002<\/p>\n<p>To start, I\u2019ll talk a little about why you need Tag Helpers when Razor can already generate any HTML you like by combining C# and HTML in a file.<br \/>\n\u9996\u5148\uff0c\u6211\u5c06\u7b80\u8981\u4ecb\u7ecd\u4e00\u4e0b\u5f53 Razor \u5df2\u7ecf\u53ef\u4ee5\u901a\u8fc7\u5c06 C# \u548c HTML \u7ec4\u5408\u5230\u4e00\u4e2a\u6587\u4ef6\u4e2d\u6765\u751f\u6210\u60a8\u559c\u6b22\u7684\u4efb\u4f55 HTML \u65f6\uff0c\u4e3a\u4ec0\u4e48\u9700\u8981\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u3002<\/p>\n<h2>18.1 Catering to editors with Tag Helpers<\/h2>\n<p>18.1 \u4f7f\u7528\u6807\u7b7e\u52a9\u624b\u8fce\u5408\u7f16\u8f91\u8005<\/p>\n<p>One of the common complaints about the mixture of C# and HTML in Razor templates is that you can\u2019t easily use standard HTML editing tools with them; all the @ and {} symbols in the C# code tend to confuse the editors. Reading the templates can be similarly difficult for people; switching paradigms between C# and HTML can be a bit jarring sometimes.<br \/>\n\u5173\u4e8e Razor \u6a21\u677f\u4e2d C# \u548c HTML \u6df7\u5408\u7684\u5e38\u89c1\u62b1\u6028\u4e4b\u4e00\u662f\uff0c\u60a8\u65e0\u6cd5\u8f7b\u677e\u5730\u5bf9\u5b83\u4eec\u4f7f\u7528\u6807\u51c6\u7684 HTML \u7f16\u8f91\u5de5\u5177;C# \u4ee3\u7801\u4e2d\u7684\u6240\u6709 @ \u548c {} \u7b26\u53f7\u5f80\u5f80\u4f1a\u4f7f\u7f16\u8f91\u5668\u611f\u5230\u56f0\u60d1\u3002\u9605\u8bfb\u6a21\u677f\u5bf9\u4eba\u4eec\u6765\u8bf4\u540c\u6837\u56f0\u96be;\u5728 C# \u548c HTML \u4e4b\u95f4\u5207\u6362\u8303\u4f8b\u6709\u65f6\u53ef\u80fd\u6709\u70b9\u4e0d\u548c\u8c10\u3002<\/p>\n<p>This arguably wasn\u2019t such a problem when Visual Studio was the only supported way to build ASP.NET websites, as it could obviously understand the templates without any problems and helpfully colorize the editor. But with ASP.NET Core going cross-platform, the desire to play nicely with other editors reared its head again.<br \/>\n\u5f53 Visual Studio \u662f\u6784\u5efa ASP.NET \u7f51\u7ad9\u7684\u552f\u4e00\u53d7\u652f\u6301\u65b9\u5f0f\u65f6\uff0c\u8fd9\u53ef\u4ee5\u8bf4\u4e0d\u662f\u95ee\u9898\uff0c\u56e0\u4e3a\u5b83\u663e\u7136\u53ef\u4ee5\u6beb\u65e0\u95ee\u9898\u5730\u7406\u89e3\u6a21\u677f\u5e76\u6709\u52a9\u4e8e\u4e3a\u7f16\u8f91\u5668\u7740\u8272\u3002\u4f46\u968f\u7740 ASP.NET Core \u8de8\u5e73\u53f0\uff0c\u4e0e\u5176\u4ed6\u7f16\u8f91\u5668\u53cb\u597d\u5408\u4f5c\u7684\u613f\u671b\u518d\u6b21\u62ac\u5934\u3002<\/p>\n<p>This was one of the big motivations for Tag Helpers. They integrate seamlessly into the standard HTML syntax by adding what look to be attributes, typically starting with asp-<em>. They\u2019re most often used to generate HTML forms, as shown in the following listing. This listing shows a view from the first iteration of the currency converter application, in which you choose the currencies and quantity to convert.<br \/>\n\u8fd9\u662f Tag Helper \u7684\u4e3b\u8981\u52a8\u673a\u4e4b\u4e00\u3002\u5b83\u4eec\u901a\u8fc7\u6dfb\u52a0\u770b\u8d77\u6765\u50cf\u5c5e\u6027\u7684\u5185\u5bb9\uff08\u901a\u5e38\u4ee5 asp-<\/em> \u5f00\u5934\uff09\u65e0\u7f1d\u96c6\u6210\u5230\u6807\u51c6 HTML \u8bed\u6cd5\u4e2d\u3002\u5b83\u4eec\u6700\u5e38\u7528\u4e8e\u751f\u6210 HTML \u8868\u5355\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u6b64\u6e05\u5355\u663e\u793a\u4e86 currency converter \u5e94\u7528\u7a0b\u5e8f\u7b2c\u4e00\u6b21\u8fed\u4ee3\u7684\u89c6\u56fe\uff0c\u60a8\u53ef\u4ee5\u5728\u5176\u4e2d\u9009\u62e9\u8981\u8f6c\u6362\u7684\u8d27\u5e01\u548c\u6570\u91cf\u3002<\/p>\n<p>Listing 18.1 User registration form using Tag Helpers<br \/>\n\u6e05\u5355 18.1 \u4f7f\u7528 Tag Helpers \u7684\u7528\u6237\u6ce8\u518c\u8868\u5355<\/p>\n<pre><code>@page                    #A\n@model ConvertModel      #A\n&lt;form method=&quot;post&quot;&gt;                                        \n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;CurrencyFrom&quot;&gt;&lt;\/label&gt;      #B\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;CurrencyFrom&quot; \/&gt;     #C\n        &lt;span asp-validation-for=&quot;CurrencyFrom&quot;&gt;&lt;\/span&gt;    #D\n    &lt;\/div&gt;\n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;Quantity&quot;&gt;&lt;\/label&gt;          #B\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;Quantity&quot; \/&gt;         #C\n        &lt;span asp-validation-for=&quot;Quantity&quot;&gt;&lt;\/span&gt;        #D\n    &lt;\/div&gt; \n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;CurrencyTo&quot;&gt;&lt;\/label&gt;        #B\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;CurrencyTo&quot; \/&gt;       #C\n        &lt;span asp-validation-for=&quot;CurrencyTo&quot;&gt;&lt;\/span&gt;      #D\n    &lt;\/div&gt;\n    &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<p>\u2776 This is the view for the Razor Page Convert.cshtml. The Model type is ConvertModel.<br \/>\n\u8fd9\u662f Razor Page Convert.cshtml \u7684\u89c6\u56fe\u3002Model \u7c7b\u578b\u4e3a ConvertModel\u3002<br \/>\n\u2777 asp-for on Labels generates the caption for labels based on the view model.<br \/>\nLabels \u4e0a\u7684 asp-for \u6839\u636e\u89c6\u56fe\u6a21\u578b\u751f\u6210\u6807\u7b7e\u7684\u6807\u9898\u3002<br \/>\n\u2778 asp-for on Inputs generates the correct type, value, name, and validation attributes for the model.<br \/>\ninputs \u4e0a\u7684 asp-for \u4e3a\u6a21\u578b\u751f\u6210\u6b63\u786e\u7684\u7c7b\u578b\u3001\u503c\u3001\u540d\u79f0\u548c\u9a8c\u8bc1\u5c5e\u6027\u3002<br \/>\n\u2779 Validation messages are written to a span using Tag Helpers.<br \/>\n\u9a8c\u8bc1\u6d88\u606f\u4f7f\u7528 Tag Helper \u5199\u5165 span\u3002<\/p>\n<p>At first glance, you might not even spot the Tag Helpers, they blend in so well with the HTML! This makes it easy to edit the files with any standard HTML text editor. But don\u2019t be concerned that you\u2019ve sacrificed readability in Visual Studio. As you can see in figure 18.2, elements with Tag Helpers are distinguishable from the standard HTML <code>&lt;div&gt;<\/code> element and the standard HTML class attribute on the <code>&lt;input&gt;<\/code> element. The C# properties of the view model being referenced (CurrencyFrom, in this case) are also displayed differently from \u201cnormal\u201d HTML attributes. And of course you get IntelliSense, as you\u2019d expect. Most other integrated development environments (IDEs) also include syntax highlighting and IntelliSense support.<br \/>\n\u4e4d\u4e00\u770b\uff0c\u60a8\u751a\u81f3\u53ef\u80fd\u6ca1\u6709\u6ce8\u610f\u5230 Tag Helpers\uff0c\u5b83\u4eec\u4e0e HTML \u878d\u5408\u5f97\u975e\u5e38\u597d\uff01\u8fd9\u4f7f\u5f97\u4f7f\u7528\u4efb\u4f55\u6807\u51c6 HTML \u6587\u672c\u7f16\u8f91\u5668\u7f16\u8f91\u6587\u4ef6\u53d8\u5f97\u5bb9\u6613\u3002\u4f46\u4e0d\u8981\u62c5\u5fc3\u60a8\u727a\u7272\u4e86 Visual Studio \u4e2d\u7684\u53ef\u8bfb\u6027\u3002\u5982\u56fe 18.2 \u6240\u793a\uff0c\u5e26\u6709 Tag Helpers \u7684\u5143\u7d20\u4e0e\u6807\u51c6 HTML <code>&lt;div&gt;<\/code>\u5143\u7d20\u548c\u5143\u7d20\u4e0a\u7684\u6807\u51c6 HTML <code>&lt;input&gt;<\/code>class \u5c5e\u6027\u662f\u53ef\u4ee5\u533a\u5206\u7684\u3002\u6240\u5f15\u7528\u7684\u89c6\u56fe\u6a21\u578b\u7684 C# \u5c5e\u6027\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a CurrencyFrom\uff09\u7684\u663e\u793a\u65b9\u5f0f\u4e5f\u4e0e\u201c\u666e\u901a\u201dHTML \u5c5e\u6027\u4e0d\u540c\u3002\u5f53\u7136\uff0c\u6b63\u5982\u60a8\u6240\u671f\u671b\u7684\u90a3\u6837\uff0c\u60a8\u53ef\u4ee5\u83b7\u5f97 IntelliSense\u3002\u5927\u591a\u6570\u5176\u4ed6\u96c6\u6210\u5f00\u53d1\u73af\u5883 \uff08IDE\uff09 \u8fd8\u5305\u62ec\u8bed\u6cd5\u7a81\u51fa\u663e\u793a\u548c IntelliSense \u652f\u6301\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1802.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.2 In Visual Studio, Tag Helpers are distinguishable from normal elements by being bold and a different color from standard HTML elements and attributes.<br \/>\n\u56fe 18.2 \u5728 Visual Studio \u4e2d\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4e0e\u666e\u901a\u5143\u7d20\u7684\u533a\u522b\u5728\u4e8e\u7c97\u4f53\u548c\u4e0e\u6807\u51c6 HTML \u5143\u7d20\u548c\u5c5e\u6027\u4e0d\u540c\u7684\u989c\u8272\u3002<\/p>\n<p>Tag Helpers are extra attributes on standard HTML elements (or new elements entirely) that work by modifying the HTML element they\u2019re attached to. They let you easily integrate your server-side values, such as those exposed on your PageModel, with the generated HTML.<br \/>\n\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u662f\u6807\u51c6 HTML \u5143\u7d20\uff08\u6216\u5b8c\u5168\u662f\u65b0\u5143\u7d20\uff09\u4e0a\u7684\u989d\u5916\u5c5e\u6027\uff0c\u901a\u8fc7\u4fee\u6539\u5b83\u4eec\u6240\u9644\u52a0\u5230\u7684 HTML \u5143\u7d20\u6765\u5de5\u4f5c\u3002\u5b83\u4eec\u53ef\u8ba9\u60a8\u8f7b\u677e\u5730\u5c06\u670d\u52a1\u5668\u7aef\u503c\uff08\u4f8b\u5982 PageModel \u4e0a\u516c\u5f00\u7684\u503c\uff09\u4e0e\u751f\u6210\u7684 HTML \u96c6\u6210\u3002<\/p>\n<p>Notice that listing 18.1 doesn\u2019t specify the captions to display in the labels. Instead, you declaratively use asp-for=&quot;CurrencyFrom&quot; to say \u201cFor this <code>&lt;label&gt;<\/code>, use the CurrencyFrom property to work out what caption to use.\u201d Similarly, for the <code>&lt;input&gt;<\/code> elements, Tag Helpers are used to<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u6e05\u5355 18.1 \u6ca1\u6709\u6307\u5b9a\u8981\u5728\u6807\u7b7e\u4e2d\u663e\u793a\u7684\u6807\u9898\u3002\u76f8\u53cd\uff0c\u60a8\u4ee5\u58f0\u660e\u65b9\u5f0f\u4f7f\u7528 asp-for=\u201cCurrencyFrom\u201d \u6765\u8868\u793a <code>&lt;label&gt;<\/code>\uff0c\u8bf7\u4f7f\u7528 CurrencyFrom \u5c5e\u6027\u6765\u786e\u5b9a\u8981\u4f7f\u7528\u7684\u6807\u9898\u3002\u540c\u6837\uff0c\u5bf9\u4e8e <code>&lt;input&gt;<\/code>\u5143\u7d20\uff0cTag Helpers \u7528\u4e8e<\/p>\n<p>\u2022  Automatically populate the value from the PageModel property.<br \/>\n\u81ea\u52a8\u586b\u5145 PageModel \u5c5e\u6027\u4e2d\u7684\u503c\u3002<br \/>\n\u2022  Choose the correct id and name, so that when the form is POSTed back to the Razor Page, the property is model-bound correctly.<br \/>\n\u9009\u62e9\u6b63\u786e\u7684 ID \u548c\u540d\u79f0\uff0c\u4ee5\u4fbf\u5728\u5c06\u8868\u5355 POST \u56de Razor \u9875\u9762\u65f6\uff0c\u8be5\u5c5e\u6027\u5c06\u6b63\u786e\u8fdb\u884c\u6a21\u578b\u7ed1\u5b9a\u3002<br \/>\n\u2022  Choose the correct input type to display (for example, a number input for the Quantity property).<br \/>\n\u9009\u62e9\u8981\u663e\u793a\u7684\u6b63\u786e\u8f93\u5165\u7c7b\u578b \uff08\u4f8b\u5982\uff0cQuantity \u5c5e\u6027\u7684\u6570\u5b57\u8f93\u5165\uff09\u3002<br \/>\n\u2022  Display any validation errors, as shown in figure 18.3.<br \/>\n\u663e\u793a\u6240\u6709\u9a8c\u8bc1\u9519\u8bef\uff0c\u5982\u56fe 18.3 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1803.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.3 Tag Helpers hook into the metadata provided by DataAnnotations attributes, as well as the property types themselves. The Validation Tag Helper can even populate error messages based on the ModelState, as you saw in chapter 16.<br \/>\n\u56fe 18.3 \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u6302\u63a5\u5230 DataAnnotations \u5c5e\u6027\u63d0\u4f9b\u7684\u5143\u6570\u636e\u4ee5\u53ca\u5c5e\u6027\u7c7b\u578b\u672c\u8eab\u3002Validation Tag Helper \u751a\u81f3\u53ef\u4ee5\u6839\u636e ModelState \u586b\u5145\u9519\u8bef\u6d88\u606f\uff0c\u5982\u7b2c 16 \u7ae0\u6240\u793a\u3002<\/p>\n<p>Tag Helpers can perform a variety of functions by modifying the HTML elements they\u2019re applied to. This chapter introduces several common Tag Helpers and how to use them, but it\u2019s not an exhaustive list. I don\u2019t cover all the helpers that come out of the box in ASP.NET Core (there are more coming with every release!), and you can easily create your own, as you\u2019ll see in chapter 32. Alternatively, you could use those published by others on NuGet or GitHub.<br \/>\n\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u4ee5\u901a\u8fc7\u4fee\u6539\u5b83\u4eec\u6240\u5e94\u7528\u7684 HTML \u5143\u7d20\u6765\u6267\u884c\u5404\u79cd\u529f\u80fd\u3002\u672c\u7ae0\u4ecb\u7ecd\u4e86\u51e0\u79cd\u5e38\u89c1\u7684 Tag Helper \u53ca\u5176\u4f7f\u7528\u65b9\u6cd5\uff0c\u4f46\u5e76\u975e\u8be6\u5c3d\u65e0\u9057\u3002\u6211\u6ca1\u6709\u6db5\u76d6 ASP.NET Core \u4e2d\u5f00\u7bb1\u5373\u7528\u7684\u6240\u6709\u5e2e\u52a9\u7a0b\u5e8f\uff08\u6bcf\u4e2a\u7248\u672c\u90fd\u4f1a\u63d0\u4f9b\u66f4\u591a\u5e2e\u52a9\u7a0b\u5e8f\uff09\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u521b\u5efa\u81ea\u5df1\u7684\u5e2e\u52a9\u7a0b\u5e8f\uff0c\u5982\u7b2c 32 \u7ae0\u6240\u793a\u3002\u6216\u8005\uff0c\u60a8\u4e5f\u53ef\u4ee5\u4f7f\u7528\u5176\u4ed6\u4eba\u5728 NuGet \u6216 GitHub \u4e0a\u53d1\u5e03\u7684 Navi\u3002<\/p>\n<blockquote>\n<p>WebForms flashbacks<br \/>\nWebForms \u95ea\u56de<\/p>\n<p>For those who used ASP.NET back in the day of WebForms, before the advent of the Model-View-Controller (MVC) pattern for web development, Tag Helpers may be triggering bad memories. Although the asp- prefix is somewhat reminiscent of ASP.NET Web Server control definitions, never fear; the two are completely different beasts.<br \/>\n\u5bf9\u4e8e\u90a3\u4e9b\u5728 WebForms \u65f6\u4ee3\u4f7f\u7528 ASP.NET \u7684\u4eba\u6765\u8bf4\uff0c\u5728\u7528\u4e8e Web \u5f00\u53d1\u7684\u6a21\u578b-\u89c6\u56fe-\u63a7\u5236\u5668 \uff08MVC\uff09 \u6a21\u5f0f\u51fa\u73b0\u4e4b\u524d\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u80fd\u4f1a\u89e6\u53d1\u7cdf\u7cd5\u7684\u56de\u5fc6\u3002\u5c3d\u7ba1 asp- \u524d\u7f00\u6709\u70b9\u8ba9\u4eba\u60f3\u8d77 ASP.NET Web \u670d\u52a1\u5668\u63a7\u4ef6\u5b9a\u4e49\uff0c\u4f46\u4e0d\u8981\u5bb3\u6015;\u4e24\u8005\u662f\u5b8c\u5168\u4e0d\u540c\u7684\u91ce\u517d\u3002<\/p>\n<p>Web Server controls were added directly to a page\u2019s backing C# class and had a broad scope that could modify seemingly unrelated parts of the page. Coupled with that, they had a complex life cycle that was hard to understand and debug when things weren\u2019t working. The perils of trying to work with that level of complexity haven\u2019t been forgotten, and Tag Helpers aren\u2019t the same.<br \/>\nWeb \u670d\u52a1\u5668\u63a7\u4ef6\u76f4\u63a5\u6dfb\u52a0\u5230\u9875\u9762\u7684\u652f\u6301 C# \u7c7b\u4e2d\uff0c\u5e76\u4e14\u5177\u6709\u5e7f\u6cdb\u7684\u8303\u56f4\uff0c\u53ef\u4ee5\u4fee\u6539\u9875\u9762\u4e2d\u770b\u4f3c\u4e0d\u76f8\u5173\u7684\u90e8\u5206\u3002\u518d\u52a0\u4e0a\uff0c\u5b83\u4eec\u7684\u751f\u547d\u5468\u671f\u5f88\u590d\u6742\uff0c\u5f53\u4e8b\u60c5\u4e0d\u6b63\u5e38\u65f6\uff0c\u5f88\u96be\u7406\u89e3\u548c\u8c03\u8bd5\u3002\u5c1d\u8bd5\u5904\u7406\u8fd9\u79cd\u590d\u6742\u7a0b\u5ea6\u7684\u5371\u9669\u5e76\u6ca1\u6709\u88ab\u9057\u5fd8\uff0c\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u4e5f\u4e0d\u4e00\u6837\u3002<\/p>\n<p>Tag Helpers don\u2019t have a life cycle; they participate in the rendering of the element to which they\u2019re attached, and that\u2019s it. They can modify the HTML element they\u2019re attached to, but they can\u2019t modify anything else on your page, making them conceptually much simpler. An additional capability they bring is the ability to have multiple Tag Helpers acting on a single element\u2014something Web Server controls couldn\u2019t easily achieve.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u6ca1\u6709\u751f\u547d\u5468\u671f;\u5b83\u4eec\u53c2\u4e0e\u6e32\u67d3\u5b83\u4eec\u6240\u9644\u52a0\u5230\u7684\u5143\u7d20\uff0c\u4ec5\u6b64\u800c\u5df2\u3002\u4ed6\u4eec\u53ef\u4ee5\u4fee\u6539\u5b83\u4eec\u6240\u9644\u52a0\u5230\u7684 HTML \u5143\u7d20\uff0c\u4f46\u4e0d\u80fd\u4fee\u6539\u9875\u9762\u4e0a\u7684\u4efb\u4f55\u5176\u4ed6\u5185\u5bb9\uff0c\u4ece\u800c\u5728\u6982\u5ff5\u4e0a\u4f7f\u5b83\u4eec\u53d8\u5f97\u66f4\u52a0\u7b80\u5355\u3002\u5b83\u4eec\u5e26\u6765\u7684\u53e6\u4e00\u9879\u529f\u80fd\u662f\u80fd\u591f\u8ba9\u591a\u4e2a Tag Helpers \u4f5c\u7528\u4e8e\u5355\u4e2a\u5143\u7d20 \u2014 \u8fd9\u662f Web Server \u63a7\u4ef6\u65e0\u6cd5\u8f7b\u677e\u5b9e\u73b0\u7684\u3002<\/p>\n<p>Overall, if you\u2019re writing Razor templates, you\u2019ll have a much more enjoyable experience if you embrace Tag Helpers as integral to its syntax. They bring a lot of benefits without obvious downsides, and your cross-platform-editor friends will thank you!<br \/>\n\u603b\u7684\u6765\u8bf4\uff0c\u5982\u679c\u4f60\u6b63\u5728\u7f16\u5199 Razor \u6a21\u677f\uff0c\u5982\u679c\u4f60\u5c06 Tag Helpers \u4f5c\u4e3a\u5176\u8bed\u6cd5\u7684\u7ec4\u6210\u90e8\u5206\uff0c\u4f60\u5c06\u83b7\u5f97\u66f4\u6109\u5feb\u7684\u4f53\u9a8c\u3002\u5b83\u4eec\u5e26\u6765\u4e86\u5f88\u591a\u597d\u5904\uff0c\u800c\u4e14\u6ca1\u6709\u660e\u663e\u7684\u7f3a\u70b9\uff0c\u4f60\u7684\u8de8\u5e73\u53f0\u7f16\u8f91\u5668\u670b\u53cb\u4f1a\u611f\u8c22\u4f60\uff01<\/p>\n<\/blockquote>\n<h2>18.2 Creating forms using Tag Helpers<\/h2>\n<p>18.2 \u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u521b\u5efa\u8868\u5355<\/p>\n<p>In this section you\u2019ll learn how to use some of the most useful Tag Helpers: Tag Helpers that work with forms. You\u2019ll learn how to use them to generate HTML markup based on properties of your PageModel, creating the correct id and name attributes, and setting the value of the element to the model property\u2019s value (among other things). This capability significantly reduces the amount of markup you need to write manually.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528\u4e00\u4e9b\u6700\u6709\u7528\u7684 Tag Helpers\uff1a\u4f7f\u7528\u8868\u5355\u7684 Tag Helpers\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528\u5b83\u4eec\u6839\u636e PageModel \u7684\u5c5e\u6027\u751f\u6210 HTML \u6807\u8bb0\uff0c\u521b\u5efa\u6b63\u786e\u7684 id \u548c name \u5c5e\u6027\uff0c\u4ee5\u53ca\u5c06\u5143\u7d20\u7684\u503c\u8bbe\u7f6e\u4e3a model \u5c5e\u6027\u7684\u503c\uff08\u4ee5\u53ca\u5176\u4ed6\u5185\u5bb9\uff09\u3002\u6b64\u529f\u80fd\u663e\u8457\u51cf\u5c11\u4e86\u60a8\u9700\u8981\u624b\u52a8\u7f16\u5199\u7684\u6807\u8bb0\u91cf\u3002<\/p>\n<p>Imagine you\u2019re building the checkout page for the currency converter application, and you need to capture the user\u2019s details on the checkout page. In chapter 16 you built a UserBindingModel model (shown in listing 18.2), added DataAnnotations attributes for validation, and saw how to model-bind it in a POST to a Razor Page. In this chapter you\u2019ll see how to create the view for it by exposing the UserBindingModel as a property on your PageModel.<br \/>\n\u5047\u8bbe\u60a8\u6b63\u5728\u4e3a\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u7ed3\u5e10\u9875\u9762\uff0c\u5e76\u4e14\u60a8\u9700\u8981\u5728\u7ed3\u5e10\u9875\u9762\u4e0a\u6355\u83b7\u7528\u6237\u7684\u8be6\u7ec6\u4fe1\u606f\u3002\u5728\u7b2c 16 \u7ae0\u4e2d\uff0c\u60a8\u6784\u5efa\u4e86\u4e00\u4e2a UserBindingModel \u6a21\u578b\uff08\u5982\u6e05\u5355 18.2 \u6240\u793a\uff09\uff0c\u6dfb\u52a0\u4e86\u7528\u4e8e\u9a8c\u8bc1\u7684 DataAnnotations \u5c5e\u6027\uff0c\u5e76\u4e86\u89e3\u4e86\u5982\u4f55\u5728 POST \u4e2d\u5c06\u5176\u6a21\u578b\u7ed1\u5b9a\u5230 Razor \u9875\u9762\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u901a\u8fc7\u5c06 UserBindingModel \u4f5c\u4e3a PageModel \u4e0a\u7684\u5c5e\u6027\u516c\u5f00\u6765\u4e3a\u5176\u521b\u5efa\u89c6\u56fe\u3002<\/p>\n<p><b>Warning<\/b> With Razor Pages, you often expose the same object in your view that you use for model binding. When you do this, you must be careful to not include sensitive values (that shouldn\u2019t be edited) in the binding model, to prevent mass-assignment attacks on your app. You can read more about these attacks on my blog at <a href=\"http:\/\/mng.bz\/RXw0\">http:\/\/mng.bz\/RXw0<\/a>.<br \/>\n\u8b66\u544a\uff1a\u4f7f\u7528 Razor Pages\uff0c\u60a8\u901a\u5e38\u4f1a\u5728\u89c6\u56fe\u4e2d\u516c\u5f00\u7528\u4e8e\u6a21\u578b\u7ed1\u5b9a\u7684\u76f8\u540c\u5bf9\u8c61\u3002\u6267\u884c\u6b64\u4f5c\u65f6\uff0c\u5fc5\u987b\u6ce8\u610f\u4e0d\u8981\u5728\u7ed1\u5b9a\u6a21\u578b\u4e2d\u5305\u542b\u654f\u611f\u503c\uff08\u4e0d\u5e94\u7f16\u8f91\uff09\uff0c\u4ee5\u9632\u6b62\u5bf9\u5e94\u7528\u7a0b\u5e8f\u8fdb\u884c\u6279\u91cf\u8d4b\u503c\u653b\u51fb\u3002\u60a8\u53ef\u4ee5\u5728\u6211\u7684\u535a\u5ba2 <a href=\"http:\/\/mng.bz\/RXw0\">http:\/\/mng.bz\/RXw0<\/a> \u4e0a\u9605\u8bfb\u6709\u5173\u8fd9\u4e9b\u653b\u51fb\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>Listing 18.2 UserBindingModel for creating a user on a checkout page<br \/>\n\u5217\u8868 18.2 \u7528\u4e8e\u5728\u7ed3\u5e10\u9875\u9762\u4e0a\u521b\u5efa\u7528\u6237\u7684 UserBindingModel<\/p>\n<pre><code>public class UserBindingModel\n{\n    [Required]\n    [StringLength(100, ErrorMessage = &quot;Maximum length is {1}&quot;)]\n    [Display(Name = &quot;Your name&quot;)]\n    public string FirstName { get; set; }\n\n    [Required]\n    [StringLength(100, ErrorMessage = &quot;Maximum length is {1}&quot;)]\n    [Display(Name = &quot;Last name&quot;)]\n    public string LastName { get; set; }\n\n    [Required]\n    [EmailAddress]\n    public string Email { get; set; }\n\n    [Phone(ErrorMessage = &quot;Not a valid phone number.&quot;)]\n    [Display(Name = &quot;Phone number&quot;)]\n    public string PhoneNumber { get; set; }\n}<\/code><\/pre>\n<p>The UserBindingModel is decorated with various DataAnnotations attributes. In chapter 16 you saw that these attributes are used during model validation when the model is bound to a request, before the page handler is executed. These attributes are also used by the Razor templating language to provide the metadata required to generate the correct HTML when you use Tag Helpers.<br \/>\nUserBindingModel \u4f7f\u7528\u5404\u79cd DataAnnotations \u5c5e\u6027\u8fdb\u884c\u4fee\u9970\u3002\u5728\u7b2c 16 \u7ae0\u4e2d\uff0c\u4f60\u770b\u5230\u8fd9\u4e9b\u5c5e\u6027\u5728\u6a21\u578b\u9a8c\u8bc1\u671f\u95f4\u3001\u5f53\u6a21\u578b\u7ed1\u5b9a\u5230\u8bf7\u6c42\u65f6\u3001\u5728\u6267\u884c\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u4e4b\u524d\u4f7f\u7528\u3002Razor \u6a21\u677f\u8bed\u8a00\u8fd8\u4f7f\u7528\u8fd9\u4e9b\u5c5e\u6027\u6765\u63d0\u4f9b\u5728\u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u65f6\u751f\u6210\u6b63\u786e HTML \u6240\u9700\u7684\u5143\u6570\u636e\u3002<\/p>\n<p>You can use the pattern I described in chapter 16, exposing a UserBindindModel as an Input property of your PageModel, to use the model for both model binding and in your Razor view:<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u6211\u5728\u7b2c 16 \u7ae0 \u5c06 UserBindindModel \u516c\u5f00\u4e3a PageModel \u7684 Input \u5c5e\u6027\u4e2d\u63cf\u8ff0\u7684\u6a21\u5f0f\uff0c\u5c06\u8be5\u6a21\u578b\u7528\u4e8e\u6a21\u578b\u7ed1\u5b9a\u548c Razor \u89c6\u56fe\uff1a<\/p>\n<pre><code>public class CheckoutModel: PageModel\n{\n    [BindProperty]\n    public UserBindingModel Input { get; set; }\n}<\/code><\/pre>\n<p>With the help of the UserBindingModel property, Tag Helpers, and a little HTML, you can create a Razor view that lets the user enter their details, as shown in figure 18.4.<br \/>\n\u501f\u52a9 UserBindingModel \u5c5e\u6027\u3001\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u548c\u4e00\u4e9b HTML\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a\u5141\u8bb8\u7528\u6237\u8f93\u5165\u5176\u8be6\u7ec6\u4fe1\u606f\u7684 Razor \u89c6\u56fe\uff0c\u5982\u56fe 18.4 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1804.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.4 The checkout page for an application. The HTML is generated based on a UserBindingModel, using Tag Helpers to render the required element values, input types, and validation messages.<br \/>\n\u56fe 18.4 \u5e94\u7528\u7a0b\u5e8f\u7684\u7ed3\u5e10\u9875\u9762\u3002HTML \u662f\u57fa\u4e8e UserBindingModel \u751f\u6210\u7684\uff0c\u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5448\u73b0\u6240\u9700\u7684\u5143\u7d20\u503c\u3001\u8f93\u5165\u7c7b\u578b\u548c\u9a8c\u8bc1\u6d88\u606f\u3002<\/p>\n<p>The Razor template to generate this page is shown in listing 18.3. This code uses a variety of tag helpers, including<br \/>\n\u7528\u4e8e\u751f\u6210\u6b64\u9875\u9762\u7684 Razor \u6a21\u677f\u5982\u6e05\u5355 18.3 \u6240\u793a\u3002\u6b64\u4ee3\u7801\u4f7f\u7528\u5404\u79cd\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\uff0c\u5305\u62ec<\/p>\n<p>\u2022  A Form Tag Helper on the <code>&lt;form&gt; <\/code>element<br \/>\n<code>&lt;form&gt; <\/code>\u5143\u7d20\u4e0a\u7684\u8868\u5355\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<br \/>\n\u2022  Label Tag Helpers on the <code>&lt;label&gt;<\/code><br \/>\n\u6807\u7b7e\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f<br \/>\n\u2022  Input Tag Helpers on the <code>&lt;input&gt;<\/code><br \/>\n\u8f93\u5165\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<br \/>\n\u2022  Validation Message Tag Helpers on <code>&lt;span&gt;<\/code> validation elements for each property in the UserBindingModel<br \/>\nUserBindingModel \u4e2d\u6bcf\u4e2a\u5c5e\u6027\u7684\u9a8c\u8bc1\u5143\u7d20\u4e0a\u7684\u9a8c\u8bc1\u6d88\u606f <code>&lt;span&gt;<\/code>\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<\/p>\n<p>Listing 18.3 Razor template for binding to UserBindingModel on the checkout page<br \/>\n\u5217\u8868 18.3 \u7528\u4e8e\u5728\u7ed3\u5e10\u9875\u9762\u4e0a\u7ed1\u5b9a\u5230 UserBindingModel \u7684 Razor \u6a21\u677f<\/p>\n<pre><code>@page\n@model CheckoutModel    #A\n@{\n    ViewData[&quot;Title&quot;] = &quot;Checkout&quot;;\n}\n&lt;h1&gt;@ViewData[&quot;Title&quot;]&lt;\/h1&gt;\n&lt;form asp-page=&quot;Checkout&quot;&gt;      #B\n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;Input.FirstName&quot;&gt;&lt;\/label&gt;               #C\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;Input.FirstName&quot; \/&gt;\n        &lt;span asp-validation-for=&quot;Input.FirstName&quot;&gt;&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;Input.LastName&quot;&gt;&lt;\/label&gt;             \n        &lt;input class=&quot;form-control&quot; asp-for=&quot;Input.LastName&quot; \/&gt;\n        &lt;span asp-validation-for=&quot;Input.LastName&quot;&gt;&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;Input.Email&quot;&gt;&lt;\/label&gt;\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;Input.Email&quot; \/&gt;    #D\n        &lt;span asp-validation-for=&quot;Input.Email&quot;&gt;&lt;\/span&gt;\n    &lt;\/div&gt;\n    &lt;div class=&quot;form-group&quot;&gt;\n        &lt;label asp-for=&quot;Input.PhoneNumber&quot;&gt;&lt;\/label&gt;\n        &lt;input class=&quot;form-control&quot; asp-for=&quot;Input.PhoneNumber&quot; \/&gt;\n        &lt;span asp-validation-for=&quot;Input.PhoneNumber&quot;&gt;&lt;\/span&gt;    #E\n    &lt;\/div&gt;\n    &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;\/button&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<p>\u2776 The CheckoutModel is the PageModel, which exposes a UserBindingModel on the Input property.<br \/>\nCheckoutModel \u662f PageModel\uff0c\u5b83\u5728 Input \u5c5e\u6027\u4e0a\u516c\u5f00 UserBindingModel\u3002<br \/>\n\u2777 Form Tag Helpers use routing to determine the URL the form will be posted to.<br \/>\n\u8868\u5355\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u4f7f\u7528\u8def\u7531\u6765\u786e\u5b9a\u8868\u5355\u5c06\u53d1\u5e03\u5230\u7684 URL\u3002<br \/>\n\u2778 The Label Tag Helper uses DataAnnotations on a property to determine the caption to display.<br \/>\nLabel Tag Helper \u5728\u5c5e\u6027\u4e0a\u4f7f\u7528 DataAnnotations \u6765\u786e\u5b9a\u8981\u663e\u793a\u7684\u6807\u9898\u3002<br \/>\n\u2779 The Input Tag Helper uses DataAnnotations to determine the type of input to generate.<br \/>\nInput Tag Helper \u4f7f\u7528 DataAnnotations \u6765\u786e\u5b9a\u8981\u751f\u6210\u7684\u8f93\u5165\u7c7b\u578b\u3002<br \/>\n\u277a The Validation Tag Helper displays error messages associated with the given property.<br \/>\nValidation Tag Helper \u663e\u793a\u4e0e\u7ed9\u5b9a\u5c5e\u6027\u5173\u8054\u7684\u9519\u8bef\u6d88\u606f\u3002<\/p>\n<p>You can see the HTML markup that this template produces in listing 18.4, which renders in the browser as you saw in figure 18.4. You can see that each of the HTML elements with a Tag Helper has been customized in the output: the <code>&lt;form&gt;<\/code> element has an action attribute, the <code>&lt;input&gt;<\/code> elements have an id and name based on the name of the referenced property, and both the <code>&lt;input&gt;<\/code> and <code>&lt;span&gt;<\/code> have data-<em> attributes for validation.<br \/>\n\u60a8\u53ef\u4ee5\u5728\u6e05\u5355 18.4 \u4e2d\u770b\u5230\u6b64\u6a21\u677f\u751f\u6210\u7684 HTML \u6807\u8bb0\uff0c\u8be5\u6807\u8bb0\u5728\u6d4f\u89c8\u5668\u4e2d\u5448\u73b0\uff0c\u5982\u56fe 18.4 \u6240\u793a\u3002\u60a8\u53ef\u4ee5\u770b\u5230\uff0c\u6bcf\u4e2a\u5e26\u6709 Tag Helper \u7684 HTML \u5143\u7d20\u5728\u8f93\u51fa\u4e2d\u90fd\u5df2\u81ea\u5b9a\u4e49\uff1a <code>&lt;form&gt;<\/code>\u5143\u7d20\u5177\u6709 action \u5c5e\u6027\uff0c<code>&lt;input&gt;<\/code> \u5143\u7d20\u5177\u6709\u57fa\u4e8e\u5f15\u7528\u5c5e\u6027\u540d\u79f0\u7684 id \u548c name\uff0c\u5e76\u4e14<code>&lt;input&gt;<\/code>  \u548c<code>&lt;span&gt;<\/code> \u90fd\u5177\u6709\u7528\u4e8e\u9a8c\u8bc1\u7684 data-<\/em> \u5c5e\u6027\u3002<\/p>\n<p>Listing 18.4 HTML generated by the Razor template on the checkout page<br \/>\n\u5217\u8868 18.4 \u7ed3\u5e10\u9875\u9762\u4e0a Razor \u6a21\u677f\u751f\u6210\u7684 HTML<\/p>\n<pre><code>&lt;form action=&quot;\/Checkout&quot; method=&quot;post&quot;&gt;\n  &lt;div class=&quot;form-group&quot;&gt;\n    &lt;label for=&quot;Input_FirstName&quot;&gt;Your name&lt;\/label&gt;\n    &lt;input class=&quot;form-control&quot; type=&quot;text&quot;\n      data-val=&quot;true&quot; data-val-length=&quot;Maximum length is 100&quot;\n      id=&quot;Input_FirstName&quot; data-val-length-max=&quot;100&quot;\n      data-val-required=&quot;The Your name field is required.&quot;\n      Maxlength=&quot;100&quot; name=&quot;Input.FirstName&quot; value=&quot;&quot; \/&gt;\n    &lt;span data-valmsg-for=&quot;Input.FirstName&quot;\n      class=&quot;field-validation-valid&quot; data-valmsg-replace=&quot;true&quot;&gt;&lt;\/span&gt;\n  &lt;\/div&gt;\n  &lt;div class=&quot;form-group&quot;&gt;\n    &lt;label for=&quot;Input_LastName&quot;&gt;Your name&lt;\/label&gt;\n    &lt;input class=&quot;form-control&quot; type=&quot;text&quot;\n      data-val=&quot;true&quot; data-val-length=&quot;Maximum length is 100&quot;\n      id=&quot;Input_LastName&quot; data-val-length-max=&quot;100&quot;\n      data-val-required=&quot;The Your name field is required.&quot;\n      Maxlength=&quot;100&quot; name=&quot;Input.LastName&quot; value=&quot;&quot; \/&gt;\n    &lt;span data-valmsg-for=&quot;Input.LastName&quot;\n      class=&quot;field-validation-valid&quot; data-valmsg-replace=&quot;true&quot;&gt;&lt;\/span&gt;\n  &lt;\/div&gt;\n  &lt;div class=&quot;form-group&quot;&gt;\n    &lt;label for=&quot;Input_Email&quot;&gt;Email&lt;\/label&gt;\n    &lt;input class=&quot;form-control&quot; type=&quot;email&quot; data-val=&quot;true&quot;\n      data-val-email=&quot;The Email field is not a valid e-mail address.&quot;\n      Data-val-required=&quot;The Email field is required.&quot;\n      Id=&quot;Input_Email&quot; name=&quot;Input.Email&quot; value=&quot;&quot; \/&gt;\n    &lt;span class=&quot;text-danger field-validation-valid&quot;\n      data-valmsg-for=&quot;Input.Email&quot; data-valmsg-replace=&quot;true&quot;&gt;&lt;\/span&gt;\n    &lt;\/div&gt;\n  &lt;div class=&quot;form-group&quot;&gt;\n    &lt;label for=&quot;Input_PhoneNumber&quot;&gt;Phone number&lt;\/label&gt;\n    &lt;input class=&quot;form-control&quot; type=&quot;tel&quot; data-val=&quot;true&quot;\n      data-val-phone=&quot;Not a valid phone number.&quot; Id=&quot;Input_PhoneNumber&quot;\n      name=&quot;Input.PhoneNumber&quot; value=&quot;&quot; \/&gt;\n    &lt;span data-valmsg-for=&quot;Input.PhoneNumber&quot;\n      class=&quot;text-danger field-validation-valid&quot;\n      data-valmsg-replace=&quot;true&quot;&gt;&lt;\/span&gt;\n  &lt;\/div&gt;\n  &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;Submit&lt;\/button&gt;\n  &lt;input name=&quot;__RequestVerificationToken&quot; type=&quot;hidden&quot;\n    value=&quot;CfDJ8PkYhAINFx1JmYUVIDWbpPyy_TRUNCATED&quot; \/&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<p>Wow, that\u2019s a lot of markup! If you\u2019re new to working with HTML, this might all seem a little overwhelming, but the important thing to notice is that you didn\u2019t have to write most of it! The Tag Helpers took care of most of the plumbing for you. That\u2019s basically Tag Helpers in a nutshell; they simplify the fiddly mechanics of building HTML forms, leaving you to concentrate on the overall design of your application instead of writing boilerplate markup.<br \/>\n\u54c7\uff0c\u597d\u591a\u6807\u8bb0\u554a\uff01\u5982\u679c\u60a8\u521a\u5f00\u59cb\u4f7f\u7528 HTML\uff0c\u8fd9\u53ef\u80fd\u770b\u8d77\u6765\u6709\u70b9\u8ba9\u4eba\u4e0d\u77e5\u6240\u63aa\uff0c\u4f46\u9700\u8981\u6ce8\u610f\u7684\u91cd\u8981\u4e00\u70b9\u662f\uff0c\u60a8\u4e0d\u5fc5\u7f16\u5199\u5927\u90e8\u5206\u5185\u5bb9\uff01Tag Helpers \u4e3a\u60a8\u5904\u7406\u4e86\u5927\u90e8\u5206\u7ba1\u9053\u5de5\u4f5c\u3002\u7b80\u800c\u8a00\u4e4b\uff0c\u8fd9\u57fa\u672c\u4e0a\u5c31\u662f Tag Helpers;\u5b83\u4eec\u7b80\u5316\u4e86\u6784\u5efa HTML \u8868\u5355\u7684\u7e41\u7410\u673a\u5236\uff0c\u8ba9\u60a8\u4e13\u6ce8\u4e8e\u5e94\u7528\u7a0b\u5e8f\u7684\u6574\u4f53\u8bbe\u8ba1\uff0c\u800c\u4e0d\u662f\u7f16\u5199\u6837\u677f\u6807\u8bb0\u3002<\/p>\n<p><b>NOTE<\/b> If you\u2019re using Razor to build your views, Tag Helpers will make your life easier, but they\u2019re entirely optional. You\u2019re free to write raw HTML without them or to use the legacy HTML Helpers.<br \/>\n\u6ce8\u610f\uff1a\u5982\u679c\u60a8\u4f7f\u7528 Razor \u6784\u5efa\u89c6\u56fe\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5c06\u4f7f\u60a8\u7684\u751f\u6d3b\u66f4\u8f7b\u677e\uff0c\u4f46\u5b83\u4eec\u5b8c\u5168\u662f\u53ef\u9009\u7684\u3002\u60a8\u53ef\u4ee5\u81ea\u7531\u7f16\u5199\u6ca1\u6709\u5b83\u4eec\u7684\u539f\u59cb HTML\uff0c\u4e5f\u53ef\u4ee5\u4f7f\u7528\u65e7\u7248 HTML \u5e2e\u52a9\u7a0b\u5e8f\u3002<\/p>\n<p>Tag Helpers simplify and abstract the process of HTML generation, but they generally try to do so without getting in your way. If you need the final generated HTML to have a particular attribute, you can add it to your markup. You can see that in the previous listings where class attributes are defined on <code>&lt;input&gt;<\/code> elements, such as <code>&lt;input class=&quot;form-control&quot; asp-for=&quot;Input.FirstName&quot; \/&gt;<\/code>. They pass untouched from Razor to the HTML output.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u7b80\u5316\u548c\u62bd\u8c61\u4e86 HTML \u751f\u6210\u8fc7\u7a0b\uff0c\u4f46\u5b83\u4eec\u901a\u5e38\u4f1a\u5c1d\u8bd5\u5728\u4e0d\u59a8\u788d\u60a8\u7684\u60c5\u51b5\u4e0b\u8fd9\u6837\u505a\u3002\u5982\u679c\u9700\u8981\u6700\u7ec8\u751f\u6210\u7684 HTML \u5177\u6709\u7279\u5b9a\u5c5e\u6027\uff0c\u53ef\u4ee5\u5c06\u5176\u6dfb\u52a0\u5230\u6807\u8bb0\u4e2d\u3002\u4f60\u53ef\u4ee5\u770b\u5230\uff0c\u5728\u524d\u9762\u7684\u6e05\u5355\u4e2d\uff0c\u7c7b\u5c5e\u6027\u662f\u5728 <code>&lt;input&gt;<\/code> \u5143\u7d20\u4e0a\u5b9a\u4e49\u7684\uff0c\u6bd4\u5982  <code>&lt;input class=&quot;form-control&quot; asp-for=&quot;Input.FirstName&quot; \/&gt;<\/code>.\u5b83\u4eec\u5c06\u539f\u5c01\u4e0d\u52a8\u7684 Razor \u4f20\u9012\u5230 HTML \u8f93\u51fa\u3002<\/p>\n<p><b>Tip<\/b> This is different from the way HTML Helpers worked in legacy ASP.NET; HTML helpers often require jumping through hoops to set attributes in the generated markup.<br \/>\n\u63d0\u793a\uff1a\u8fd9\u4e0e HTML \u5e2e\u52a9\u7a0b\u5e8f\u5728\u65e7\u7248 ASP.NET \u4e2d\u7684\u5de5\u4f5c\u65b9\u5f0f\u4e0d\u540c;HTML \u5e2e\u52a9\u7a0b\u5e8f\u901a\u5e38\u9700\u8981\u8df3\u8fc7\u91cd\u91cd\u969c\u788d\u624d\u80fd\u5728\u751f\u6210\u7684\u6807\u8bb0\u4e2d\u8bbe\u7f6e\u5c5e\u6027\u3002<\/p>\n<p>Even better, you can also override attributes that are normally generated by a Tag Helper, like the type attribute on an <code>&lt;input&gt;<\/code> element. For example, if the FavoriteColor property on your PageModel was a string, by default Tag Helpers would generate an <code>&lt;input&gt;<\/code> element with type=&quot;text&quot;. Updating your markup to use the HTML5 color picker type is trivial; set the type explicitly in your Razor view:<br \/>\n\u66f4\u597d\u7684\u662f\uff0c\u60a8\u8fd8\u53ef\u4ee5\u8986\u76d6\u901a\u5e38\u7531 Tag Helper \u751f\u6210\u7684\u5c5e\u6027\uff0c\u4f8b\u5982<code>&lt;input&gt;<\/code> \u5143\u7d20\u4e0a\u7684 type \u5c5e\u6027\u3002\u4f8b\u5982\uff0c\u5982\u679c PageModel \u4e0a\u7684 FavoriteColor \u5c5e\u6027\u662f\u4e00\u4e2a\u5b57\u7b26\u4e32\uff0c\u5219\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5c06\u751f\u6210\u4e00\u4e2a\u5177\u6709 type=\u201ctext\u201d \u7684<code>&lt;input&gt;<\/code> \u5143\u7d20\u3002\u66f4\u65b0\u6807\u8bb0\u4ee5\u4f7f\u7528 HTML5 \u989c\u8272\u9009\u53d6\u5668\u7c7b\u578b\u975e\u5e38\u7b80\u5355;\u5728 Razor \u89c6\u56fe\u4e2d\u663e\u5f0f\u8bbe\u7f6e\u7c7b\u578b\uff1a<\/p>\n<pre><code>&lt;input type=&quot;color&quot; asp-for=&quot;FavoriteColor&quot; \/&gt;<\/code><\/pre>\n<p><b>Tip<\/b> HTML5 adds a huge number of features, including lots of form elements that you may not have come across before, such as range inputs and color pickers. You can read about them on the Mozilla Developer Network website at <a href=\"http:\/\/mng.bz\/qOc1\">http:\/\/mng.bz\/qOc1<\/a>.<br \/>\n\u63d0\u793a\uff1aHTML5 \u6dfb\u52a0\u4e86\u5927\u91cf\u529f\u80fd\uff0c\u5305\u62ec\u8bb8\u591a\u60a8\u4ee5\u524d\u53ef\u80fd\u6ca1\u6709\u9047\u5230\u8fc7\u7684\u8868\u5355\u5143\u7d20\uff0c\u4f8b\u5982\u8303\u56f4\u8f93\u5165\u548c\u989c\u8272\u9009\u62e9\u5668\u3002\u60a8\u53ef\u4ee5\u5728 Mozilla Developer Network \u7f51\u7ad9\u4e0a\u9605\u8bfb\u6709\u5173\u5b83\u4eec\u7684\u4fe1\u606f\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/qOc1\">http:\/\/mng.bz\/qOc1<\/a>\u3002<\/p>\n<p>For the remainder of section 18.2, you\u2019ll build the currency converter Razor templates from scratch, adding Tag Helpers as you find you need them. You\u2019ll probably find you use most of the common form Tag Helpers in every application you build, even if it\u2019s on a simple login page.<br \/>\n\u5728\u7b2c 18.2 \u8282\u7684\u5176\u4f59\u90e8\u5206\uff0c\u60a8\u5c06\u4ece\u5934\u5f00\u59cb\u6784\u5efa\u8d27\u5e01\u8f6c\u6362\u5668 Razor \u6a21\u677f\uff0c\u5e76\u6839\u636e\u9700\u8981\u6dfb\u52a0 Tag Helpers\u3002\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0\uff0c\u5728\u6784\u5efa\u7684\u6bcf\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u90fd\u4f7f\u7528\u4e86\u5927\u591a\u6570\u5e38\u89c1\u5f62\u5f0f\u7684 Tag Helpers\uff0c\u5373\u4f7f\u5b83\u4f4d\u4e8e\u7b80\u5355\u7684\u767b\u5f55\u9875\u9762\u4e0a\u3002<\/p>\n<h3>18.2.1 The Form Tag Helper<\/h3>\n<p>18.2.1 Form \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<\/p>\n<p>The first thing you need to start building your HTML form is, unsurprisingly, the <code>&lt;form&gt;<\/code> element. In listing 18.3 the <code>&lt;form&gt;<\/code> element was augmented with an asp-page Tag Helper attribute:<br \/>\n\u6beb\u65e0\u7591\u95ee\uff0c\u5f00\u59cb\u6784\u5efa HTML \u8868\u5355\u9700\u8981\u505a\u7684\u7b2c\u4e00\u4ef6\u4e8b\u662f<code>&lt;form&gt;<\/code>\u5143\u7d20\u3002\u5728\u6e05\u5355 18.3 \u4e2d\uff0c\u8be5<code>&lt;form&gt;<\/code>\u5143\u7d20\u88ab\u6269\u5145\u4e86\u4e00\u4e2a asp-page Tag Helper \u5c5e\u6027\uff1a<\/p>\n<pre><code>&lt;form asp-page=&quot;Checkout&quot;&gt;<\/code><\/pre>\n<p>The Tag Helper adds action and method attributes to the final HTML, indicating which URL the form should be sent to when it\u2019s submitted and the HTTP verb to use:<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5c06 action \u548c method \u5c5e\u6027\u6dfb\u52a0\u5230\u6700\u7ec8\u7684 HTML \u4e2d\uff0c\u6307\u793a\u8868\u5355\u5728\u63d0\u4ea4\u65f6\u5e94\u53d1\u9001\u5230\u54ea\u4e2a URL\uff0c\u4ee5\u53ca\u8981\u4f7f\u7528\u7684 HTTP \u52a8\u8bcd\uff1a<\/p>\n<pre><code>&lt;form action=&quot;\/Checkout&quot; method=&quot;post&quot;&gt;<\/code><\/pre>\n<p>Setting the asp-page attribute allows you to specify a different Razor Page in your application that the form will be posted to when it\u2019s submitted. If you omit the asp-page attribute, the form will post back to the same URL it was served from. This is common with Razor Pages. You normally handle the result of a form post in the same Razor Page that is used to display it.<br \/>\n\u8bbe\u7f6e asp-page \u5c5e\u6027\u540e\uff0c\u53ef\u4ee5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6307\u5b9a\u4e0d\u540c\u7684 Razor \u9875\u9762\uff0c\u8868\u5355\u5728\u63d0\u4ea4\u65f6\u5c06\u53d1\u5e03\u5230\u8be5\u9875\u9762\u3002\u5982\u679c\u7701\u7565 asp-page \u5c5e\u6027\uff0c\u8868\u5355\u5c06\u56de\u53d1\u5230\u63d0\u4f9b\u5b83\u7684\u540c\u4e00 URL\u3002\u8fd9\u5728 Razor Pages \u4e2d\u5f88\u5e38\u89c1\u3002\u901a\u5e38\u5728\u7528\u4e8e\u663e\u793a\u8868\u5355\u5e16\u5b50\u7684\u540c\u4e00 Razor Page \u4e2d\u5904\u7406\u8868\u5355\u5e16\u5b50\u7684\u7ed3\u679c\u3002<\/p>\n<p><b>Warning<\/b> If you omit the asp-page attribute, you must add the method=&quot;post&quot; attribute manually. It\u2019s important to add this attribute so the form is sent using the POST verb instead of the default GET verb. Using GET for forms can be a security risk.<br \/>\n\u8b66\u544a\uff1a\u5982\u679c\u7701\u7565 asp-page \u5c5e\u6027\uff0c\u5219\u5fc5\u987b\u624b\u52a8\u6dfb\u52a0 method=\u201cpost\u201d \u5c5e\u6027\u3002\u6dfb\u52a0\u6b64\u5c5e\u6027\u975e\u5e38\u91cd\u8981\uff0c\u4ee5\u4fbf\u4f7f\u7528 POST \u52a8\u8bcd\u800c\u4e0d\u662f\u9ed8\u8ba4\u7684 GET \u52a8\u8bcd\u53d1\u9001\u8868\u5355\u3002\u5bf9\u8868\u5355\u4f7f\u7528 GET \u53ef\u80fd\u4f1a\u5e26\u6765\u5b89\u5168\u98ce\u9669\u3002<\/p>\n<p>The asp-page attribute is added by a FormTagHelper. This Tag Helper uses the value provided to generate a URL for the action attribute, using the URL generation features of routing that I described in chapters 5 and 14.<br \/>\nasp-page \u5c5e\u6027\u7531 FormTagHelper \u6dfb\u52a0\u3002\u6b64 Tag Helper \u4f7f\u7528\u63d0\u4f9b\u7684\u503c\u4e3a action \u5c5e\u6027\u751f\u6210 URL\uff0c\u4f7f\u7528\u6211\u5728\u7b2c 5 \u7ae0\u548c\u7b2c 14 \u7ae0\u4e2d\u63cf\u8ff0\u7684\u8def\u7531\u7684 URL \u751f\u6210\u529f\u80fd\u3002<\/p>\n<p><b>NOTE<\/b> Tag Helpers can make multiple attributes available on an element. Think of them like properties on a Tag Helper configuration object. Adding a single asp- attribute activates the Tag Helper on the element. Adding more attributes lets you override further default values of its implementation.<br \/>\n\u6ce8\u610f\uff1a\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u4ee5\u5728\u4e00\u4e2a\u5143\u7d20\u4e0a\u63d0\u4f9b\u591a\u4e2a\u5c5e\u6027\u3002\u5c06\u5b83\u4eec\u89c6\u4e3a Tag Helper \u914d\u7f6e\u5bf9\u8c61\u4e0a\u7684\u5c5e\u6027\u3002\u6dfb\u52a0\u5355\u4e2a asp- \u5c5e\u6027\u4f1a\u6fc0\u6d3b\u5143\u7d20\u4e0a\u7684 Tag Helper\u3002\u6dfb\u52a0\u66f4\u591a\u5c5e\u6027\u53ef\u8ba9\u60a8\u8986\u76d6\u5176\u5b9e\u73b0\u7684\u66f4\u591a\u9ed8\u8ba4\u503c\u3002<\/p>\n<p>The Form Tag Helper makes several other attributes available on the <code>&lt;form&gt;<\/code> element that you can use to customize the generated URL. I hope you\u2019ll remember that you can set route values when generating URLs. For example, if you have a Razor Page called Product.cshtml that uses the directive<br \/>\nForm Tag Helper \u5728 <code>&lt;form&gt;<\/code> \u5143\u7d20\u4e0a\u63d0\u4f9b\u4e86\u51e0\u4e2a\u5176\u4ed6\u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u5c5e\u6027\u6765\u81ea\u5b9a\u4e49\u751f\u6210\u7684 URL\u3002\u6211\u5e0c\u671b\u60a8\u4f1a\u8bb0\u4f4f\uff0c\u60a8\u53ef\u4ee5\u5728\u751f\u6210 URL \u65f6\u8bbe\u7f6e\u8def\u7531\u503c\u3002\u4f8b\u5982\uff0c\u5982\u679c\u4f60\u6709\u4e00\u4e2a\u540d\u4e3a Product.cshtml \u7684 Razor \u9875\u9762\uff0c\u8be5\u9875\u9762\u4f7f\u7528\u6307\u4ee4<\/p>\n<pre><code>@page &quot;{id}&quot;<\/code><\/pre>\n<p>the full route template for the page would be &quot;Product\/{id}&quot;. To generate the URL for this page correctly, you must provide the {id} route value. How can you set that value using the Form Tag Helper?<br \/>\n\u9875\u9762\u7684\u5b8c\u6574\u8def\u7531\u6a21\u677f\u5c06\u4e3a \u201cProduct\/{id}\u201d\u3002\u8981\u6b63\u786e\u751f\u6210\u6b64\u9875\u9762\u7684 URL\uff0c\u60a8\u5fc5\u987b\u63d0\u4f9b {id} \u8def\u7531\u503c\u3002\u5982\u4f55\u4f7f\u7528 Form Tag Helper \u8bbe\u7f6e\u8be5\u503c\uff1f<\/p>\n<p>The Form Tag Helper defines an asp-route-<em> wildcard attribute that you can use to set arbitrary route parameters. Set the <\/em> in the attribute to the route parameter name. For example, to set the id route parameter, you\u2019d set the asp-route-id value. If the ProductId property of your PageModel contains the id value required, you could use:<br \/>\n\u8868\u5355\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5b9a\u4e49\u53ef\u7528\u4e8e\u8bbe\u7f6e\u4efb\u610f\u8def\u7531\u53c2\u6570\u7684 asp-route- \u901a\u914d\u7b26\u5c5e\u6027\u3002\u5c06 in \u5c5e\u6027\u8bbe\u7f6e\u4e3a\u8def\u7531\u53c2\u6570\u540d\u79f0\u3002\u4f8b\u5982\uff0c\u8981\u8bbe\u7f6e id \u8def\u7531\u53c2\u6570\uff0c\u60a8\u9700\u8981\u8bbe\u7f6e asp-route-id \u503c\u3002\u5982\u679c PageModel \u7684 ProductId \u5c5e\u6027\u5305\u542b\u6240\u9700\u7684 id \u503c\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\uff1a<\/p>\n<pre><code>&lt;form asp-page=&quot;Product&quot; asp-route-id=&quot;@Model.ProductId&quot;&gt;<\/code><\/pre>\n<p>Based on the route template of the Product.cshtml Razor Page (and assuming ProductId=5 in this example), this would generate the following markup:<br \/>\n\u6839\u636e Product.cshtml Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\uff08\u5728\u6b64\u793a\u4f8b\u4e2d\u5047\u8bbe ProductId=5\uff09\uff0c\u8fd9\u5c06\u751f\u6210\u4ee5\u4e0b\u6807\u8bb0\uff1a<\/p>\n<pre><code>&lt;form action=&quot;\/Product\/5&quot; method=&quot;post&quot;&gt;<\/code><\/pre>\n<p>You can add as many asp-route-* attributes as necessary to your <code>&lt;form&gt;<\/code> to generate the correct action URL. You can also set the Razor Page handler to use the asp-page-handler attribute. This ensures that the form POST will be handled by the handler you specify.<\/p>\n<p>\u60a8\u53ef\u4ee5\u6839\u636e\u9700\u8981\u5c06\u4efb\u610f\u6570\u91cf\u7684 asp-route-* \u5c5e\u6027\u6dfb\u52a0\u5230 <code>&lt;form&gt;<\/code> \u4ee5\u751f\u6210\u6b63\u786e\u7684\u4f5c URL\u3002\u60a8\u8fd8\u53ef\u4ee5\u5c06 Razor Page \u5904\u7406\u7a0b\u5e8f\u8bbe\u7f6e\u4e3a\u4f7f\u7528 asp-page-handler \u5c5e\u6027\u3002\u8fd9\u53ef\u786e\u4fdd\u8868\u5355 POST \u5c06\u7531\u60a8\u6307\u5b9a\u7684\u5904\u7406\u7a0b\u5e8f\u5904\u7406\u3002<\/p>\n<p><b>NOTE<\/b> The Form Tag Helper has many additional attributes, such as asp-action and asp-controller, that you generally won\u2019t use with Razor Pages. Those are useful only if you\u2019re using MVC controllers with views. In particular, look out for the asp-route attribute\u2014this is not the same as the asp-route-<em> attribute. The former is used to specify a named route (such as a named minimal API endpoint), and the latter is used to specify the route values to use during URL generation.<br \/>\n\u6ce8\u610f:\u8868\u5355\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5177\u6709\u8bb8\u591a\u5176\u4ed6\u5c5e\u6027\uff0c\u4f8b\u5982 asp-action \u548c asp-controller\uff0c\u8fd9\u4e9b\u5c5e\u6027\u901a\u5e38\u4e0d\u4f1a\u4e0e Razor Pages \u4e00\u8d77\u4f7f\u7528\u3002\u4ec5\u5f53\u60a8\u5c06 MVC \u63a7\u5236\u5668\u4e0e\u89c6\u56fe\u4e00\u8d77\u4f7f\u7528\u65f6\uff0c\u8fd9\u4e9b\u624d\u6709\u7528\u3002\u7279\u522b\u662f\uff0c\u8bf7\u6ce8\u610f asp-route \u5c5e\u6027 \u2014 \u8fd9\u4e0e asp-route-<\/em> \u5c5e\u6027\u4e0d\u540c\u3002\u524d\u8005\u7528\u4e8e\u6307\u5b9a\u547d\u540d\u8def\u7531\uff08\u4f8b\u5982\u547d\u540d\u7684\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\uff09\uff0c\u540e\u8005\u7528\u4e8e\u6307\u5b9a\u5728 URL \u751f\u6210\u671f\u95f4\u8981\u4f7f\u7528\u7684\u8def\u7531\u503c\u3002<\/p>\n<p>The main job of the Form Tag Helper is to generate the action attribute, but it performs one additional important function: generating a hidden <code>&lt;input&gt;<\/code> field needed to prevent cross-site request forgery (CSRF) attacks.<br \/>\nForm Tag Helper \u7684\u4e3b\u8981\u5de5\u4f5c\u662f\u751f\u6210 action \u5c5e\u6027\uff0c\u4f46\u5b83\u6267\u884c\u4e00\u9879\u989d\u5916\u7684\u91cd\u8981\u529f\u80fd\uff1a\u751f\u6210\u9632\u6b62\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08CSRF\uff09 \u653b\u51fb\u6240\u9700\u7684\u9690\u85cf<code>&lt;input&gt;<\/code> \u5b57\u6bb5\u3002<\/p>\n<p><b>DEFINITION<\/b> Cross-site request forgery (CSRF) attacks are a website exploit that can allow actions to be executed on your website by an unrelated malicious website. You\u2019ll learn about them in detail in chapter 29.<br \/>\n\u5b9a\u4e49:\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08CSRF\uff09 \u653b\u51fb\u662f\u4e00\u79cd\u7f51\u7ad9\u6f0f\u6d1e\uff0c\u53ef\u4ee5\u5141\u8bb8\u4e0d\u76f8\u5173\u7684\u6076\u610f\u7f51\u7ad9\u5728\u60a8\u7684\u7f51\u7ad9\u4e0a\u6267\u884c\u4f5c\u3002\u60a8\u5c06\u5728\u7b2c 29 \u7ae0\u4e2d\u8be6\u7ec6\u4e86\u89e3\u5b83\u4eec\u3002<\/p>\n<p>You can see the generated hidden <code>&lt;input&gt;<\/code> at the bottom of the <code>&lt;form&gt;<\/code> in listing 18.4; it\u2019s named <strong>RequestVerificationToken and contains a seemingly random string of characters. This field won\u2019t protect you on its own, but I\u2019ll describe in chapter 29 how it\u2019s used to protect your website. The Form Tag Helper generates it by default, so you generally won\u2019t need to worry about it, but if you need to disable it, you can do so by adding asp-antiforgery=&quot;false&quot; to your <code>&lt;form&gt;<\/code> element.<br \/>\n\u4f60\u53ef\u4ee5\u5728\u6e05\u5355 18.4 \u7684 <code>&lt;form&gt;<\/code> \u5e95\u90e8\u770b\u5230\u751f\u6210\u9690\u85cf<code>&lt;input&gt;<\/code> ;\u5b83\u88ab\u547d\u540d\u4e3a <\/strong>RequestVerificationToken \u5e76\u5305\u542b\u4e00\u4e2a\u770b\u4f3c\u968f\u673a\u7684\u5b57\u7b26\u4e32\u3002\u6b64\u5b57\u6bb5\u672c\u8eab\u4e0d\u4f1a\u4fdd\u62a4\u60a8\uff0c\u4f46\u6211\u5c06\u5728\u7b2c 29 \u7ae0\u4e2d\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528\u5b83\u6765\u4fdd\u62a4\u60a8\u7684\u7f51\u7ad9\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cForm Tag Helper \u4f1a\u751f\u6210\u5b83\uff0c\u56e0\u6b64\u60a8\u901a\u5e38\u65e0\u9700\u62c5\u5fc3\u5b83\uff0c\u4f46\u5982\u679c\u60a8\u9700\u8981\u7981\u7528\u5b83\uff0c\u53ef\u4ee5\u901a\u8fc7\u5c06 asp-antiforgery=\u201cfalse\u201d \u6dfb\u52a0\u5230\u60a8\u7684<code>&lt;form&gt;<\/code> \u5143\u7d20\u6765\u5b9e\u73b0\u3002<\/p>\n<p>The Form Tag Helper is obviously useful for generating the action URL, but it\u2019s time to move on to more interesting elements\u2014those that you can see in your browser!<br \/>\n\u8868\u5355\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u663e\u7136\u53ef\u7528\u4e8e\u751f\u6210\u4f5c URL\uff0c\u4f46\u73b0\u5728\u662f\u65f6\u5019\u8f6c\u5411\u66f4\u6709\u8da3\u7684\u5143\u7d20\u4e86 \u2014 \u60a8\u53ef\u4ee5\u5728\u6d4f\u89c8\u5668\u4e2d\u770b\u5230\u7684\u5143\u7d20\uff01<\/p>\n<h3>18.2.2 The Label Tag Helper<\/h3>\n<p>18.2.2 \u6807\u7b7e\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f<\/p>\n<p>Every <code>&lt;input&gt;<\/code> field in your currency converter application needs to have an associated label so the user knows what the <code>&lt;input&gt;<\/code> is for. You could easily create those yourself, manually typing the name of the field and setting the for attribute as appropriate, but luckily there\u2019s a Tag Helper to do that for you.<br \/>\n\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6bcf\u4e2a <code>&lt;input&gt;<\/code> \u5b57\u6bb5\u90fd\u9700\u8981\u6709\u4e00\u4e2a\u5173\u8054\u7684\u6807\u7b7e\uff0c\u4ee5\u4fbf\u7528\u6237\u77e5\u9053 for what for what.\u60a8\u53ef\u4ee5\u8f7b\u677e\u5730\u81ea\u5df1\u521b\u5efa\u8fd9\u4e9b\u6807\u8bb0\uff0c\u624b\u52a8\u952e\u5165\u5b57\u6bb5\u7684\u540d\u79f0\u5e76\u6839\u636e\u9700\u8981\u8bbe\u7f6e <code>&lt;input&gt;<\/code> for \u5c5e\u6027\uff0c\u4f46\u5e78\u8fd0\u7684\u662f\uff0c\u6709\u4e00\u4e2a Tag Helper \u53ef\u4ee5\u4e3a\u60a8\u6267\u884c\u6b64\u4f5c\u3002<\/p>\n<p>The Label Tag Helper is used to generate the caption (the visible text) and the for attribute for a <code>&lt;label&gt;<\/code> element, based on the properties in the PageModel. It\u2019s used by providing the name of the property in the asp-for attribute:<br \/>\nLabel Tag Helper \u7528\u4e8e\u6839\u636e PageModel \u4e2d\u7684\u5c5e\u6027\u4e3a<code>&lt;label&gt;<\/code> \u5143\u7d20\u751f\u6210\u6807\u9898\uff08\u53ef\u89c1\u6587\u672c\uff09\u548c for \u5c5e\u6027\u3002\u901a\u8fc7\u5728 asp-for \u5c5e\u6027\u4e2d\u63d0\u4f9b\u5c5e\u6027\u7684\u540d\u79f0\u6765\u4f7f\u7528\u5b83\uff1a<\/p>\n<pre><code>&lt;label asp-for=&quot;FirstName&quot;&gt;&lt;\/label&gt;<\/code><\/pre>\n<p>The Label Tag Helper uses the [Display] DataAnnotations attribute that you saw in chapter 16 to determine the appropriate value to display. If the property you\u2019re generating a label for doesn\u2019t have a [Display] attribute, the Label Tag Helper uses the name of the property instead. Consider this model in which the FirstName property has a [Display] attribute, but the Email property doesn\u2019t:<br \/>\nLabel Tag Helper \u4f7f\u7528\u60a8\u5728\u7b2c 16 \u7ae0\u4e2d\u770b\u5230\u7684 [Display] DataAnnotations \u5c5e\u6027\u6765\u786e\u5b9a\u8981\u663e\u793a\u7684\u9002\u5f53\u503c\u3002\u5982\u679c\u8981\u4e3a\u5176\u751f\u6210\u6807\u7b7e\u7684\u5c5e\u6027\u6ca1\u6709 [Display] \u5c5e\u6027\uff0c\u5219 Label Tag Helper \u4f1a\u6539\u7528\u8be5\u5c5e\u6027\u7684\u540d\u79f0\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u6a21\u578b\uff1aFirstName \u5c5e\u6027\u5177\u6709 [Display] \u5c5e\u6027\uff0c\u4f46 Email \u5c5e\u6027\u6ca1\u6709\uff1a<\/p>\n<pre><code>public class UserModel\n{\n    [Display(Name = &quot;Your name&quot;)]\n    public string FirstName { get; set; }\n    public string Email { get; set; }\n}<\/code><\/pre>\n<p>The following Razor<br \/>\n\u4ee5\u4e0b Razor<\/p>\n<pre><code>&lt;label asp-for=&quot;FirstName&quot;&gt;&lt;\/label&gt;\n&lt;label asp-for=&quot;Email&quot;&gt;&lt;\/label&gt;<\/code><\/pre>\n<p>would generate this HTML:<br \/>\n\u5c06\u751f\u6210\u6b64 HTML\uff1a<\/p>\n<pre><code>&lt;label for=&quot;FirstName&quot;&gt;Your name&lt;\/label&gt;\n&lt;label for=&quot;Email&quot;&gt;Email&lt;\/label&gt;<\/code><\/pre>\n<p>The inner text inside the <code>&lt;label&gt;<\/code> element uses the value set in the [Display] attribute, or the property name in the case of the Email property. Also note that the for attribute has been generated with the name of the property. This is a key bonus of using Tag Helpers; it hooks in with the element IDs generated by other Tag Helpers, as you\u2019ll see shortly.<br \/>\n<code>&lt;label&gt;<\/code> \u5143\u7d20\u5185\u90e8\u6587\u672c\u4f7f\u7528 [Display] \u5c5e\u6027\u4e2d\u8bbe\u7f6e\u7684\u503c\uff0c\u6216\u8005\u4f7f\u7528 Email \u5c5e\u6027\u7684\u5c5e\u6027\u540d\u79f0\u3002\u53e6\u8bf7\u6ce8\u610f\uff0c\u5df2\u4f7f\u7528\u5c5e\u6027\u540d\u79f0\u751f\u6210 for \u5c5e\u6027\u3002\u8fd9\u662f\u4f7f\u7528 Tag Helper \u7684\u4e00\u4e2a\u5173\u952e\u597d\u5904;\u5b83\u4e0e\u5176\u4ed6 Tag Helper \u751f\u6210\u7684\u5143\u7d20 ID \u6302\u94a9\uff0c\u60a8\u5f88\u5feb\u5c31\u4f1a\u770b\u5230\u3002<\/p>\n<p><b>NOTE<\/b> The for attribute is important for accessibility. It specifies the ID of the element to which the label refers. This is important for users who are using a screen reader, for example, as they can tell what property a form field relates to.<br \/>\n\u6ce8\u610f\uff1afor \u5c5e\u6027\u5bf9\u4e8e\u8f85\u52a9\u529f\u80fd\u975e\u5e38\u91cd\u8981\u3002\u5b83\u6307\u5b9a\u6807\u7b7e\u6240\u5f15\u7528\u7684\u5143\u7d20\u7684 ID\u3002\u4f8b\u5982\uff0c\u8fd9\u5bf9\u4e8e\u4f7f\u7528\u5c4f\u5e55\u9605\u8bfb\u5668\u7684\u7528\u6237\u6765\u8bf4\u975e\u5e38\u91cd\u8981\uff0c\u56e0\u4e3a\u4ed6\u4eec\u53ef\u4ee5\u5224\u65ad\u8868\u5355\u5b57\u6bb5\u4e0e\u54ea\u4e2a\u5c5e\u6027\u76f8\u5173\u3002<\/p>\n<p>As well as properties on the PageModel, you can also reference sub-properties on child objects. For example, as I described in chapter 16, it\u2019s common to create a nested class in a Razor Page, expose that as a property, and decorate it with the [BindProperty] attribute:<br \/>\n\u9664\u4e86 PageModel \u4e0a\u7684\u5c5e\u6027\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u5f15\u7528\u5b50\u5bf9\u8c61\u4e0a\u7684\u5b50\u5c5e\u6027\u3002\u4f8b\u5982\uff0c\u6b63\u5982\u6211\u5728\u7b2c 16 \u7ae0\u4e2d\u6240\u63cf\u8ff0\u7684\uff0c\u5728 Razor Page \u4e2d\u521b\u5efa\u4e00\u4e2a\u5d4c\u5957\u7c7b\uff0c\u5c06\u5176\u4f5c\u4e3a\u5c5e\u6027\u516c\u5f00\uff0c\u5e76\u4f7f\u7528 [BindProperty] \u5c5e\u6027\u5bf9\u5176\u8fdb\u884c\u4fee\u9970\u662f\u5f88\u5e38\u89c1\u7684\uff1a<\/p>\n<pre><code>public class CheckoutModel: PageModel\n{\n    [BindProperty]\n    public UserBindingModel Input { get; set; }\n}<\/code><\/pre>\n<p>You can reference the FirstName property of the UserBindingModel by \u201cdotting\u201d into the property as you would in any other C# code. Listing 18.3 shows more examples of this.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u5c5e\u6027\u4e2d\u201c\u70b9\u201d\u6765\u5f15\u7528 UserBindingModel \u7684 FirstName \u5c5e\u6027\uff0c\u5c31\u50cf\u5728\u4efb\u4f55\u5176\u4ed6 C# \u4ee3\u7801\u4e2d\u4e00\u6837\u3002\u6e05\u5355 18.3 \u663e\u793a\u4e86\u66f4\u591a\u8fd9\u6837\u7684\u4f8b\u5b50\u3002<\/p>\n<pre><code>&lt;label asp-for=&quot;Input.FirstName&quot;&gt;&lt;\/label&gt;\n&lt;label asp-for=&quot;Input.Email&quot;&gt;&lt;\/label&gt;<\/code><\/pre>\n<p>As is typical with Tag Helpers, the Label Tag Helper won\u2019t override values that you set yourself. If, for example, you don\u2019t want to use the caption generated by the helper, you could insert your own manually. The code<br \/>\n\u4e0e\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u7684\u5178\u578b\u60c5\u51b5\u4e00\u6837\uff0c\u6807\u7b7e\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u4e0d\u4f1a\u8986\u76d6\u60a8\u81ea\u5df1\u8bbe\u7f6e\u7684\u503c\u3002\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u4e0d\u60f3\u4f7f\u7528\u5e2e\u52a9\u7a0b\u5e8f\u751f\u6210\u7684\u6807\u9898\uff0c\u5219\u53ef\u4ee5\u624b\u52a8\u63d2\u5165\u81ea\u5df1\u7684\u6807\u9898\u3002\u4ee3\u7801\uff1a<\/p>\n<pre><code>&lt;label asp-for=&quot;Email&quot;&gt;Please enter your Email&lt;\/label&gt;<\/code><\/pre>\n<p>would generate this HTML:<br \/>\n\u5c06\u751f\u6210\u6b64 HTML\uff1a<\/p>\n<pre><code>&lt;label for=&quot;Email&quot;&gt;Please enter your Email&lt;\/label&gt;<\/code><\/pre>\n<p>As ever, you\u2019ll generally have an easier time with maintenance if you stick to the standard conventions and don\u2019t override values like this, but the option is there. Next up is a biggie: the Input and Textarea Tag Helpers.<br \/>\n\u4e0e\u5f80\u5e38\u4e00\u6837\uff0c\u5982\u679c\u60a8\u575a\u6301\u6807\u51c6\u7ea6\u5b9a\u5e76\u4e14\u4e0d\u8986\u76d6\u8fd9\u6837\u7684\u503c\uff0c\u60a8\u901a\u5e38\u4f1a\u66f4\u8f7b\u677e\u5730\u8fdb\u884c\u7ef4\u62a4\uff0c\u4f46\u9009\u9879\u5c31\u5728\u90a3\u91cc\u3002\u63a5\u4e0b\u6765\u662f\u4e00\u4e2a\u5927\u95ee\u9898\uff1aInput \u548c Textarea \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u3002<\/p>\n<h3>18.2.3 The Input and Textarea Tag Helpers<\/h3>\n<p>18.2.3 input \u548c textarea \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<\/p>\n<p>Now you\u2019re getting into the meat of your form: the <code>&lt;input&gt;<\/code> elements that handle user input. Given that there\u2019s such a wide array of possible input types, there\u2019s a variety of ways they can be displayed in the browser. For example, Boolean values are typically represented by a checkbox type <code>&lt;input&gt;<\/code> element, whereas integer values would use a number type <code>&lt;input&gt;<\/code> element, and a date would use the date type, as shown in figure 18.5.<br \/>\n\u73b0\u5728\uff0c\u4f60\u8fdb\u5165\u4e86\u8868\u5355\u7684\u6838\u5fc3\uff1a\u5904\u7406\u7528\u6237\u8f93\u5165\u7684 <code>&lt;input&gt;<\/code>\u5143\u7d20\u3002\u9274\u4e8e\u53ef\u80fd\u7684\u8f93\u5165\u7c7b\u578b\u5982\u6b64\u5e7f\u6cdb\uff0c\u5b83\u4eec\u5728\u6d4f\u89c8\u5668\u4e2d\u7684\u663e\u793a\u65b9\u5f0f\u591a\u79cd\u591a\u6837\u3002\u4f8b\u5982\uff0c\u5e03\u5c14\u503c\u901a\u5e38\u7531\u590d\u9009\u6846\u7c7b\u578b <code>&lt;input&gt;<\/code>\u5143\u7d20\u8868\u793a\uff0c\u800c\u6574\u6570\u503c\u5c06\u4f7f\u7528\u6570\u5b57\u7c7b\u578b <code>&lt;input&gt;<\/code>\u5143\u7d20\uff0c\u65e5\u671f\u5c06\u4f7f\u7528\u65e5\u671f\u7c7b\u578b\uff0c\u5982\u56fe 18.5 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1805.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.5 Various input element types. The exact way in which each type is displayed varies by browser.<br \/>\n\u56fe 18.5 \u5404\u79cd\u8f93\u5165\u5143\u7d20\u7c7b\u578b\u3002\u6bcf\u79cd\u7c7b\u578b\u7684\u786e\u5207\u663e\u793a\u65b9\u5f0f\u56e0\u6d4f\u89c8\u5668\u800c\u5f02\u3002<\/p>\n<p>To handle this diversity, the Input Tag Helper is one of the most powerful Tag Helpers. It uses information based on both the type of the property (bool, string, int, and so on) and any DataAnnotations attributes applied to it ([EmailAddress] and [Phone], among others) to determine the type of the input element to generate. The DataAnnotations are also used to add data-val-<em> client-side validation attributes to the generated HTML.<br \/>\n\u4e3a\u4e86\u5904\u7406\u8fd9\u79cd\u591a\u6837\u6027\uff0cInput Tag Helper \u662f\u6700\u5f3a\u5927\u7684 Tag Helper \u4e4b\u4e00\u3002\u5b83\u4f7f\u7528\u57fa\u4e8e\u5c5e\u6027\u7c7b\u578b\uff08bool\u3001string\u3001int \u7b49\uff09\u548c\u5e94\u7528\u4e8e\u5b83\u7684\u4efb\u4f55 DataAnnotations \u5c5e\u6027\uff08[EmailAddress] \u548c [Phone] \u7b49\uff09\u7684\u4fe1\u606f\u6765\u786e\u5b9a\u8981\u751f\u6210\u7684\u8f93\u5165\u5143\u7d20\u7684\u7c7b\u578b\u3002DataAnnotations \u8fd8\u7528\u4e8e\u5c06 data-val-<\/em> \u5ba2\u6237\u7aef\u9a8c\u8bc1\u5c5e\u6027\u6dfb\u52a0\u5230\u751f\u6210\u7684 HTML \u4e2d\u3002<\/p>\n<p>Consider the Email property from listing 18.2 that was decorated with the [EmailAddress] attribute. Adding an <code>&lt;input&gt;<\/code> is as simple as using the asp-for attribute:<br \/>\n\u8bf7\u8003\u8651\u6e05\u5355 18.2 \u4e2d\u7684 Email \u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u4f7f\u7528 [EmailAddress] \u5c5e\u6027\u8fdb\u884c\u4fee\u9970\u3002\u6dfb\u52a0 <code>&lt;input&gt;<\/code>\u5c31\u50cf\u4f7f\u7528 asp-for \u5c5e\u6027\u4e00\u6837\u7b80\u5355\uff1a<\/p>\n<pre><code>&lt;input asp-for=&quot;Input.Email&quot; \/&gt;<\/code><\/pre>\n<p>The property is a string, so ordinarily the Input Tag Helper would generate an <code>&lt;input&gt;<\/code> with type=&quot;text&quot;. But the addition of the [EmailAddress] attribute provides additional metadata about the property. Consequently, the Tag Helper generates an HTML5 <code>&lt;input&gt;<\/code> with type=&quot;email&quot;:<br \/>\n\u8be5\u5c5e\u6027\u662f\u4e00\u4e2a\u5b57\u7b26\u4e32\uff0c\u56e0\u6b64\u901a\u5e38 Input Tag Helper \u4f1a\u751f\u6210\u4e00\u4e2a\u5e26\u6709 type=\u201ctext\u201d \u7684  <code>&lt;input&gt;<\/code> \u3002\u4f46\u662f\uff0c\u6dfb\u52a0 [EmailAddress] \u5c5e\u6027\u4f1a\u63d0\u4f9b\u6709\u5173\u5c5e\u6027\u7684\u5176\u4ed6\u5143\u6570\u636e\u3002\u56e0\u6b64\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4f1a\u751f\u6210\u4e00\u4e2a type=\u201cemail\u201d \u7684 HTML5 <code>&lt;input&gt;<\/code> \uff1a<\/p>\n<pre><code>&lt;input type=&quot;email&quot; id=&quot;Input_Email&quot; name=&quot;Input.Email&quot;\n    value=&quot;test@example.com&quot; data-val=&quot;true&quot;\n    data-val-email=&quot;The Email Address field is not a valid e-mail address.&quot;\n    Data-val-required=&quot;The Email Address field is required.&quot;\n    \/&gt;<\/code><\/pre>\n<p>You can take a whole host of things away from this example. First, the id and name attributes of the HTML element have been generated from the name of the property. The value of the id attribute matches the value generated by the Label Tag Helper in its for attribute, Input_Email. The value of the name attribute preserves the \u201cdot\u201d notation, Input.Email, so that model binding works correctly when the field is POSTed to the Razor Page.<br \/>\n\u60a8\u53ef\u4ee5\u4ece\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\u5b66\u5230\u5f88\u591a\u4e1c\u897f\u3002\u9996\u5148\uff0cHTML \u5143\u7d20\u7684 id \u548c name \u5c5e\u6027\u662f\u4ece\u5c5e\u6027\u7684\u540d\u79f0\u751f\u6210\u7684\u3002id \u5c5e\u6027\u7684\u503c\u4e0e Label Tag Helper \u5728\u5176 for \u5c5e\u6027 Input_Email \u4e2d\u751f\u6210\u7684\u503c\u5339\u914d\u3002Input.Email\uff0cname \u5c5e\u6027\u7684\u503c\u4fdd\u7559\u201c\u70b9\u201d\u8868\u793a\u6cd5\uff0c\u4ee5\u4fbf\u5728\u5c06\u5b57\u6bb5\u53d1\u5e03\u5230 Razor \u9875\u9762\u65f6\uff0c\u6a21\u578b\u7ed1\u5b9a\u6b63\u5e38\u5de5\u4f5c\u3002<\/p>\n<p>Also, the initial value of the field has been set to the value currently stored in the property (&quot;test@example.com&quot;, in this case). The type of the element has also been set to the HTML5 email type, instead of using the default text type.<br \/>\n\u6b64\u5916\uff0c\u5b57\u6bb5\u7684\u521d\u59cb\u503c\u5df2\u8bbe\u7f6e\u4e3a\u5f53\u524d\u5b58\u50a8\u5728\u5c5e\u6027\u4e2d\u7684\u503c\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a\u201ctest@example.com\u201d\uff09\u3002\u5143\u7d20\u7684\u7c7b\u578b\u4e5f\u5df2\u8bbe\u7f6e\u4e3a HTML5 \u7535\u5b50\u90ae\u4ef6\u7c7b\u578b\uff0c\u800c\u4e0d\u662f\u4f7f\u7528\u9ed8\u8ba4\u6587\u672c\u7c7b\u578b\u3002<\/p>\n<p>Perhaps the most striking addition is the swath of data-val-<em> attributes. These can be used by client-side JavaScript libraries such as jQuery to provide client-side validation of your DataAnnotations constraints. Client-side validation provides instant feedback to users when the values they enter are invalid, providing a smoother user experience than can be achieved with server-side validation alone, as I described in chapter 16.<br \/>\n\u4e5f\u8bb8\u6700\u5f15\u4eba\u6ce8\u76ee\u7684\u65b0\u589e\u529f\u80fd\u662f\u5927\u91cf\u7684 data-val-<\/em> \u5c5e\u6027\u3002\u5ba2\u6237\u7aef JavaScript \u5e93\uff08\u5982 jQuery\uff09\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u7ea6\u675f\u6765\u63d0\u4f9b DataAnnotations \u7ea6\u675f\u7684\u5ba2\u6237\u7aef\u9a8c\u8bc1\u3002\u5ba2\u6237\u7aef\u9a8c\u8bc1\u4f1a\u5728\u7528\u6237\u8f93\u5165\u7684\u503c\u65e0\u6548\u65f6\u5411\u7528\u6237\u63d0\u4f9b\u5373\u65f6\u53cd\u9988\uff0c\u4ece\u800c\u63d0\u4f9b\u6bd4\u5355\u72ec\u4f7f\u7528\u670d\u52a1\u5668\u7aef\u9a8c\u8bc1\u66f4\u6d41\u7545\u7684\u7528\u6237\u4f53\u9a8c\uff0c\u5982\u6211\u5728\u7b2c 16 \u7ae0\u4e2d\u6240\u8ff0\u3002<\/p>\n<blockquote>\n<p>Client-side validation<br \/>\n\u5ba2\u6237\u7aef\u9a8c\u8bc1<\/p>\n<p>To enable client-side validation in your application, you need to add some jQuery libraries to your HTML pages. In particular, you need to include the jQuery, jQuery-validation, and jQuery-validation-unobtrusive JavaScript libraries. You can do this in several ways, but the simplest is to include the script files at the bottom of your view using<br \/>\n\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u542f\u7528\u5ba2\u6237\u7aef\u9a8c\u8bc1\uff0c\u60a8\u9700\u8981\u5411 HTML \u9875\u9762\u6dfb\u52a0\u4e00\u4e9b jQuery \u5e93\u3002\u7279\u522b\u662f\uff0c\u60a8\u9700\u8981\u5305\u62ec jQuery\u3001jQuery-validation \u548c jQuery-validation-unobtrusive JavaScript \u5e93\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u6267\u884c\u6b64\u4f5c\uff0c\u4f46\u6700\u7b80\u5355\u7684\u65b9\u6cd5\u662f\u4f7f\u7528<\/p>\n<\/blockquote>\n<pre><code>&lt;script src=&quot;~\/lib\/jquery-validation\/dist\/jquery.validate.min.js&quot;&gt;&lt;\/script&gt;\n&lt;script src=&quot;~\/lib\/jquery-validation-unobtrusive\/jquery.validate.unobtrusive.min.js&quot;&gt;&lt;\/script&gt;<\/code><\/pre>\n<blockquote>\n<p>The default templates include these scripts for you in a handy partial template that you can add to your page in a Scripts section. If you\u2019re using the default layout and need to add client-side validation to your view, add the following section somewhere on your view:<br \/>\n\u9ed8\u8ba4\u6a21\u677f\u5c06\u8fd9\u4e9b\u811a\u672c\u5305\u542b\u5728\u4e00\u4e2a\u65b9\u4fbf\u7684\u90e8\u5206\u6a21\u677f\u4e2d\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u6dfb\u52a0\u5230\u9875\u9762\u7684 Scripts \u90e8\u5206\u3002\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u9ed8\u8ba4\u5e03\u5c40\uff0c\u5e76\u4e14\u9700\u8981\u5411\u89c6\u56fe\u6dfb\u52a0\u5ba2\u6237\u7aef\u9a8c\u8bc1\uff0c\u8bf7\u5728\u89c6\u56fe\u4e0a\u7684\u67d0\u4e2a\u4f4d\u7f6e\u6dfb\u52a0\u4ee5\u4e0b\u90e8\u5206\uff1a<\/p>\n<\/blockquote>\n<pre><code>@section Scripts{\n    @Html.Partial(&quot;_ValidationScriptsPartial&quot;)\n}<\/code><\/pre>\n<blockquote>\n<p>This partial view references files in your wwwroot folder. The default layout template includes jQuery itself. If you don\u2019t need to use jQuery in your application, you may want to consider a small alternative validation library called aspnet-client-validation. I describe why you might consider this library and how to use it in this blog post: <a href=\"http:\/\/mng.bz\/V1pX\">http:\/\/mng.bz\/V1pX<\/a>.<br \/>\n\u6b64\u5206\u90e8\u89c6\u56fe\u5f15\u7528 wwwroot \u6587\u4ef6\u5939\u4e2d\u7684\u6587\u4ef6\u3002\u9ed8\u8ba4\u5e03\u5c40\u6a21\u677f\u5305\u62ec jQuery \u672c\u8eab\u3002\u5982\u679c\u60a8\u4e0d\u9700\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 jQuery\uff0c\u5219\u53ef\u80fd\u9700\u8981\u8003\u8651\u4e00\u4e2a\u540d\u4e3a aspnet-client-validation \u7684\u5c0f\u578b\u66ff\u4ee3\u9a8c\u8bc1\u5e93\u3002\u6211\u5728\u8fd9\u7bc7\u535a\u6587\u4e2d\u63cf\u8ff0\u4e86\u4e3a\u4ec0\u4e48\u4f1a\u8003\u8651\u4f7f\u7528\u8fd9\u4e2a\u5e93\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u5b83\uff1a<a href=\"http:\/\/mng.bz\/V1pX\">http:\/\/mng.bz\/V1pX<\/a>\u3002<\/p>\n<p>You can also load these files, whether you\u2019re using jQuery or aspnet-client-validation, from a content delivery network (CDN). If you want to take this approach, you should consider scenarios where the CDN is unavailable or compromised, as I discuss in this blog post: <a href=\"http:\/\/mng.bz\/2e6d\">http:\/\/mng.bz\/2e6d<\/a>.<br \/>\n\u60a8\u8fd8\u53ef\u4ee5\u4ece\u5185\u5bb9\u5206\u53d1\u7f51\u7edc \uff08CDN\uff09 \u52a0\u8f7d\u8fd9\u4e9b\u6587\u4ef6\uff0c\u65e0\u8bba\u60a8\u4f7f\u7528\u7684\u662f jQuery \u8fd8\u662f aspnet-client-validation\u3002\u5982\u679c\u60a8\u60f3\u91c7\u7528\u8fd9\u79cd\u65b9\u6cd5\uff0c\u60a8\u5e94\u8be5\u8003\u8651 CDN \u4e0d\u53ef\u7528\u6216\u53d7\u635f\u7684\u60c5\u51b5\uff0c\u6b63\u5982\u6211\u5728\u8fd9\u7bc7\u535a\u6587\u4e2d\u8ba8\u8bba\u7684\u90a3\u6837\uff1a<a href=\"http:\/\/mng.bz\/2e6d\">http:\/\/mng.bz\/2e6d<\/a>\u3002<\/p>\n<\/blockquote>\n<p>The Input Tag Helper tries to pick the most appropriate template for a given property based on DataAnnotations attributes or the type of the property. Whether this generates the exact <code>&lt;input&gt;<\/code> type you need may depend, to an extent, on your application. As always, you can override the generated type by adding your own type attribute to the element in your Razor template. Table 18.1 shows how some of the common data types are mapped to <code>&lt;input&gt;<\/code> types and how the data types themselves can be specified.<br \/>\n\u8f93\u5165\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5c1d\u8bd5\u6839\u636e DataAnnotations \u5c5e\u6027\u6216\u5c5e\u6027\u7c7b\u578b\u4e3a\u7ed9\u5b9a\u5c5e\u6027\u9009\u62e9\u6700\u5408\u9002\u7684\u6a21\u677f\u3002\u8fd9\u662f\u5426\u751f\u6210\u60a8\u9700\u8981\u7684\u786e\u5207\u7c7b\u578b\u53ef\u80fd\u5728\u4e00\u5b9a\u7a0b\u5ea6\u4e0a\u53d6\u51b3\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u4e0e\u5f80\u5e38\u4e00\u6837\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06\u81ea\u5df1\u7684 type \u5c5e\u6027\u6dfb\u52a0\u5230 Razor \u6a21\u677f\u4e2d\u7684 <code>&lt;input&gt;<\/code> \u5143\u7d20\u6765\u66ff\u4ee3\u751f\u6210\u7684\u7c7b\u578b\u3002Table 18.1 \u663e\u793a\u4e86\u5982\u4f55\u5c06\u4e00\u4e9b\u5e38\u89c1\u6570\u636e\u7c7b\u578b\u6620\u5c04\u5230\u7c7b\u578b\u4ee5\u53ca\u5982\u4f55\u6307\u5b9a\u6570\u636e\u7c7b\u578b\u672c\u8eab\u3002<\/p>\n<p>Table 18.1 Common data types, how to specify them, and the input element type they map to<br \/>\n\u8868 18.1 \u5e38\u89c1\u6570\u636e\u7c7b\u578b\u3001\u5982\u4f55\u6307\u5b9a\u5b83\u4eec\u4ee5\u53ca\u5b83\u4eec\u6620\u5c04\u5230\u7684\u8f93\u5165\u5143\u7d20\u7c7b\u578b<\/p>\n<table>\n<thead>\n<tr>\n<th>Data type<\/th>\n<th>How it\u2019s specified<\/th>\n<th>Input element type<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>byte, int, short, long, uint<\/td>\n<td>Property type<\/td>\n<td>number<\/td>\n<\/tr>\n<tr>\n<td>decimal, double, float<\/td>\n<td>Property type<\/td>\n<td>text<\/td>\n<\/tr>\n<tr>\n<td>bool<\/td>\n<td>Property type<\/td>\n<td>checkbox<\/td>\n<\/tr>\n<tr>\n<td>string<\/td>\n<td>Property type, [DataType(DataType.Text)] attribute<\/td>\n<td>text<\/td>\n<\/tr>\n<tr>\n<td>HiddenInput<\/td>\n<td>[HiddenInput] attribute<\/td>\n<td>hidden<\/td>\n<\/tr>\n<tr>\n<td>Password<\/td>\n<td>[Password] attribute<\/td>\n<td>password<\/td>\n<\/tr>\n<tr>\n<td>Phone<\/td>\n<td>[Phone] attribute<\/td>\n<td>tel<\/td>\n<\/tr>\n<tr>\n<td>EmailAddress<\/td>\n<td>[EmailAddress] attribute<\/td>\n<td>email<\/td>\n<\/tr>\n<tr>\n<td>Url<\/td>\n<td>[Url] attribute<\/td>\n<td>url<\/td>\n<\/tr>\n<tr>\n<td>Date<\/td>\n<td>DateTime property type, [DataType(DataType.Date)] attribute<\/td>\n<td>datetime-local<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The Input Tag Helper has one additional attribute that can be used to customize the way data is displayed: asp-format. HTML forms are entirely string-based, so when the value of an <code>&lt;input&gt;<\/code> is set, the Input Tag Helper must take the value stored in the property and convert it to a string. Under the covers, this performs a string.Format() on the property\u2019s value, passing in the format string.<br \/>\nInput Tag Helper \u5177\u6709\u4e00\u4e2a\u53ef\u7528\u4e8e\u81ea\u5b9a\u4e49\u6570\u636e\u663e\u793a\u65b9\u5f0f\u7684\u9644\u52a0\u5c5e\u6027\uff1aasp-format\u3002HTML \u8868\u5355\u5b8c\u5168\u57fa\u4e8e\u5b57\u7b26\u4e32\uff0c\u56e0\u6b64\u5728\u8bbe\u7f6e <code>&lt;input&gt;<\/code>  \u7684\u503c\u65f6\uff0cInput Tag Helper \u5fc5\u987b\u83b7\u53d6\u5b58\u50a8\u5728\u5c5e\u6027\u4e2d\u7684\u503c\u5e76\u5c06\u5176\u8f6c\u6362\u4e3a\u5b57\u7b26\u4e32\u3002\u5728\u540e\u53f0\uff0c\u8fd9\u5c06\u6267\u884c\u4e00\u4e2a\u5b57\u7b26\u4e32\u3002Format\uff08\uff09 \u5bf9\u5c5e\u6027\u7684\u503c\u6267\u884c\uff0c\u5e76\u4f20\u5165\u683c\u5f0f\u5b57\u7b26\u4e32\u3002<\/p>\n<p>The Input Tag Helper uses a default format string for each different data type, but with the asp-format attribute, you can set the specific format string to use. For example, you could ensure that a decimal property, Dec, is formatted to three decimal places with the following code:<br \/>\nInput Tag Helper \u5bf9\u6bcf\u79cd\u4e0d\u540c\u7684\u6570\u636e\u7c7b\u578b\u4f7f\u7528\u9ed8\u8ba4\u683c\u5f0f\u5b57\u7b26\u4e32\uff0c\u4f46\u4f7f\u7528 asp-format \u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u8bbe\u7f6e\u8981\u4f7f\u7528\u7684\u7279\u5b9a\u683c\u5f0f\u5b57\u7b26\u4e32\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u786e\u4fdd\u5c06 decimal \u5c5e\u6027 Dec \u7684\u683c\u5f0f\u8bbe\u7f6e\u4e3a\u4e09\u4f4d\u5c0f\u6570\uff1a<\/p>\n<pre><code>&lt;input asp-for=&quot;Dec&quot; asp-format=&quot;{0:0.000}&quot; \/&gt;<\/code><\/pre>\n<p>If the Dec property had a value of 1.2, this would generate HTML similar to<br \/>\n\u5982\u679c Dec \u5c5e\u6027\u7684\u503c\u4e3a 1.2\uff0c\u5219\u751f\u6210\u7c7b\u4f3c\u4e8e<\/p>\n<pre><code>&lt;input type=&quot;text&quot; id=&quot;Dec&quot; name=&quot;Dec&quot; value=&quot;1.200&quot;&gt;<\/code><\/pre>\n<p>Alternatively, you can define the format to use by adding the [DisplayFormat] attribute to the model property:<br \/>\n\u6216\u8005\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06 [DisplayFormat] \u5c5e\u6027\u6dfb\u52a0\u5230 model \u5c5e\u6027\u6765\u5b9a\u4e49\u8981\u4f7f\u7528\u7684\u683c\u5f0f\uff1a<\/p>\n<pre><code>[DisplayFormat(&quot;{0:0.000}&quot;)]\npublic decimal Dec { get; set; }<\/code><\/pre>\n<p><b>NOTE<\/b> You may be surprised that decimal and double types are rendered as text fields and not as number fields. This is due to several technical reasons, predominantly related to the way different cultures render decimal points and number group separators. Rendering as text avoids errors that would appear only in certain browser-culture combinations.<br \/>\n\u6ce8\u610f\u60a8\u53ef\u80fd\u4f1a\u60ca\u8bb6\u5730\u53d1\u73b0\uff0cdecimal \u548c double \u7c7b\u578b\u5448\u73b0\u4e3a\u6587\u672c\u5b57\u6bb5\uff0c\u800c\u4e0d\u662f\u6570\u5b57\u5b57\u6bb5\u3002\u8fd9\u662f\u7531\u4e8e\u51e0\u4e2a\u6280\u672f\u539f\u56e0\uff0c\u4e3b\u8981\u4e0e\u4e0d\u540c\u533a\u57df\u6027\u5448\u73b0\u5c0f\u6570\u70b9\u548c\u6570\u5b57\u7ec4\u5206\u9694\u7b26\u7684\u65b9\u5f0f\u6709\u5173\u3002\u5448\u73b0\u4e3a\u6587\u672c\u53ef\u907f\u514d\u4ec5\u5728\u67d0\u4e9b\u6d4f\u89c8\u5668\u533a\u57df\u6027\u7ec4\u5408\u4e2d\u51fa\u73b0\u7684\u9519\u8bef\u3002<\/p>\n<p>In addition to the Input Tag Helper, ASP.NET Core provides the Textarea Tag Helper. This works in a similar way, using the asp-for attribute, but it\u2019s attached to a <code>&lt;textarea&gt;<\/code> element instead:<br \/>\n\u9664\u4e86 Input Tag Helper \u4e4b\u5916\uff0cASP.NET Core \u8fd8\u63d0\u4f9b Textarea Tag Helper\u3002\u8fd9\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\u5de5\u4f5c\uff0c\u4f7f\u7528 asp-for \u5c5e\u6027\uff0c\u4f46\u5b83\u88ab\u9644\u52a0\u5230\u4e00\u4e2a<code>&lt;textarea&gt;<\/code> \u5143\u7d20\u4e0a\uff1a<\/p>\n<pre><code>&lt;textarea asp-for=&quot;BigtextValue&quot;&gt;&lt;\/textarea&gt;<\/code><\/pre>\n<p>This generates HTML similar to the following. Note that the property value is rendered inside the element, and data-val-<em> validation elements are attached as usual:<br \/>\n\u8fd9\u5c06\u751f\u6210\u7c7b\u4f3c\u4e8e\u4ee5\u4e0b\u5185\u5bb9\u7684 HTML\u3002\u8bf7\u6ce8\u610f\uff0cproperty value \u5728\u5143\u7d20\u5185\u90e8\u5448\u73b0\uff0c\u5e76\u4e14 data-val-<\/em> \u9a8c\u8bc1\u5143\u7d20\u50cf\u5f80\u5e38\u4e00\u6837\u9644\u52a0\uff1a<\/p>\n<pre><code>&lt;textarea data-val=&quot;true&quot; id=&quot;BigtextValue&quot; name=&quot;BigtextValue&quot;\n    data-val-length=&quot;Maximum length 200.&quot; data-val-length-max=&quot;200&quot;\n    data-val-required=&quot;The Multiline field is required.&quot; &gt;This is some text,\nI&#039;m going to display it\nin a text area&lt;\/textarea&gt;<\/code><\/pre>\n<p>I hope that this section has hammered home how much typing Tag Helpers can cut down on, especially when using them in conjunction with DataAnnotations for generating validation attributes. But this is more than reducing the number of keystrokes required; Tag Helpers ensure that the markup generated is correct and has the correct name, id, and format to automatically bind your binding models when they\u2019re sent to the server.<br \/>\n\u6211\u5e0c\u671b\u672c\u8282\u5df2\u7ecf\u9610\u660e\u4e86 Tag Helpers \u53ef\u4ee5\u51cf\u5c11\u591a\u5c11\u952e\u5165\u5de5\u4f5c\uff0c\u5c24\u5176\u662f\u5728\u5c06\u5b83\u4eec\u4e0e DataAnnotations \u7ed3\u5408\u4f7f\u7528\u4ee5\u751f\u6210\u9a8c\u8bc1\u5c5e\u6027\u65f6\u3002\u4f46\u8fd9\u4e0d\u4ec5\u4ec5\u662f\u51cf\u5c11\u6240\u9700\u7684\u51fb\u952e\u6b21\u6570;\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u786e\u4fdd\u751f\u6210\u7684\u6807\u8bb0\u6b63\u786e\u65e0\u8bef\uff0c\u5e76\u4e14\u5177\u6709\u6b63\u786e\u7684\u540d\u79f0\u3001ID \u548c\u683c\u5f0f\uff0c\u4ee5\u4fbf\u5728\u5c06\u7ed1\u5b9a\u6a21\u578b\u53d1\u9001\u5230\u670d\u52a1\u5668\u65f6\u81ea\u52a8\u7ed1\u5b9a\u7ed1\u5b9a\u6a21\u578b\u3002<\/p>\n<p>With <code>&lt;form&gt;<\/code>, <code>&lt;label&gt;<\/code>, and <code>&lt;input&gt;<\/code> under your belt, you\u2019re able to build most of your currency converter forms. Before we look at displaying validation messages, there\u2019s one more element to look at: the <code>&lt;select&gt;<\/code>, or drop-down, input.<br \/>\n\u4f7f\u7528 <code>&lt;form&gt;<\/code>, <code>&lt;label&gt;<\/code>\u548c <code>&lt;input&gt;<\/code> \uff0c\u60a8\u53ef\u4ee5\u6784\u5efa\u5927\u591a\u6570\u8d27\u5e01\u8f6c\u6362\u5668\u8868\u5355\u3002\u5728\u6211\u4eec\u67e5\u770b\u663e\u793a\u9a8c\u8bc1\u6d88\u606f\u4e4b\u524d\uff0c\u8fd8\u6709\u4e00\u4e2a\u5143\u7d20\u9700\u8981\u67e5\u770b\uff1a<code>&lt;select&gt;<\/code>\u6216\u4e0b\u62c9\u5217\u8868,\u8f93\u5165\u3002<\/p>\n<h3>18.2.4 The Select Tag Helper<\/h3>\n<p>As well as <code>&lt;input&gt;<\/code> fields, a common element you\u2019ll see on web forms is the <code>&lt;select&gt;<\/code> element, or drop-down lists and list boxes. Your currency converter application, for example, could use a <code>&lt;select&gt;<\/code> element to let you pick which currency to convert from a list.<br \/>\n\u9664\u4e86<code>&lt;input&gt;<\/code> \u5b57\u6bb5\u4e4b\u5916\uff0c\u60a8\u5c06\u5728 Web \u8868\u5355\u4e0a\u770b\u5230\u7684\u4e00\u4e2a\u5e38\u89c1\u5143\u7d20\u662f <code>&lt;select&gt;<\/code>\u5143\u7d20\uff0c\u5373\u4e0b\u62c9\u5217\u8868\u548c\u5217\u8868\u6846\u3002\u4f8b\u5982\uff0c\u60a8\u7684\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u4f7f\u7528\u4e00\u4e2a <code>&lt;select&gt;<\/code>\u5143\u7d20\u8ba9\u60a8\u4ece\u5217\u8868\u4e2d\u9009\u62e9\u8981\u8f6c\u6362\u7684\u8d27\u5e01\u3002<\/p>\n<p>By default, this element shows a list of items and lets you select one, but there are several variations, as shown in figure 18.6. As well as the normal drop-down list, you could show a list box, add multiselection, or display your list items in groups.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6b64\u5143\u7d20\u663e\u793a\u4e00\u4e2a\u9879\u76ee\u5217\u8868\u5e76\u5141\u8bb8\u60a8\u9009\u62e9\u4e00\u4e2a\uff0c\u4f46\u6709\u51e0\u79cd\u53d8\u4f53\uff0c\u5982\u56fe 18.6 \u6240\u793a\u3002\u9664\u4e86\u5e38\u89c4\u4e0b\u62c9\u5217\u8868\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u663e\u793a\u5217\u8868\u6846\u3001\u6dfb\u52a0\u591a\u9009\u6216\u6210\u7ec4\u663e\u793a\u5217\u8868\u9879\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1806.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.6 Some of the many ways to display <code>&lt;select&gt;<\/code> elements using the Select Tag Helper.<br \/>\n\u56fe 18.6 \u4f7f\u7528 Select Tag Helper \u663e\u793a<code>&lt;select&gt;<\/code>\u5143\u7d20\u7684\u591a\u79cd\u65b9\u6cd5\u4e2d\u7684\u4e00\u4e9b\u3002<\/p>\n<p>To use <code>&lt;select&gt;<\/code> elements in your Razor code, you\u2019ll need to include two properties in your PageModel: one property for the list of options to display and one to hold the value (or values) selected. For example, listing 18.5 shows the properties on the PageModel used to create the three leftmost select lists shown in figure 18.6. Displaying groups requires a slightly different setup, as you\u2019ll see shortly.<br \/>\n\u8981\u5728 Razor \u4ee3\u7801\u4e2d\u4f7f\u7528<code>&lt;select&gt;<\/code> \u5143\u7d20\uff0c\u60a8\u9700\u8981\u5728 PageModel \u4e2d\u5305\u542b\u4e24\u4e2a\u5c5e\u6027\uff1a\u4e00\u4e2a\u5c5e\u6027\u7528\u4e8e\u663e\u793a\u9009\u9879\u5217\u8868\uff0c\u53e6\u4e00\u4e2a\u5c5e\u6027\u7528\u4e8e\u4fdd\u5b58\u6240\u9009\u503c\u3002\u4f8b\u5982\uff0c\u6e05\u5355 18.5 \u663e\u793a\u4e86\u7528\u4e8e\u521b\u5efa\u56fe 18.6 \u4e2d\u6240\u793a\u7684\u4e09\u4e2a\u6700\u5de6\u4fa7\u9009\u62e9\u5217\u8868\u7684 PageModel \u4e0a\u7684\u5c5e\u6027\u3002\u663e\u793a\u7ec4\u9700\u8981\u7684\u8bbe\u7f6e\u7565\u6709\u4e0d\u540c\uff0c\u60a8\u5f88\u5feb\u5c31\u4f1a\u770b\u5230\u3002<\/p>\n<p>Listing 18.5 View model for displaying select element drop-down lists and list boxes<\/p>\n<pre><code>public class SelectListsModel: PageModel\n{\n    [BindProperty]                                #A\n    public class InputModel Input { get; set; }   #A\n\n    public IEnumerable&lt;SelectListItem&gt; Items { get; set; }    #B\n        = new List&lt;SelectListItem&gt;                            #B\n    {                                                         #B\n        new SelectListItem{Value = &quot;csharp&quot;, Text=&quot;C#&quot;},       #B\n        new SelectListItem{Value = &quot;python&quot;, Text= &quot;Python&quot;},  #B\n        new SelectListItem{Value = &quot;cpp&quot;, Text=&quot;C++&quot;},         #B\n        new SelectListItem{Value = &quot;java&quot;, Text=&quot;Java&quot;},       #B\n        new SelectListItem{Value = &quot;js&quot;, Text=&quot;JavaScript&quot;},   #B\n        new SelectListItem{Value = &quot;ruby&quot;, Text=&quot;Ruby&quot;},       #B\n    };                                                        #B\n\n    public class InputModel\n    {\n        public string SelectedValue1 { get; set; }                #C\n        public string SelectedValue2 { get; set; }                #C\n        public IEnumerable&lt;string&gt; MultiValues { get; set; }    #D\n    }\n}<\/code><\/pre>\n<p>\u2776 The InputModel for binding the user\u2019s selections to the select boxes<br \/>\n\u7528\u4e8e\u5c06\u7528\u6237\u7684\u9009\u62e9\u7ed1\u5b9a\u5230\u9009\u62e9\u6846\u7684 InputModel<br \/>\n\u2777 The list of items to display in the select boxes<br \/>\n\u8981\u5728\u9009\u62e9\u6846\u4e2d\u663e\u793a\u7684\u9879\u76ee\u5217\u8868<br \/>\n\u2778 These properties will hold the values selected by the single-selection select boxes.<br \/>\n\u8fd9\u4e9b\u5c5e\u6027\u5c06\u4fdd\u5b58\u7531\u5355\u9009\u9009\u62e9\u6846\u9009\u62e9\u7684\u503c\u3002<br \/>\n\u2779 To create a multiselect list box, use an <code>IEnumerable&lt;&gt;<\/code>.<br \/>\n\u82e5\u8981\u521b\u5efa\u591a\u9009\u5217\u8868\u6846\uff0c\u8bf7\u4f7f\u7528 <code>IEnumerable&lt;&gt;<\/code>\u3002<\/p>\n<p>This listing demonstrates several aspects of working with <code>&lt;select&gt;<\/code> lists:<br \/>\n\u6b64\u6e05\u5355\u6f14\u793a\u4e86\u4f7f\u7528<code>&lt;select&gt;<\/code>\u5217\u8868\u7684\u51e0\u4e2a\u65b9\u9762\uff1a<\/p>\n<p>\u2022  SelectedValue1\/SelectedValue2\u2014Used to hold the value selected by the user. They\u2019re model-bound to the value selected from the drop-down list\/list box and used to preselect the correct item when rendering the form.<br \/>\nSelectedValue1\/SelectedValue2 - \u7528\u4e8e\u4fdd\u5b58\u7528\u6237\u9009\u62e9\u7684\u503c\u3002\u5b83\u4eec\u88ab\u6a21\u578b\u7ed1\u5b9a\u5230\u4ece\u4e0b\u62c9\u5217\u8868\/\u5217\u8868\u6846\u4e2d\u9009\u62e9\u7684\u503c\uff0c\u5e76\u7528\u4e8e\u5728\u5448\u73b0\u8868\u5355\u65f6\u9884\u5148\u9009\u62e9\u6b63\u786e\u7684\u9879\u76ee\u3002<\/p>\n<p>\u2022  MultiValues\u2014Used to hold the selected values for a multiselect list. It\u2019s an IEnumerable, so it can hold more than one selection per <code>&lt;select&gt;<\/code> element.<br \/>\nMultiValues - \u7528\u4e8e\u4fdd\u5b58\u591a\u9009\u5217\u8868\u7684\u9009\u5b9a\u503c\u3002\u5b83\u662f\u4e00\u4e2a IEnumerable\uff0c\u56e0\u6b64\u6bcf\u4e2a <code>&lt;select&gt;<\/code> \u5143\u7d20\u53ef\u4ee5\u4fdd\u5b58\u591a\u4e2a\u9009\u62e9\u3002<\/p>\n<p>\u2022  Items\u2014Provides the list of options to display in the <code>&lt;select&gt;<\/code> elements. Note that the element type must be SelectListItem, which exposes the Value and Text properties, to work with the Select Tag Helper. This isn\u2019t part of the InputModel, as we don\u2019t want to model-bind these items to the request; they would normally be loaded directly from the application model or hardcoded. The order of the values in the Items property controls the order of items in the <code>&lt;select&gt;<\/code> list.<br \/>\nItems - \u63d0\u4f9b\u8981\u5728<code>&lt;select&gt;<\/code> \u5143\u7d20\u4e2d\u663e\u793a\u7684\u9009\u9879\u5217\u8868\u3002\u8bf7\u6ce8\u610f\uff0c\u5143\u7d20\u7c7b\u578b\u5fc5\u987b\u662f SelectListItem\uff0c\u5b83\u516c\u5f00 Value \u548c Text \u5c5e\u6027\uff0c\u624d\u80fd\u4f7f\u7528 Select \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u3002\u8fd9\u4e0d\u662f InputModel \u7684\u4e00\u90e8\u5206\uff0c\u56e0\u4e3a\u6211\u4eec\u4e0d\u60f3\u5c06\u8fd9\u4e9b\u9879\u6a21\u578b\u7ed1\u5b9a\u5230\u8bf7\u6c42;\u5b83\u4eec\u901a\u5e38\u76f4\u63a5\u4ece\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u52a0\u8f7d\u6216\u786c\u7f16\u7801\u3002Items \u5c5e\u6027\u4e2d\u503c\u7684\u987a\u5e8f\u63a7\u5236\u5217\u8868\u4e2d\u9879\u7684\u987a\u5e8f\u3002<\/p>\n<p><b>NOTE<\/b> The Select Tag Helper works only with SelectListItem elements. That means you\u2019ll normally have to convert from an application-specific list set of items (for example, a <code>List&lt;string&gt;<\/code> or <code>List&lt;MyClass&gt;<\/code>) to the UI-centric List<code>&lt;SelectListItem&gt;<\/code>.<br \/>\n\u6ce8\u610f\uff1aSelect Tag Helper \u4ec5\u9002\u7528\u4e8e SelectListItem \u5143\u7d20\u3002\u8fd9\u610f\u5473\u7740\u60a8\u901a\u5e38\u5fc5\u987b\u4ece\u7279\u5b9a\u4e8e\u5e94\u7528\u7a0b\u5e8f\u7684\u5217\u8868\u9879\u96c6\uff08\u4f8b\u5982\uff0ca <code>List&lt;string&gt;<\/code>\u6216 <code>List&lt;MyClass&gt;<\/code>\uff09\u8f6c\u6362\u4e3a\u4ee5 UI \u4e3a\u4e2d\u5fc3\u7684 List<code>&lt;SelectListItem&gt;<\/code>\u3002<\/p>\n<p>The Select Tag Helper exposes the asp-for and asp-items attributes that you can add to <code>&lt;select&gt;<\/code> elements. As for the Input Tag Helper, the asp-for attribute specifies the property in your PageModel to bind to. The asp-items attribute provides the <code>IEnumerable&lt;SelectListItem&gt;<\/code> to display the available <code>&lt;option&gt;<\/code> elements.<br \/>\nSelect \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u516c\u5f00\u53ef\u6dfb\u52a0\u5230<code>&lt;select&gt;<\/code>\u5143\u7d20\u7684 asp-for \u548c asp-items \u5c5e\u6027\u3002\u5bf9\u4e8e Input Tag Helper\uff0casp-for \u5c5e\u6027\u6307\u5b9a\u8981\u7ed1\u5b9a\u5230\u7684 PageModel \u4e2d\u7684\u5c5e\u6027\u3002asp-items \u5c5e\u6027\u63d0\u4f9b \u4ee5<code>IEnumerable&lt;SelectListItem&gt;<\/code> \u663e\u793a\u53ef\u7528 <code>&lt;option&gt;<\/code> \u5143\u7d20\u3002<\/p>\n<p><b>Tip<\/b> It\u2019s common to want to display a list of enum options in a <code>&lt;select&gt;<\/code> list. This is so common that ASP.NET Core ships with a helper for generating a SelectListItem for any enum. If you have an enum of the TEnum type, you can generate the available options in your view using   <code> asp-items=&quot;Html.GetEnumSelectList&lt;TEnum&gt;()&quot; <\/code>.<br \/>\n\u63d0\u793a\uff1a\u5e0c\u671b\u5728\u5217\u8868\u4e2d\u663e\u793a\u679a\u4e3e\u9009\u9879 <code>&lt;select&gt;<\/code>\u5217\u8868\u662f\u5f88\u5e38\u89c1\u7684\u3002\u8fd9\u79cd\u60c5\u51b5\u975e\u5e38\u5e38\u89c1\uff0c\u56e0\u6b64 ASP.NET Core \u9644\u5e26\u4e86\u4e00\u4e2a\u5e2e\u52a9\u7a0b\u5e8f\uff0c\u7528\u4e8e\u4e3a\u4efb\u4f55\u679a\u4e3e\u751f\u6210 SelectListItem\u3002\u5982\u679c\u60a8\u6709 TEnum \u7c7b\u578b\u7684\u679a\u4e3e\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528<code> asp-items=&quot;Html.GetEnumSelectList&lt;TEnum&gt;()&quot; <\/code>\u5728\u89c6\u56fe\u4e2d\u751f\u6210\u53ef\u7528\u9009\u9879\u3002<\/p>\n<p>The following listing shows how to display a drop-down list, a single-selection list box, and a multiselection list box. It uses the PageModel from the previous listing, binding each <code>&lt;select&gt;<\/code> list value to a different property but reusing the same Items list for all of them.<br \/>\n\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u663e\u793a\u4e0b\u62c9\u5217\u8868\u3001\u5355\u9009\u5217\u8868\u6846\u548c\u591a\u9009\u5217\u8868\u6846\u3002\u5b83\u4f7f\u7528\u4e0a\u4e00\u4e2a\u6e05\u5355\u4e2d\u7684 PageModel\uff0c\u5c06\u6bcf\u4e2a<code>&lt;select&gt;<\/code> \u5217\u8868\u503c\u7ed1\u5b9a\u5230\u4e0d\u540c\u7684\u5c5e\u6027\uff0c\u4f46\u5bf9\u6240\u6709\u5217\u8868\u91cd\u7528\u76f8\u540c\u7684 Items \u5217\u8868\u3002<\/p>\n<p>Listing 18.6 Razor template to display a select element in three ways<br \/>\n\u6e05\u5355 18.6 \u4ee5\u4e09\u79cd\u65b9\u5f0f\u663e\u793a select \u5143\u7d20\u7684 Razor \u6a21\u677f<\/p>\n<pre><code>@page\n@model SelectListsModel\n&lt;select asp-for=&quot;Input.SelectedValue1&quot;   #A\n    asp-items=&quot;Model.Items&quot;&gt;&lt;\/select&gt;    #A\n&lt;select asp-for=&quot;Input.SelectedValue2&quot;            #B\n    asp-items=&quot;Model.Items&quot; size=&quot;4&quot;&gt;&lt;\/select&gt;    #B\n&lt;select asp-for=&quot;Input.MultiValues&quot;      #C\n    asp-items=&quot;Model.Items&quot;&gt;&lt;\/select&gt;    #C<\/code><\/pre>\n<p>\u2776 Creates a standard drop-down select list by binding to a standard property in asp-for<br \/>\n\u901a\u8fc7\u7ed1\u5b9a\u5230 asp-for\u4e2d\u7684\u6807\u51c6\u5c5e\u6027\u521b\u5efa\u6807\u51c6\u4e0b\u62c9\u5217\u8868<br \/>\n\u2777 Creates a single-select list box of height 4 by providing the standard HTML size attribute<br \/>\n\u901a\u8fc7\u63d0\u4f9b\u6807\u51c6 HTML \u5927\u5c0f\u5c5e\u6027\u521b\u5efa\u9ad8\u5ea6\u4e3a 4 \u7684\u5355\u9009\u5217\u8868\u6846<br \/>\n\u2778 Creates a multiselect list box by binding to an IEnumerable property in asp-for<br \/>\n\u901a\u8fc7\u7ed1\u5b9a\u5230 asp-for \u4e2d\u7684 IEnumerable \u5c5e\u6027\u521b\u5efa\u591a\u9009\u5217\u8868\u6846<\/p>\n<p>I hope you can see that the Razor for generating a drop-down <code>&lt;select&gt;<\/code> list is almost identical to the Razor for generating a multiselect <code>&lt;select&gt;<\/code> list. The Select Tag Helper takes care of adding the multiple HTML attribute to the generated output if the property it\u2019s binding to is an IEnumerable.<br \/>\n\u6211\u5e0c\u671b\u4f60\u80fd\u770b\u5230\uff0c\u7528\u4e8e\u751f\u6210\u4e0b\u62c9<code>&lt;select&gt;<\/code>\u5217\u8868\u7684 Razor \u4e0e\u7528\u4e8e\u751f\u6210\u591a\u9009<code>&lt;select&gt;<\/code>\u5217\u8868\u7684 Razor \u51e0\u4e4e\u76f8\u540c\u3002Tag Helper \u8d1f\u8d23\u5c06 multiple HTML \u5c5e\u6027\u6dfb\u52a0\u5230\u751f\u6210\u7684\u8f93\u51fa\u4e2d\uff08\u5982\u679c\u5b83\u7ed1\u5b9a\u5230\u7684\u5c5e\u6027\u662f IEnumerable\uff09\u3002<\/p>\n<p><b>Warning<\/b> The asp-for attribute must not include the Model. prefix. The asp-items attribute, on the other hand, must include it if referencing a property on the PageModel. The asp-items attribute can also reference other C# items, such as objects stored in ViewData, but using a PageModel property is the best approach.<br \/>\n\u8b66\u544a:asp-for \u5c5e\u6027\u4e0d\u5f97\u5305\u542b Model\u3002\u524d\u7f00\u3002\u53e6\u4e00\u65b9\u9762\uff0c\u5982\u679c\u5f15\u7528 PageModel \u4e0a\u7684\u5c5e\u6027\uff0c\u5219 asp-items \u5c5e\u6027\u5fc5\u987b\u5305\u542b\u5b83\u3002asp-items \u5c5e\u6027\u8fd8\u53ef\u4ee5\u5f15\u7528\u5176\u4ed6 C# \u9879\uff0c\u4f8b\u5982\u5b58\u50a8\u5728 ViewData \u4e2d\u7684\u5bf9\u8c61\uff0c\u4f46\u4f7f\u7528 PageModel \u5c5e\u6027\u662f\u6700\u597d\u7684\u65b9\u6cd5\u3002<\/p>\n<p>You\u2019ve seen how to bind three types of select lists so far, but the one I haven\u2019t yet covered from figure 18.6 is how to display groups in your list boxes using <code>&lt;optgroup&gt;<\/code> elements. Luckily, nothing needs to change in your Razor code; you have to update only how you define your SelectListItems.<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u5982\u4f55\u7ed1\u5b9a\u4e09\u79cd\u7c7b\u578b\u7684\u9009\u62e9\u5217\u8868\uff0c\u4f46\u662f\u56fe 18.6 \u4e2d\u6211\u8fd8\u6ca1\u6709\u4ecb\u7ecd\u7684\u662f\u5982\u4f55\u4f7f\u7528 <code>&lt;optgroup&gt;<\/code> \u5143\u7d20\u5728\u5217\u8868\u6846\u4e2d\u663e\u793a\u7ec4\u3002\u5e78\u8fd0\u7684\u662f\uff0c\u60a8\u7684 Razor \u4ee3\u7801\u4e2d\u4e0d\u9700\u8981\u66f4\u6539\u4efb\u4f55\u5185\u5bb9;\u60a8\u53ea\u9700\u66f4\u65b0\u5b9a\u4e49 SelectListItems \u7684\u65b9\u5f0f\u3002<\/p>\n<p>The SelectListItem object defines a Group property that specifies the SelectListGroup the item belongs to. The following listing shows how you could create two groups and assign each list item to a \u201cdynamic\u201d or \u201cstatic\u201d group, using a PageModel similar to that shown in listing 18.5. The final list item, C#, isn\u2019t assigned to a group, so it will be displayed as normal, without an <code>&lt;optgroup&gt;<\/code>.<br \/>\nSelectListItem \u5bf9\u8c61\u5b9a\u4e49\u4e00\u4e2a Group \u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u6307\u5b9a\u9879\u76ee\u6240\u5c5e\u7684 SelectListGroup\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u4f7f\u7528\u7c7b\u4f3c\u4e8e\u6e05\u5355 18.5 \u4e2d\u6240\u793a\u7684 PageModel \u521b\u5efa\u4e24\u4e2a\u7ec4\u5e76\u5c06\u6bcf\u4e2a\u5217\u8868\u9879\u5206\u914d\u7ed9\u201c\u52a8\u6001\u201d\u6216\u201c\u9759\u6001\u201d\u7ec4\u3002\u6700\u540e\u4e00\u4e2a\u5217\u8868\u9879 C# \u672a\u5206\u914d\u7ed9\u7ec4\uff0c\u56e0\u6b64\u6ca1\u6709<optgroup>\u5b83\u5c06\u6b63\u5e38\u663e\u793a\u3002<\/p>\n<p>Listing 18.7 Adding Groups to SelectListItems to create optgroup elements<br \/>\n\u6e05\u5355 18.7 \u5411 SelectListItems \u6dfb\u52a0\u7ec4\u4ee5\u521b\u5efa optgroup \u5143\u7d20<\/p>\n<pre><code>public class SelectListsModel: PageModel\n{\n    [BindProperty]\n    public IEnumerable&lt;string&gt; SelectedValues { get; set; }    #A\n    public IEnumerable&lt;SelectListItem&gt; Items { get; set; }\n\n    public SelectListsModel()     #B\n    {\n        var dynamic = new SelectListGroup { Name = &quot;Dynamic&quot; };   #C\n        var @static = new SelectListGroup { Name = &quot;Static&quot; };       #C\n        Items = new List&lt;SelectListItem&gt;\n        {\n            new SelectListItem {\n                Value= &quot;js&quot;,\n                Text=&quot;Javascript&quot;,\n                Group = dynamic       #D\n            },\n            new SelectListItem {\n                Value= &quot;cpp&quot;,\n                Text=&quot;C++&quot;,\n                Group = @static        #D\n            },\n            new SelectListItem {\n                Value= &quot;python&quot;,\n                Text=&quot;Python&quot;,\n                Group = dynamic       #D\n            },\n            new SelectListItem {    #E\n                Value= &quot;csharp&quot;,    #E\n                Text=&quot;C#&quot;,          #E\n            }\n        };\n    }\n}<\/code><\/pre>\n<p>With this in place, the Select Tag Helper generates <code>&lt;optgroup&gt;<\/code> elements as necessary when rendering the Razor to HTML. The Razor template<br \/>\n\u5b8c\u6210\u6b64\u4f5c\u540e\uff0cSelect Tag Helper \u4f1a\u751f\u6210<code>&lt;optgroup&gt;<\/code>\u5143\u7d20.<\/p>\n<pre><code>@page\n@model SelectListsModel\n&lt;select asp-for=&quot;SelectedValues&quot; asp-items=&quot;Model.Items&quot;&gt;&lt;\/select&gt;<\/code><\/pre>\n<p>would be rendered to HTML as follows:<br \/>\n\u5c06\u5448\u73b0\u4e3a HTML\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre><code>&lt;select id=&quot;SelectedValues&quot; name=&quot;SelectedValues&quot; multiple=&quot;multiple&quot;&gt;\n    &lt;optgroup label=&quot;Dynamic&quot;&gt;\n        &lt;option value=&quot;js&quot;&gt;JavaScript&lt;\/option&gt;\n        &lt;option value=&quot;python&quot;&gt;Python&lt;\/option&gt;\n    &lt;\/optgroup&gt;\n    &lt;optgroup label=&quot;Static&quot;&gt;\n        &lt;option value=&quot;cpp&quot;&gt;C++&lt;\/option&gt;\n    &lt;\/optgroup&gt;\n    &lt;option value=&quot;csharp&quot;&gt;C#&lt;\/option&gt;\n&lt;\/select&gt;<\/code><\/pre>\n<p>Another common requirement when working with <code>&lt;select&gt;<\/code> elements is to include an option in the list that indicates that no value has been selected, as shown in figure 18.7. Without this extra option, the default <code>&lt;select&gt;<\/code> drop-down will always have a value, and it will default to the first item in the list.<br \/>\n\u4f7f\u7528<code>&lt;select&gt;<\/code>\u5143\u7d20\u65f6\u7684\u53e6\u4e00\u4e2a\u5e38\u89c1\u8981\u6c42\u662f\u5728\u5217\u8868\u4e2d\u5305\u542b\u4e00\u4e2a\u9009\u9879\uff0c\u8be5\u9009\u9879\u6307\u793a\u672a\u9009\u62e9\u4efb\u4f55\u503c\uff0c\u5982\u56fe 18.7 \u6240\u793a\u3002\u5982\u679c\u6ca1\u6709\u8fd9\u4e2a\u989d\u5916\u7684\u9009\u9879\uff0c<code>&lt;select&gt;<\/code>\u9ed8\u8ba4\u4e0b\u62c9\u5217\u8868\u5c06\u59cb\u7ec8\u6709\u4e00\u4e2a\u503c\uff0c\u5e76\u4e14\u5b83\u5c06\u9ed8\u8ba4\u4e3a\u5217\u8868\u4e2d\u7684\u7b2c\u4e00\u9879\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1807.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.7 Without a \u201cnot selected\u201d option, the <code>&lt;select&gt;<\/code> element will always have a value. This may not be the behavior you desire if you don\u2019t want an <code>&lt;option&gt;<\/code> to be selected by default.<br \/>\n\u56fe 18.7 \u5982\u679c\u6ca1\u6709 \u201cnot selected\u201d \u9009\u9879\uff0c <code>&lt;select&gt;<\/code> \u5143\u7d20\u5c06\u59cb\u7ec8\u5177\u6709\u4e00\u4e2a\u503c\u3002\u5982\u679c\u60a8\u4e0d\u5e0c\u671b\u9ed8\u8ba4\u9009\u62e9<code>&lt;option&gt;<\/code> \uff0c\u8fd9\u53ef\u80fd\u4e0d\u662f\u60a8\u60f3\u8981\u7684\u884c\u4e3a\u3002<\/p>\n<p>You can achieve this in one of two ways: you could add the \u201cnot selected\u201d option to the available SelectListItems, or you could add the option to the Razor manually, such as by using<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u4e24\u79cd\u65b9\u5f0f\u4e4b\u4e00\u6765\u5b9e\u73b0\u6b64\u76ee\u7684\uff1a\u60a8\u53ef\u4ee5\u5c06\u201c\u672a\u9009\u62e9\u201d\u9009\u9879\u6dfb\u52a0\u5230\u53ef\u7528\u7684 SelectListItems\uff0c\u4e5f\u53ef\u4ee5\u624b\u52a8\u5c06\u9009\u9879\u6dfb\u52a0\u5230 Razor\uff0c\u4f8b\u5982\u4f7f\u7528<\/p>\n<pre><code>&lt;select asp-for=&quot;SelectedValue&quot; asp-items=&quot;Model.Items&quot;&gt;\n    &lt;option Value=&quot;&quot;&gt;**Not selected**&lt;\/option&gt;\n&lt;\/select&gt;<\/code><\/pre>\n<p>This will add an extra <code>&lt;option&gt;<\/code> at the top of your <code>&lt;select&gt;<\/code> element, with a blank Value attribute, allowing you to provide a \u201cno selection\u201d option for the user.<br \/>\n\u8fd9\u5c06\u5728<code>&lt;select&gt;<\/code> \u5143\u7d20\u9876\u90e8\u6dfb\u52a0\u4e00\u4e2a\u989d\u5916\u7684<code>&lt;option&gt;<\/code> Value\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a\u7a7a\u767d\u7684 Value \u5c5e\u6027\uff0c\u5141\u8bb8\u60a8\u4e3a\u7528\u6237\u63d0\u4f9b \u201cno selection\u201d \u9009\u9879\u3002<\/p>\n<p><b>Tip<\/b>  Adding a \u201cno selection\u201d option to a <code>&lt;select&gt;<\/code> element is so common that you might want to create a partial view to encapsulate this logic.<br \/>\n\u63d0\u793a\uff1a\u5411\u5143<code>&lt;select&gt;<\/code>\u7d20\u6dfb\u52a0 \u201cno selection\u201d \u9009\u9879\u975e\u5e38\u5e38\u89c1\uff0c\u4ee5\u81f3\u4e8e\u60a8\u53ef\u80fd\u5e0c\u671b\u521b\u5efa\u4e00\u4e2a partial view \u6765\u5c01\u88c5\u6b64\u903b\u8f91\u3002<\/p>\n<p>With the Input Tag Helper and Select Tag Helper under your belt, you should be able to create most of the forms that you\u2019ll need. You have all the pieces you need to create the currency converter application now, with one exception.<br \/>\n\u6709\u4e86 Input Tag Helper \u548c Select Tag Helper\uff0c\u60a8\u5e94\u8be5\u80fd\u591f\u521b\u5efa\u6240\u9700\u7684\u5927\u591a\u6570\u8868\u5355\u3002\u60a8\u73b0\u5728\u62e5\u6709\u521b\u5efa\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\u6240\u9700\u7684\u6240\u6709\u90e8\u5206\uff0c\u4f46\u6709\u4e00\u4e2a\u4f8b\u5916\u3002<\/p>\n<p>Remember that whenever you accept input from a user, you should always validate the data. The Validation Tag Helpers provide a way for you to display model validation errors to the user on your form without having to write a lot of boilerplate markup.<br \/>\n\u8bf7\u8bb0\u4f4f\uff0c\u65e0\u8bba\u4f55\u65f6\u63a5\u53d7\u7528\u6237\u7684\u8f93\u5165\uff0c\u90fd\u5e94\u59cb\u7ec8\u9a8c\u8bc1\u6570\u636e\u3002\u9a8c\u8bc1\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u63d0\u4f9b\u4e86\u4e00\u79cd\u5728\u8868\u5355\u4e0a\u5411\u7528\u6237\u663e\u793a\u6a21\u578b\u9a8c\u8bc1\u9519\u8bef\u7684\u65b9\u6cd5\uff0c\u800c\u65e0\u9700\u7f16\u5199\u5927\u91cf\u6837\u677f\u6807\u8bb0\u3002<\/p>\n<h3>18.2.5 The Validation Message and Validation Summary Tag Helpers<\/h3>\n<p>18.2.5 \u9a8c\u8bc1\u6d88\u606f\u548c\u9a8c\u8bc1\u6458\u8981\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f<\/p>\n<p>In section 18.2.3 you saw that the Input Tag Helper generates the necessary data-val-<em> validation attributes on form input elements themselves. But you also need somewhere to display the validation messages. This can be achieved for each property in your view model using the Validation Message Tag Helper applied to a <code>&lt;span&gt;<\/code> by using the asp-validation-for attribute:<br \/>\n\u5728\u7b2c 18.2.3 \u8282\u4e2d\uff0c\u60a8\u770b\u5230 Input Tag Helper \u5728\u8868\u5355 input \u5143\u7d20\u672c\u8eab\u4e0a\u751f\u6210\u5fc5\u8981\u7684 data-val-<\/em> \u9a8c\u8bc1\u5c5e\u6027\u3002\u4f46\u60a8\u8fd8\u9700\u8981\u5728\u67d0\u4e2a\u4f4d\u7f6e\u663e\u793a\u9a8c\u8bc1\u6d88\u606f\u3002\u8fd9\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 asp-validation-for \u5c5e\u6027\u5e94\u7528\u4e8e<code>&lt;span&gt;<\/code> \u7684\u9a8c\u8bc1\u6d88\u606f\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4e3a\u89c6\u56fe\u6a21\u578b\u4e2d\u7684\u6bcf\u4e2a\u5c5e\u6027\u5b9e\u73b0\uff1a<\/p>\n<pre><code>&lt;span asp-validation-for=&quot;Email&quot;&gt;&lt;\/span&gt;<\/code><\/pre>\n<p>When an error occurs during client-side validation, the appropriate error message for the referenced property is displayed in the <code>&lt;span&gt;<\/code>, as shown in figure 18.8. This <code>&lt;span&gt;<\/code> element is also used to show appropriate validation messages if server-side validation fails when the form is redisplayed.<br \/>\n\u5f53\u5728\u5ba2\u6237\u7aef\u9a8c\u8bc1\u671f\u95f4\u53d1\u751f\u9519\u8bef\u65f6\uff0c\u5f15\u7528\u7684\u5c5e\u6027\u7684\u76f8\u5e94\u9519\u8bef\u6d88\u606f\u5c06\u663e\u793a\u5728<code>&lt;span&gt;<\/code>\u4e2d\uff0c\u5982\u56fe 18.8 \u6240\u793a\u3002\u6b64<code>&lt;span&gt;<\/code>\u5143\u7d20\u8fd8\u7528\u4e8e\u5728\u91cd\u65b0\u663e\u793a\u8868\u5355\u65f6\u670d\u52a1\u5668\u7aef\u9a8c\u8bc1\u5931\u8d25\u65f6\u663e\u793a\u76f8\u5e94\u7684\u9a8c\u8bc1\u6d88\u606f\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1808.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.8 Validation messages can be shown in an associated <code>&lt;span&gt;<\/code> by using the Validation Message Tag Helper.<br \/>\n\u56fe 18.8 \u9a8c\u8bc1\u6d88\u606f\u53ef\u4ee5\u4f7f\u7528 Validation Message Tag Helper \u663e\u793a\u5728\u5173\u8054\u7684<code>&lt;span&gt;<\/code>\u4e2d\u3002<\/p>\n<p>Any errors associated with the Email property stored in ModelState are rendered in the element body, and the appropriate attributes to hook into jQuery validation are added:<br \/>\n\u4e0e ModelState \u4e2d\u5b58\u50a8\u7684 Email \u5c5e\u6027\u5173\u8054\u7684\u4efb\u4f55\u9519\u8bef\u90fd\u5c06\u5448\u73b0\u5728\u5143\u7d20\u6b63\u6587\u4e2d\uff0c\u5e76\u6dfb\u52a0\u7528\u4e8e\u6302\u63a5\u5230 jQuery \u9a8c\u8bc1\u7684\u76f8\u5e94\u5c5e\u6027\uff1a<\/p>\n<pre><code>&lt;span class=&quot;field-validation-valid&quot; data-valmsg-for=&quot;Email&quot;\n  data-valmsg-replace=&quot;true&quot;&gt;The Email Address field is required.&lt;\/span&gt;<\/code><\/pre>\n<p>The validation error shown in the element is removed or replaced when the user updates the Email <code>&lt;input&gt;<\/code> field and client-side validation is performed.<br \/>\n\u5f53\u7528\u6237\u66f4\u65b0 Email  <code>&lt;input&gt;<\/code>\u5b57\u6bb5\u5e76\u6267\u884c\u5ba2\u6237\u7aef\u9a8c\u8bc1\u65f6\uff0c\u5c06\u5220\u9664\u6216\u66ff\u6362\u5143\u7d20\u4e2d\u663e\u793a\u7684\u9a8c\u8bc1\u9519\u8bef\u3002<\/p>\n<p><b>NOTE<\/b> For more details on ModelState and server-side validation, see chapter 16.<br \/>\n\u6ce8\u610f\u6709\u5173 ModelState \u548c\u670d\u52a1\u5668\u7aef\u9a8c\u8bc1\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u7b2c 16 \u7ae0\u3002<\/p>\n<p>As well as display validation messages for individual properties, you can display a summary of all the validation messages in a <code>&lt;div&gt;<\/code> with the Validation Summary Tag Helper, shown in figure 18.9. This renders a <code>&lt;ul&gt;<\/code> containing a list of the ModelState errors.<br \/>\n\u9664\u4e86\u663e\u793a\u5404\u4e2a\u5c5e\u6027\u7684\u9a8c\u8bc1\u6d88\u606f\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 Validation Summary Tag Helper \u5728<code>&lt;div&gt;<\/code>  \u4e2d\u663e\u793a\u6240\u6709\u9a8c\u8bc1\u6d88\u606f\u7684\u6458\u8981\uff0c\u5982\u56fe 18.9 \u6240\u793a\u3002\u8fd9\u5c06\u5448\u73b0\u4e00\u4e2a\u5305\u542b ModelState \u9519\u8bef\u5217\u8868\u7684<code>&lt;ul&gt;<\/code> \u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1809.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.9 Form showing validation errors. The Validation Message Tag Helper is applied to <code>&lt;span&gt;<\/code>, close to the associated input. The Validation Summary Tag Helper is applied to a <code>&lt;div&gt;<\/code>, normally at the top or bottom of the form.<br \/>\n\u56fe 18.9 \u663e\u793a\u9a8c\u8bc1\u9519\u8bef\u7684\u8868\u5355\u3002\u9a8c\u8bc1\u6d88\u606f\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5e94\u7528\u4e8e <code>&lt;span&gt;<\/code>\uff0c\u9760\u8fd1\u5173\u8054\u7684\u8f93\u5165\u3002\u9a8c\u8bc1\u6458\u8981\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5e94\u7528\u4e8e<code>&lt;div&gt;<\/code>\uff0c\u901a\u5e38\u4f4d\u4e8e\u8868\u5355\u7684\u9876\u90e8\u6216\u5e95\u90e8\u3002<\/p>\n<p>The Validation Summary Tag Helper is applied to a <code>&lt;div&gt;<\/code> using the asp-validation-summary attribute and providing a ValidationSummary enum value, such as<br \/>\n\u9a8c\u8bc1\u6458\u8981\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4f7f\u7528 asp-validation-summary \u5c5e\u6027\u5e76\u63d0\u4f9b ValidationSummary \u679a\u4e3e\u503c\uff08\u5982<\/p>\n<pre><code>&lt;div asp-validation-summary=&quot;All&quot;&gt;&lt;\/div&gt;<\/code><\/pre>\n<p>The ValidationSummary enum controls which values are displayed, and it has three possible values:<br \/>\nValidationSummary \u679a\u4e3e\u63a7\u5236\u663e\u793a\u54ea\u4e9b\u503c\uff0c\u5b83\u6709\u4e09\u4e2a\u53ef\u80fd\u7684\u503c\uff1a<\/p>\n<p>\u2022  None\u2014Don\u2019t display a summary. (I don\u2019t know why you\u2019d use this.)<br \/>\n\u65e0 \uff08None\uff09 - \u4e0d\u663e\u793a\u6458\u8981\u3002\uff08\u6211\u4e0d\u77e5\u9053\u4f60\u4e3a\u4ec0\u4e48\u4f1a\u7528\u8fd9\u4e2a\u3002\uff09<br \/>\n\u2022  ModelOnly\u2014Display only errors that are not associated with a property.<br \/>\n\u201c\u4ec5\u6a21\u578b\u201d\uff08ModelOnly\uff09 - \u4ec5\u663e\u793a\u4e0e\u5c5e\u6027\u65e0\u5173\u7684\u9519\u8bef\u3002<br \/>\n\u2022  All\u2014Display errors associated with either a property or the model.<br \/>\n\u201c\u5168\u90e8\u201d\uff08All\uff09 - \u663e\u793a\u4e0e\u5c5e\u6027\u6216\u6a21\u578b\u5173\u8054\u7684\u9519\u8bef\u3002<\/p>\n<p>The Validation Summary Tag Helper is particularly useful if you have errors associated with your page that aren\u2019t specific to a single property. These can be added to the model state by using a blank key, as shown in listing 18.8. In this example, the property validation passed, but we provide additional model-level validation to check that we aren\u2019t trying to convert a currency to itself.<br \/>\n\u5982\u679c\u5b58\u5728\u4e0e\u9875\u9762\u5173\u8054\u7684\u9519\u8bef\uff0c\u800c\u8fd9\u4e9b\u9519\u8bef\u5e76\u975e\u7279\u5b9a\u4e8e\u5355\u4e2a\u5c5e\u6027\uff0c\u5219 Validation Summary Tag Helper \u7279\u522b\u6709\u7528\u3002\u8fd9\u4e9b\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528\u7a7a\u952e\u6dfb\u52a0\u5230\u6a21\u578b\u72b6\u6001\u4e2d\uff0c\u5982\u6e05\u5355 18.8 \u6240\u793a\u3002\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u5c5e\u6027\u9a8c\u8bc1\u901a\u8fc7\uff0c\u4f46\u6211\u4eec\u63d0\u4f9b\u4e86\u989d\u5916\u7684\u6a21\u578b\u7ea7\u9a8c\u8bc1\uff0c\u4ee5\u68c0\u67e5\u6211\u4eec\u662f\u5426\u6ca1\u6709\u5c1d\u8bd5\u5c06\u8d27\u5e01\u8f6c\u6362\u4e3a\u81ea\u8eab\u3002<\/p>\n<p>Listing 18.8 Adding model-level validation errors to the ModelState<br \/>\n\u793a\u4f8b 18.8 \u5411 ModelState \u6dfb\u52a0\u6a21\u578b\u7ea7\u9a8c\u8bc1\u9519\u8bef<\/p>\n<pre><code>public class ConvertModel : PageModel\n{\n    [BindProperty]\n    public InputModel Input { get; set; }\n\n    [HttpPost]\n    public IActionResult OnPost()\n    {\n        if(Input.CurrencyFrom == Input.CurrencyTo)    #A\n        {\n            ModelState.AddModelError(                  #B\n                string.Empty,                          #B\n                &quot;Cannot convert currency to itself&quot;);  #B\n        }\n        if (!ModelState.IsValid)     #C\n        {                            #C\n            return Page();           #C\n        }                            #C\n\n        \/\/store the valid values somewhere etc\n        return RedirectToPage(&quot;Checkout&quot;);\n    }\n}<\/code><\/pre>\n<p>\u2776 Can\u2019t convert currency to itself<br \/>\n\u65e0\u6cd5\u5c06\u8d27\u5e01\u8f6c\u6362\u4e3a\u81ea\u8eab<br \/>\n\u2777 Adds model-level error, not tied to a specific property, by using empty key<br \/>\n\u4f7f\u7528\u7a7a\u952e\u6dfb\u52a0\u6a21\u578b\u7ea7\u9519\u8bef\uff0c\u4e0d\u4e0e\u7279\u5b9a\u5c5e\u6027\u7ed1\u5b9a<br \/>\n\u2778 If there are any property-level or model-level errors, displays them<br \/>\n\u5982\u679c\u5b58\u5728\u4efb\u4f55\u5c5e\u6027\u7ea7\u6216\u6a21\u578b\u7ea7\u9519\u8bef\uff0c\u5219\u663e\u793a\u5b83\u4eec<\/p>\n<p>Without the Validation Summary Tag Helper, the model-level error would still be added if the user used the same currency twice, and the form would be redisplayed. Unfortunately, there would have been no visual cue to the user indicating why the form did not submit. Obviously, that\u2019s a problem! By adding the Validation Summary Tag Helper, the model-level errors are shown to the user so they can correct the problem, as shown in figure 18.10.<br \/>\n\u5982\u679c\u6ca1\u6709 Validation Summary Tag Helper\uff0c\u5982\u679c\u7528\u6237\u4e24\u6b21\u4f7f\u7528\u76f8\u540c\u7684\u8d27\u5e01\uff0c\u5219\u4ecd\u4f1a\u6dfb\u52a0\u6a21\u578b\u7ea7\u9519\u8bef\uff0c\u5e76\u4e14\u8868\u5355\u5c06\u91cd\u65b0\u663e\u793a\u3002\u9057\u61be\u7684\u662f\uff0c\u4e0d\u4f1a\u5411\u7528\u6237\u63d0\u4f9b\u89c6\u89c9\u63d0\u793a\uff0c\u8bf4\u660e\u8868\u5355\u672a\u63d0\u4ea4\u7684\u539f\u56e0\u3002\u663e\u7136\uff0c\u8fd9\u662f\u4e00\u4e2a\u95ee\u9898\uff01\u901a\u8fc7\u6dfb\u52a0 Validation Summary Tag Helper\uff0c\u53ef\u4ee5\u5411\u7528\u6237\u663e\u793a\u6a21\u578b\u7ea7\u9519\u8bef\uff0c\u4ee5\u4fbf\u4ed6\u4eec\u53ef\u4ee5\u7ea0\u6b63\u95ee\u9898\uff0c\u5982\u56fe 18.10 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1810.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.10 Model-level errors are only displayed by the Validation Summary Tag Helper. Without one, users won\u2019t have any indication that there were errors on the form and so won\u2019t be able to correct them.<br \/>\n\u56fe 18.10 \u6a21\u578b\u7ea7\u9519\u8bef\u4ec5\u7531 Validation Summary Tag Helper \u663e\u793a\u3002\u5982\u679c\u6ca1\u6709 ID\uff0c\u7528\u6237\u5c06\u4e0d\u4f1a\u6709\u4efb\u4f55\u8ff9\u8c61\u8868\u660e\u8868\u5355\u4e0a\u5b58\u5728\u9519\u8bef\uff0c\u56e0\u6b64\u65e0\u6cd5\u66f4\u6b63\u5b83\u4eec\u3002<\/p>\n<p><b>NOTE<\/b> For simplicity, I added the validation check to the page handler. An alternative approach would be to create a custom validation attribute or use IValidatableObject (described in chapter 7). That way, your handler stays lean and sticks to the single- responsibility principle (SRP). You\u2019ll see how to create a custom validation attribute in chapter 32.<br \/>\n\u6ce8\u610f\uff1a\u4e3a\u7b80\u5355\u8d77\u89c1\uff0c\u6211\u5c06\u9a8c\u8bc1\u68c0\u67e5\u6dfb\u52a0\u5230\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u4e2d\u3002\u53e6\u4e00\u79cd\u65b9\u6cd5\u662f\u521b\u5efa\u81ea\u5b9a\u4e49\u9a8c\u8bc1\u5c5e\u6027\u6216\u4f7f\u7528 IValidatableObject\uff08\u5982\u7b2c 7 \u7ae0\u6240\u8ff0\uff09\u3002\u8fd9\u6837\uff0c\u60a8\u7684\u5904\u7406\u4eba\u5458\u5c31\u4f1a\u4fdd\u6301\u7cbe\u7b80\u5e76\u575a\u6301\u5355\u4e00\u8d23\u4efb\u539f\u5219 \uff08SRP\uff09\u3002\u60a8\u5c06\u5728\u7b2c 32 \u7ae0\u4e2d\u4e86\u89e3\u5982\u4f55\u521b\u5efa\u81ea\u5b9a\u4e49\u9a8c\u8bc1\u5c5e\u6027\u3002<\/p>\n<p>This section covered most of the common Tag Helpers available for working with forms, including all the pieces you need to build the currency converter forms. They should give you everything you need to get started building forms in your own applications. But forms aren\u2019t the only area in which Tag Helpers are useful; they\u2019re generally applicable any time you need to mix server-side logic with HTML generation.<br \/>\n\u672c\u8282\u4ecb\u7ecd\u4e86\u53ef\u7528\u4e8e\u8868\u5355\u7684\u5927\u591a\u6570\u5e38\u89c1 Tag Helper\uff0c\u5305\u62ec\u6784\u5efa\u8d27\u5e01\u8f6c\u6362\u5668\u8868\u5355\u6240\u9700\u7684\u6240\u6709\u90e8\u5206\u3002\u5b83\u4eec\u5e94\u8be5\u4e3a\u60a8\u63d0\u4f9b\u5f00\u59cb\u5728\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6784\u5efa\u8868\u5355\u6240\u9700\u7684\u4e00\u5207\u3002\u4f46\u662f\uff0c\u8868\u5355\u5e76\u4e0d\u662f Tag Helpers \u552f\u4e00\u6709\u7528\u7684\u9886\u57df;\u5b83\u4eec\u901a\u5e38\u9002\u7528\u4e8e\u60a8\u9700\u8981\u5c06\u670d\u52a1\u5668\u7aef\u903b\u8f91\u4e0e HTML \u751f\u6210\u6df7\u5408\u7684\u4efb\u4f55\u65f6\u95f4\u3002<\/p>\n<p>One such example is generating links to other pages in your application using routing-based URL generation. Given that routing is designed to be fluid as you refactor your application, keeping track of the exact URLs the links should point to would be a bit of a maintenance nightmare if you had to do it by hand. As you might expect, there\u2019s a Tag Helper for that: the Anchor Tag Helper.<br \/>\n\u4e00\u4e2a\u8fd9\u6837\u7684\u793a\u4f8b\u662f\u4f7f\u7528\u57fa\u4e8e\u8def\u7531\u7684 URL \u751f\u6210\u751f\u6210\u6307\u5411\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5176\u4ed6\u9875\u9762\u7684\u94fe\u63a5\u3002\u9274\u4e8e\u8def\u7531\u8bbe\u8ba1\u4e3a\u5728\u91cd\u6784\u5e94\u7528\u7a0b\u5e8f\u65f6\u662f\u6d41\u7545\u7684\uff0c\u56e0\u6b64\u5982\u679c\u5fc5\u987b\u624b\u52a8\u8ddf\u8e2a\u94fe\u63a5\u5e94\u6307\u5411\u7684\u786e\u5207 URL\uff0c\u90a3\u5c06\u6709\u70b9\u50cf\u7ef4\u62a4\u7684\u5669\u68a6\u3002\u5982\u60a8\u6240\u6599\uff0c\u6709\u4e00\u4e2a Tag Helper \u53ef\u7528\u4e8e\u6b64\uff1aAnchor Tag Helper\u3002<\/p>\n<h2>18.3 Generating links with the Anchor Tag Helper<\/h2>\n<p>18.3 \u4f7f\u7528 Anchor Tag Helper \u751f\u6210\u94fe\u63a5<\/p>\n<p>In chapters 6 and 15, I showed how you could generate URLs for links to other pages in your application using LinkGenerator and IUrlHelper. Views are another common place where you need to generate links, normally by way of an <code>&lt;a&gt;<\/code> element with an href attribute pointing to the appropriate URL.<br \/>\n\u5728\u7b2c 6 \u7ae0\u548c\u7b2c 15 \u7ae0\u4e2d\uff0c\u6211\u6f14\u793a\u4e86\u5982\u4f55\u4f7f\u7528 LinkGenerator \u548c IUrlHelper \u4e3a\u6307\u5411\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5176\u4ed6\u9875\u9762\u7684\u94fe\u63a5\u751f\u6210 URL\u3002\u89c6\u56fe\u662f\u53e6\u4e00\u4e2a\u9700\u8981\u751f\u6210\u94fe\u63a5\u7684\u5e38\u89c1\u4f4d\u7f6e\uff0c\u901a\u5e38\u662f\u901a\u8fc7\u5177\u6709 href \u5c5e\u6027\u7684<code>&lt;a&gt;<\/code>\u5143\u7d20\u6307\u5411\u76f8\u5e94\u7684 URL\u3002<\/p>\n<p>In this section I show how you can use the Anchor Tag Helper to generate the URL for a given Razor Page using routing. Conceptually, this is almost identical to the way the Form Tag Helper generates the action URL, as you saw in section 18.2.1. For the most part, using the Anchor Tag Helper is identical too; you provide asp-page and asp-page-handler attributes, along with asp-route-<em> attributes as necessary. The default Razor Page templates use the Anchor Tag Helper to generate the links shown in the navigation bar using the code in the following listing.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528 Anchor Tag Helper \u901a\u8fc7\u8def\u7531\u4e3a\u7ed9\u5b9a\u7684 Razor Page \u751f\u6210 URL\u3002\u4ece\u6982\u5ff5\u4e0a\u8bb2\uff0c\u8fd9\u4e0e Form Tag Helper \u751f\u6210\u4f5c URL \u7684\u65b9\u5f0f\u51e0\u4e4e\u76f8\u540c\uff0c\u5982\u60a8\u5728\u7b2c 18.2.1 \u8282\u4e2d\u770b\u5230\u7684\u90a3\u6837\u3002\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u4f7f\u7528 Anchor Tag Helper \u4e5f\u662f\u76f8\u540c\u7684;\u6839\u636e\u9700\u8981\u63d0\u4f9b asp-page \u548c asp-page-handler \u5c5e\u6027\u4ee5\u53ca asp-route-<\/em> \u5c5e\u6027\u3002\u9ed8\u8ba4\u7684 Razor \u9875\u9762\u6a21\u677f\u4f7f\u7528 Anchor Tag Helper \u4f7f\u7528\u4ee5\u4e0b\u6e05\u5355\u4e2d\u7684\u4ee3\u7801\u751f\u6210\u5bfc\u822a\u680f\u4e2d\u663e\u793a\u7684\u94fe\u63a5\u3002<\/p>\n<p>Listing 18.9 Using the Anchor Tag Helper to generate URLs in _Layout.cshtml<br \/>\n\u5217\u8868 18.9 \u4f7f\u7528 Anchor \u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u5728 _Layout.cshtml \u4e2d\u751f\u6210 URL<\/p>\n<pre><code>&lt;ul class=&quot;navbar-nav flex-grow-1&quot;&gt;\n    &lt;li class=&quot;nav-item&quot;&gt;\n        &lt;a class=&quot;nav-link text-dark&quot;\n            asp-area=&quot;&quot; asp-page=&quot;\/Index&quot;&gt;Home&lt;\/a&gt;\n    &lt;\/li&gt;\n    &lt;li class=&quot;nav-item&quot;&gt;\n        &lt;a class=&quot;nav-link text-dark&quot;\n            asp-area=&quot;&quot; asp-page=&quot;\/Privacy&quot;&gt;Privacy&lt;\/a&gt;\n    &lt;\/li&gt;\n&lt;\/ul&gt;<\/code><\/pre>\n<p>As you can see, each <code>&lt;a&gt;<\/code> element has an asp-page attribute. This Tag Helper uses the routing system to generate an appropriate URL for the <code>&lt;a&gt;<\/code>, resulting in the following markup:<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6bcf\u4e2a<code>&lt;a&gt;<\/code> \u5143\u7d20\u90fd\u6709\u4e00\u4e2a asp-page \u5c5e\u6027\u3002\u6b64 Tag Helper \u4f7f\u7528\u8def\u7531\u7cfb\u7edf\u4e3a<code>&lt;a&gt;<\/code> \u751f\u6210\u9002\u5f53\u7684 URL\uff0c\u4ece\u800c\u751f\u6210\u4ee5\u4e0b\u6807\u8bb0\uff1a<\/p>\n<pre><code>&lt;ul class=&quot;nav navbar-nav&quot;&gt;\n    &lt;li class=&quot;nav-item&quot;&gt;\n        &lt;a class=&quot;nav-link text-dark&quot; href=&quot;\/&quot;&gt;Home&lt;\/a&gt;\n    &lt;\/li&gt;\n    &lt;li class=&quot;nav-item&quot;&gt;\n        &lt;a class=&quot;nav-link text-dark&quot; href=&quot;\/Privacy&quot;&gt;Privacy&lt;\/a&gt;\n    &lt;\/li&gt;t\n&lt;\/ul&gt;<\/code><\/pre>\n<p>The URLs use default values where possible, so the Index Razor Page generates the simple &quot;\/&quot; URL instead of &quot;\/Index&quot;.<br \/>\nURL \u4f1a\u5c3d\u53ef\u80fd\u4f7f\u7528\u9ed8\u8ba4\u503c\uff0c\u56e0\u6b64\u7d22\u5f15 Razor \u9875\u9762\u4f1a\u751f\u6210\u7b80\u5355\u7684\u201c\/\u201dURL\uff0c\u800c\u4e0d\u662f\u201c\/Index\u201d\u3002<\/p>\n<p>If you need more control over the URL generated, the Anchor Tag Helper exposes several additional properties you can set, which are during URL generation. The attributes most often used with Razor Pages are<br \/>\n\u5982\u679c\u60a8\u9700\u8981\u5bf9\u751f\u6210\u7684 URL \u8fdb\u884c\u66f4\u591a\u63a7\u5236\uff0c\u5219 Anchor Tag Helper \u4f1a\u516c\u5f00\u60a8\u53ef\u4ee5\u8bbe\u7f6e\u7684\u51e0\u4e2a\u5176\u4ed6\u5c5e\u6027\uff0c\u8fd9\u4e9b\u5c5e\u6027\u5728 URL \u751f\u6210\u671f\u95f4\u8fdb\u884c\u3002Razor Pages \u6700\u5e38\u7528\u7684\u5c5e\u6027\u662f<\/p>\n<p>\u2022  asp-page\u2014Sets the Razor Page to execute.<br \/>\nasp-page - \u8bbe\u7f6e\u8981\u6267\u884c\u7684 Razor \u9875\u9762\u3002<br \/>\n\u2022  asp-page-handler\u2014Sets the Razor Page handler to execute.<br \/>\nasp-page-handler - \u8bbe\u7f6e\u8981\u6267\u884c\u7684 Razor Page \u5904\u7406\u7a0b\u5e8f\u3002<br \/>\n\u2022  asp-area\u2014Sets the area route parameter to use. Areas can be used to provide an additional layer of organization to your application.[1]<br \/>\nasp-area - \u8bbe\u7f6e\u8981\u4f7f\u7528\u7684\u533a\u57df\u8def\u7531\u53c2\u6570\u3002\u533a\u57df\u53ef\u7528\u4e8e\u4e3a\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u989d\u5916\u7684\u7ec4\u7ec7\u5c42\u3002[1]<br \/>\n\u2022  asp-host\u2014If set, the generated link points to the provided host and generates an absolute URL instead of a relative URL.<br \/>\nasp-host - \u5982\u679c\u8bbe\u7f6e\uff0c\u5219\u751f\u6210\u7684\u94fe\u63a5\u5c06\u6307\u5411\u63d0\u4f9b\u7684\u4e3b\u673a\uff0c\u5e76\u751f\u6210\u7edd\u5bf9 URL \u800c\u4e0d\u662f\u76f8\u5bf9 URL\u3002<br \/>\n\u2022  asp-protocol\u2014Sets whether to generate an http or https link. If set, it generates an absolute URL instead of a relative URL.<br \/>\nasp-protocol - \u8bbe\u7f6e\u662f\u751f\u6210 http \u8fd8\u662f https \u94fe\u63a5\u3002\u5982\u679c\u8bbe\u7f6e\uff0c\u5b83\u5c06\u751f\u6210\u7edd\u5bf9 URL \u800c\u4e0d\u662f\u76f8\u5bf9 URL\u3002<br \/>\n\u2022  asp-route-<em>\u2014Sets the route parameters to use during generation. Can be added multiple times for different route parameters.<br \/>\nasp-route-<\/em> - \u8bbe\u7f6e\u751f\u6210\u8fc7\u7a0b\u4e2d\u8981\u4f7f\u7528\u7684\u8def\u7531\u53c2\u6570\u3002\u53ef\u4ee5\u4e3a\u4e0d\u540c\u7684\u8def\u7531\u53c2\u6570\u591a\u6b21\u6dfb\u52a0\u3002<\/p>\n<p>By using the Anchor Tag Helper and its attributes, you generate your URLs using the routing system, as described in chapters 5 and 14. This reduces the duplication in your code by removing the hardcoded URLs you\u2019d otherwise need to embed in all your views.<br \/>\n\u901a\u8fc7\u4f7f\u7528 Anchor Tag Helper \u53ca\u5176\u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u7cfb\u7edf\u751f\u6210 URL\uff0c\u5982\u7b2c 5 \u7ae0\u548c\u7b2c 14 \u7ae0\u6240\u8ff0\u3002\u8fd9\u901a\u8fc7\u5220\u9664\u60a8\u9700\u8981\u5d4c\u5165\u5230\u6240\u6709\u89c6\u56fe\u4e2d\u7684\u786c\u7f16\u7801 URL \u6765\u51cf\u5c11\u4ee3\u7801\u4e2d\u7684\u91cd\u590d\u3002<\/p>\n<p>If you find yourself writing repetitive code in your markup, chances are someone has written a Tag Helper to help with it. The Append Version Tag Helper in the following section is a great example of using Tag Helpers to reduce the amount of fiddly code required.<br \/>\n\u5982\u679c\u60a8\u53d1\u73b0\u81ea\u5df1\u5728\u6807\u8bb0\u4e2d\u7f16\u5199\u4e86\u91cd\u590d\u7684\u4ee3\u7801\uff0c\u5219\u5f88\u53ef\u80fd\u6709\u4eba\u7f16\u5199\u4e86 Tag Helper \u6765\u5e2e\u52a9\u5904\u7406\u5b83\u3002\u4ee5\u4e0b\u90e8\u5206\u4e2d\u7684 Append Version Tag Helper \u662f\u4f7f\u7528 Tag Helpers \u51cf\u5c11\u6240\u9700\u7e41\u7410\u4ee3\u7801\u91cf\u7684\u4e00\u4e2a\u5f88\u597d\u7684\u793a\u4f8b\u3002<\/p>\n<h2>18.4 Cache-busting with the Append Version Tag Helper<\/h2>\n<p>18.4 \u4f7f\u7528 Append Version Tag Helper \u8fdb\u884c\u7f13\u5b58\u65e0\u6548\u5316<\/p>\n<p>A common problem with web development, both when developing and when an application goes into production, is ensuring that browsers are all using the latest files. For performance reasons, browsers often cache files locally and reuse them for subsequent requests rather than calling your application every time a file is requested.<br \/>\nWeb \u5f00\u53d1\u7684\u4e00\u4e2a\u5e38\u89c1\u95ee\u9898\uff0c\u65e0\u8bba\u662f\u5728\u5f00\u53d1\u65f6\u8fd8\u662f\u5728\u5e94\u7528\u7a0b\u5e8f\u6295\u5165\u751f\u4ea7\u65f6\uff0c\u90fd\u662f\u786e\u4fdd\u6d4f\u89c8\u5668\u90fd\u4f7f\u7528\u6700\u65b0\u7684\u6587\u4ef6\u3002\u51fa\u4e8e\u6027\u80fd\u539f\u56e0\uff0c\u6d4f\u89c8\u5668\u901a\u5e38\u4f1a\u5728\u672c\u5730\u7f13\u5b58\u6587\u4ef6\uff0c\u5e76\u5728\u540e\u7eed\u8bf7\u6c42\u4e2d\u91cd\u590d\u4f7f\u7528\u5b83\u4eec\uff0c\u800c\u4e0d\u662f\u5728\u6bcf\u6b21\u8bf7\u6c42\u6587\u4ef6\u65f6\u90fd\u8c03\u7528\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>Normally, this is great. Most of the static assets in your site rarely change, so caching them significantly reduces the burden on your server. Think of an image of your company logo. How often does that change? If every page shows your logo, caching the image in the browser makes a lot of sense.<br \/>\n\u901a\u5e38\uff0c\u8fd9\u5f88\u597d\u3002\u60a8\u7f51\u7ad9\u4e2d\u7684\u5927\u591a\u6570\u9759\u6001\u8d44\u6e90\u5f88\u5c11\u66f4\u6539\uff0c\u56e0\u6b64\u7f13\u5b58\u5b83\u4eec\u53ef\u4ee5\u663e\u8457\u51cf\u8f7b\u670d\u52a1\u5668\u7684\u8d1f\u62c5\u3002\u60f3\u60f3\u60a8\u516c\u53f8\u5fbd\u6807\u7684\u56fe\u50cf\u3002\u8fd9\u79cd\u60c5\u51b5\u591a\u4e45\u6539\u53d8\u4e00\u6b21\uff1f\u5982\u679c\u6bcf\u4e2a\u9875\u9762\u90fd\u663e\u793a\u60a8\u7684 logo\uff0c\u90a3\u4e48\u5728\u6d4f\u89c8\u5668\u4e2d\u7f13\u5b58\u56fe\u50cf\u5c31\u5f88\u6709\u610f\u4e49\u3002<\/p>\n<p>But what happens if it does change? You want to make sure users get the updated assets as soon as they\u2019re available. A more critical requirement might be if the JavaScript files associated with your site change. If users end up using cached versions of your JavaScript, they might see strange errors, or your application might appear broken to them.<br \/>\n\u4f46\u662f\uff0c\u5982\u679c\u5b83\u771f\u7684\u53d1\u751f\u4e86\u53d8\u5316\uff0c\u4f1a\u53d1\u751f\u4ec0\u4e48\u5462\uff1f\u60a8\u5e0c\u671b\u786e\u4fdd\u7528\u6237\u5728\u66f4\u65b0\u7684\u8d44\u4ea7\u53ef\u7528\u65f6\u7acb\u5373\u83b7\u5f97\u66f4\u65b0\u7684\u8d44\u4ea7\u3002\u66f4\u5173\u952e\u7684\u8981\u6c42\u53ef\u80fd\u662f\u4e0e\u60a8\u7684\u7f51\u7ad9\u5173\u8054\u7684 JavaScript \u6587\u4ef6\u662f\u5426\u53d1\u751f\u66f4\u6539\u3002\u5982\u679c\u7528\u6237\u6700\u7ec8\u4f7f\u7528\u60a8\u7684 JavaScript \u7684\u7f13\u5b58\u7248\u672c\uff0c\u4ed6\u4eec\u53ef\u80fd\u4f1a\u770b\u5230\u5947\u602a\u7684\u9519\u8bef\uff0c\u6216\u8005\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u53ef\u80fd\u4f1a\u5bf9\u4ed6\u4eec\u9020\u6210\u7834\u574f\u3002<\/p>\n<p>This conundrum is a common one in web development, and one of the most common ways for handling it is to use a cache-busting query string.<br \/>\n\u8fd9\u4e2a\u96be\u9898\u5728 Web \u5f00\u53d1\u4e2d\u5f88\u5e38\u89c1\uff0c\u6700\u5e38\u89c1\u7684\u5904\u7406\u65b9\u6cd5\u4e4b\u4e00\u662f\u4f7f\u7528\u7f13\u5b58\u6e05\u9664\u67e5\u8be2\u5b57\u7b26\u4e32\u3002<\/p>\n<p><b>DEFINITION<\/b> A cache-busting query string adds a query parameter to a URL, such as ?v=1. Browsers will cache the response and use it for subsequent requests to the URL. When the resource changes, the query string is also changed, such as to ?v=2. Browsers will see this as a request for a new resource and make a fresh request.<br \/>\n\u5b9a\u4e49\uff1a\u7f13\u5b58\u65e0\u6548\u5316\u67e5\u8be2\u5b57\u7b26\u4e32\u4f1a\u5c06\u67e5\u8be2\u53c2\u6570\u6dfb\u52a0\u5230 URL\uff0c\u4f8b\u5982 \uff1fv=1\u3002\u6d4f\u89c8\u5668\u5c06\u7f13\u5b58\u54cd\u5e94\u5e76\u5c06\u5176\u7528\u4e8e\u5bf9 URL \u7684\u540e\u7eed\u8bf7\u6c42\u3002\u5f53\u8d44\u6e90\u66f4\u6539\u65f6\uff0c\u67e5\u8be2\u5b57\u7b26\u4e32\u4e5f\u4f1a\u66f4\u6539\uff0c\u4f8b\u5982 \uff1fv=2\u3002\u6d4f\u89c8\u5668\u4f1a\u5c06\u6b64\u89c6\u4e3a\u5bf9\u65b0\u8d44\u6e90\u7684\u8bf7\u6c42\uff0c\u5e76\u53d1\u51fa\u65b0\u7684\u8bf7\u6c42\u3002<\/p>\n<p>The biggest problem with this approach is that it requires you to update a URL every time an image, CSS, or JavaScript file changes. This is a manual step that requires updating every place the resource is referenced, so it\u2019s inevitable that mistakes are made. Tag Helpers to the rescue! When you add a <code>&lt;script&gt;<\/code>, <code>&lt;img&gt;<\/code>, or <code>&lt;link&gt;<\/code> element to your application, you can use Tag Helpers to automatically generate a cache-busting query string:<br \/>\n\u8fd9\u79cd\u65b9\u6cd5\u7684\u6700\u5927\u95ee\u9898\u662f\uff0c\u5b83\u8981\u6c42\u60a8\u5728\u6bcf\u6b21\u56fe\u50cf\u3001CSS \u6216 JavaScript \u6587\u4ef6\u66f4\u6539\u65f6\u66f4\u65b0 URL\u3002\u8fd9\u662f\u4e00\u4e2a\u624b\u52a8\u6b65\u9aa4\uff0c\u9700\u8981\u66f4\u65b0\u5f15\u7528\u8d44\u6e90\u7684\u6bcf\u4e2a\u4f4d\u7f6e\uff0c\u56e0\u6b64\u4e0d\u53ef\u907f\u514d\u5730\u4f1a\u72af\u9519\u8bef\u3002\u6807\u8bb0\u52a9\u624b\u6765\u6551\u63f4\uff01\u5f53\u60a8\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0 <code>&lt;script&gt;<\/code>, <code>&lt;img&gt;<\/code>\u6216 <code>&lt;link&gt;<\/code>\u5143\u7d20\u65f6\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 Tag Helpers \u81ea\u52a8\u751f\u6210\u7f13\u5b58\u65e0\u6548\u5316\u67e5\u8be2\u5b57\u7b26\u4e32\uff1a<\/p>\n<pre><code>&lt;script src=&quot;~\/js\/site.js&quot; asp-append-version=&quot;true&quot;&gt;&lt;\/script&gt;<\/code><\/pre>\n<p>The asp-append-version attribute will load the file being referenced and generate a unique hash based on its contents. This is then appended as a unique query string to the resource URL:<br \/>\nasp-append-version \u5c5e\u6027\u5c06\u52a0\u8f7d\u88ab\u5f15\u7528\u7684\u6587\u4ef6\uff0c\u5e76\u6839\u636e\u5176\u5185\u5bb9\u751f\u6210\u552f\u4e00\u7684\u54c8\u5e0c\u503c\u3002\u7136\u540e\uff0c\u5c06\u5176\u4f5c\u4e3a\u552f\u4e00\u67e5\u8be2\u5b57\u7b26\u4e32\u9644\u52a0\u5230\u8d44\u6e90 URL\uff1a<\/p>\n<pre><code>&lt;script src=&quot;\/js\/site.js?v=EWaMeWsJBYWmL2g_KkgXZQ5nPe&quot;&gt;&lt;\/script&gt;<\/code><\/pre>\n<p>As this value is a hash of the file contents, it remains unchanged as long as the file isn\u2019t modified, so the file will be cached in users\u2019 browsers. But if the file is modified, the hash of the contents changes and so does the query string. This ensures that browsers are always served the most up-to-date files for your application without your having to worry about updating every URL manually whenever you change a file.<br \/>\n\u7531\u4e8e\u6b64\u503c\u662f\u6587\u4ef6\u5185\u5bb9\u7684\u54c8\u5e0c\u503c\uff0c\u56e0\u6b64\u53ea\u8981\u6587\u4ef6\u672a\u88ab\u4fee\u6539\uff0c\u5b83\u5c31\u4f1a\u4fdd\u6301\u4e0d\u53d8\uff0c\u56e0\u6b64\u8be5\u6587\u4ef6\u5c06\u7f13\u5b58\u5728\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e2d\u3002\u4f46\u662f\uff0c\u5982\u679c\u6587\u4ef6\u88ab\u4fee\u6539\uff0c\u5185\u5bb9\u7684\u54c8\u5e0c\u503c\u4f1a\u53d1\u751f\u53d8\u5316\uff0c\u67e5\u8be2\u5b57\u7b26\u4e32\u4e5f\u4f1a\u53d1\u751f\u53d8\u5316\u3002\u8fd9\u53ef\u786e\u4fdd\u6d4f\u89c8\u5668\u59cb\u7ec8\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u6700\u65b0\u7684\u6587\u4ef6\uff0c\u800c\u4e0d\u5fc5\u62c5\u5fc3\u5728\u66f4\u6539\u6587\u4ef6\u65f6\u624b\u52a8\u66f4\u65b0\u6bcf\u4e2a URL\u3002<\/p>\n<p>So far in this chapter you\u2019ve seen how to use Tag Helpers for forms, link generation, and cache busting. You can also use Tag Helpers to conditionally render different markup depending on the current environment. This uses a technique you haven\u2019t seen yet, where the Tag Helper is declared as a completely separate element.<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u8fdb\u884c\u8868\u5355\u3001\u94fe\u63a5\u751f\u6210\u548c\u7f13\u5b58\u65e0\u6548\u5316\u3002\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 Tag Helpers \u6839\u636e\u5f53\u524d\u73af\u5883\u6709\u6761\u4ef6\u5730\u5448\u73b0\u4e0d\u540c\u7684\u6807\u8bb0\u3002\u8fd9\u4f7f\u7528\u4e86\u4e00\u79cd\u4f60\u8fd8\u6ca1\u89c1\u8fc7\u7684\u6280\u672f\uff0c\u5176\u4e2d Tag Helper \u88ab\u58f0\u660e\u4e3a\u4e00\u4e2a\u5b8c\u5168\u72ec\u7acb\u7684\u5143\u7d20\u3002<\/p>\n<h2>18.5 Using conditional markup with the Environment Tag Helper<\/h2>\n<p>18.5 \u5c06\u6761\u4ef6\u6807\u8bb0\u4e0e\u73af\u5883\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4e00\u8d77\u4f7f\u7528<\/p>\n<p>In many cases, you want to render different HTML in your Razor templates depending on whether your website is running in a development or production environment. For example, in development you typically want your JavaScript and CSS assets to be verbose and easy to read, but in production you\u2019d process these files to make them as small as possible. Another example might be the desire to apply a banner to the application when it\u2019s running in a testing environment, which is removed when you move to production, as shown in figure 18.11.<br \/>\n\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\uff0c\u60a8\u5e0c\u671b\u5728 Razor \u6a21\u677f\u4e2d\u5448\u73b0\u4e0d\u540c\u7684 HTML\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u7684\u7f51\u7ad9\u662f\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u8fd0\u884c\u8fd8\u662f\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u8fd0\u884c\u3002\u4f8b\u5982\uff0c\u5728\u5f00\u53d1\u4e2d\uff0c\u60a8\u901a\u5e38\u5e0c\u671b JavaScript \u548c CSS \u8d44\u6e90\u5197\u957f\u4e14\u6613\u4e8e\u9605\u8bfb\uff0c\u4f46\u5728\u751f\u4ea7\u73af\u5883\u4e2d\uff0c\u60a8\u9700\u8981\u5904\u7406\u8fd9\u4e9b\u6587\u4ef6\u4ee5\u4f7f\u5176\u5c3d\u53ef\u80fd\u5c0f\u3002\u53e6\u4e00\u4e2a\u793a\u4f8b\u53ef\u80fd\u662f\u5e0c\u671b\u5728\u5e94\u7528\u7a0b\u5e8f\u5728\u6d4b\u8bd5\u73af\u5883\u4e2d\u8fd0\u884c\u65f6\u5411\u5e94\u7528\u7a0b\u5e8f\u5e94\u7528\u6a2a\u5e45\uff0c\u5f53\u60a8\u79fb\u52a8\u5230\u751f\u4ea7\u73af\u5883\u65f6\uff0c\u8be5\u6a2a\u5e45\u5c06\u88ab\u5220\u9664\uff0c\u5982\u56fe 18.11 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1811.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 18.11 The warning banner will be shown whenever you\u2019re running in a testing environment, to make it easy to distinguish from production.<br \/>\n\u56fe 18.11 \u5f53\u60a8\u5728\u6d4b\u8bd5\u73af\u5883\u4e2d\u8fd0\u884c\u65f6\uff0c\u90fd\u4f1a\u663e\u793a\u8b66\u544a\u6a2a\u5e45\uff0c\u4ee5\u4fbf\u4e8e\u4e0e\u751f\u4ea7\u533a\u5206\u5f00\u6765\u3002<\/p>\n<p>You\u2019ve already seen how to use C# to add if statements to your markup, so it would be perfectly possible to use this technique to add an extra div to your markup when the current environment has a given value. If we assume that the env variable contains the current environment, you could use something like this:<br \/>\n\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528 C# \u5c06 if \u8bed\u53e5\u6dfb\u52a0\u5230\u6807\u8bb0\u4e2d\uff0c\u56e0\u6b64\u5f53\u5f53\u524d\u73af\u5883\u5177\u6709\u7ed9\u5b9a\u503c\u65f6\uff0c\u5b8c\u5168\u53ef\u4ee5\u4f7f\u7528\u6b64\u6280\u672f\u5411\u6807\u8bb0\u4e2d\u6dfb\u52a0\u989d\u5916\u7684 div\u3002\u5982\u679c\u6211\u4eec\u5047\u8bbe env \u53d8\u91cf\u5305\u542b\u5f53\u524d\u73af\u5883\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u5982\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code>@if(env == &quot;Testing&quot; || env == &quot;Staging&quot;)\n{\n    &lt;div class=&quot;warning&quot;&gt;You are currently on a testing environment&lt;\/div&gt;\n}<\/code><\/pre>\n<p>There\u2019s nothing wrong with this, but a better approach would be to use the Tag Helper paradigm to keep your markup clean and easy to read. Luckily, ASP.NET Core comes with the EnvironmentTagHelper, which can be used to achieve the same result in a slightly clearer way:<br \/>\n\u8fd9\u6ca1\u6709\u9519\uff0c\u4f46\u66f4\u597d\u7684\u65b9\u6cd5\u662f\u4f7f\u7528 Tag Helper \u8303\u4f8b\u6765\u4fdd\u6301\u6807\u8bb0\u5e72\u51c0\u4e14\u6613\u4e8e\u9605\u8bfb\u3002\u5e78\u8fd0\u7684\u662f\uff0cASP.NET Core \u9644\u5e26\u4e86 EnvironmentTagHelper\uff0c\u5b83\u53ef\u7528\u4e8e\u4ee5\u66f4\u6e05\u6670\u7684\u65b9\u5f0f\u5b9e\u73b0\u76f8\u540c\u7684\u7ed3\u679c\uff1a<\/p>\n<pre><code>&lt;environment include=&quot;Testing,Staging&quot;&gt;\n    &lt;div class=&quot;warning&quot;&gt;You are currently on a testing environment&lt;\/div&gt;\n&lt;\/environment&gt;<\/code><\/pre>\n<p>This Tag Helper is a little different from the others you\u2019ve seen before. Instead of augmenting an existing HTML element using an asp- attribute, the whole element is the Tag Helper. This Tag Helper is completely responsible for generating the markup, and it uses an attribute to configure it.<br \/>\n\u6b64 Tag Helper \u4e0e\u60a8\u4ee5\u524d\u89c1\u8fc7\u7684\u5176\u4ed6 Tag Helper \u7565\u6709\u4e0d\u540c\u3002\u6574\u4e2a\u5143\u7d20\u4e0d\u662f\u4f7f\u7528 asp- \u5c5e\u6027\u6765\u6269\u5145\u73b0\u6709\u7684 HTML \u5143\u7d20\uff0c\u800c\u662f Tag Helper\u3002\u6b64 Tag Helper \u5b8c\u5168\u8d1f\u8d23\u751f\u6210\u6807\u8bb0\uff0c\u5e76\u4f7f\u7528\u5c5e\u6027\u5bf9\u5176\u8fdb\u884c\u914d\u7f6e\u3002<\/p>\n<p>Functionally, this Tag Helper is identical to the C# markup (where the env variable contains the hosting environment, as described in chapter 10), but it\u2019s more declarative in its function than the C# alternative. You\u2019re obviously free to use either approach, but personally I like the HTML-like nature of Tag Helpers.<br \/>\n\u4ece\u529f\u80fd\u4e0a\u8bb2\uff0c\u6b64 Tag Helper \u4e0e C# \u6807\u8bb0\u76f8\u540c\uff08\u5176\u4e2d env \u53d8\u91cf\u5305\u542b\u6258\u7ba1\u73af\u5883\uff0c\u5982\u7b2c 10 \u7ae0\u6240\u8ff0\uff09\uff0c\u4f46\u5b83\u7684\u51fd\u6570\u6bd4 C# \u66ff\u4ee3\u65b9\u6848\u66f4\u5177\u58f0\u660e\u6027\u3002\u663e\u7136\uff0c\u60a8\u53ef\u4ee5\u81ea\u7531\u4f7f\u7528\u4efb\u4f55\u4e00\u79cd\u65b9\u6cd5\uff0c\u4f46\u5c31\u4e2a\u4eba\u800c\u8a00\uff0c\u6211\u559c\u6b22 Tag Helper \u7684\u7c7b\u4f3c HTML \u7684\u6027\u8d28\u3002<\/p>\n<p>We\u2019ve reached the end of this chapter on Tag Helpers, and with it, we\u2019ve finished our main look at building traditional web applications that display HTML to users. In the last part of the book, we\u2019ll revisit Razor templates when you learn how to build custom components like custom Tag Helpers and view components. For now, you have everything you need to build complex Razor layouts; the custom components can help tidy up your code down the line.<br \/>\n\u6211\u4eec\u5df2\u7ecf\u5b8c\u6210\u4e86\u672c\u7ae0\u5173\u4e8e\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u7684\u7ed3\u5c3e\uff0c\u8fd9\u6837\uff0c\u6211\u4eec\u5df2\u7ecf\u5b8c\u6210\u4e86\u6784\u5efa\u5411\u7528\u6237\u663e\u793a HTML \u7684\u4f20\u7edf Web \u5e94\u7528\u7a0b\u5e8f\u7684\u4e3b\u8981\u5185\u5bb9\u3002\u5728\u672c\u4e66\u7684\u6700\u540e\u4e00\u90e8\u5206\uff0c\u5f53\u60a8\u5b66\u4e60\u5982\u4f55\u6784\u5efa\u81ea\u5b9a\u4e49\u7ec4\u4ef6\uff08\u5982\u81ea\u5b9a\u4e49\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\uff09\u548c\u89c6\u56fe\u7ec4\u4ef6\u65f6\uff0c\u6211\u4eec\u5c06\u91cd\u65b0\u8bbf\u95ee Razor \u6a21\u677f\u3002\u76ee\u524d\uff0c\u60a8\u62e5\u6709\u6784\u5efa\u590d\u6742 Razor \u5e03\u5c40\u6240\u9700\u7684\u4e00\u5207;\u81ea\u5b9a\u4e49\u7ec4\u4ef6\u53ef\u4ee5\u5e2e\u52a9\u6574\u7406\u60a8\u7684\u4ee3\u7801\u3002<\/p>\n<p>Part 3 of this book has been a whistle-stop tour of how to build Razor Page applications with ASP.NET Core. You now have the basic building blocks to start making server-rendered ASP.NET Core applications. Before we move on to discussing security in part 4 of this book, I\u2019ll take a couple of chapters to discuss building apps with MVC controllers.<br \/>\n\u672c\u4e66\u7684\u7b2c 3 \u90e8\u5206\u7b80\u8981\u4ecb\u7ecd\u4e86\u5982\u4f55\u4f7f\u7528 ASP.NET Core \u6784\u5efa Razor Page \u5e94\u7528\u7a0b\u5e8f\u3002\u73b0\u5728\uff0c\u60a8\u62e5\u6709\u4e86\u5f00\u59cb\u5236\u4f5c\u670d\u52a1\u5668\u6e32\u67d3\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u7684\u57fa\u672c\u6784\u5efa\u5757\u3002\u5728\u6211\u4eec\u7ee7\u7eed\u8ba8\u8bba\u672c\u4e66\u7684\u7b2c 4 \u90e8\u5206\u7684\u5b89\u5168\u6027\u4e4b\u524d\uff0c\u6211\u5c06\u7528\u51e0\u7ae0\u6765\u8ba8\u8bba\u4f7f\u7528 MVC \u63a7\u5236\u5668\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>I\u2019ve talked about MVC controllers a lot in passing, but in chapter 19 you\u2019ll learn why I recommend Razor Pages over MVC controllers for server-rendered apps. Nevertheless, there are some situations for which MVC controllers make sense.<br \/>\n\u6211\u987a\u4fbf\u8c08\u4e86\u5f88\u591a MVC \u63a7\u5236\u5668\uff0c\u4f46\u5728\u7b2c 19 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u4e3a\u4ec0\u4e48\u6211\u5efa\u8bae\u5c06 Razor Pages \u7528\u4e8e\u670d\u52a1\u5668\u6e32\u67d3\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u800c\u4e0d\u662f MVC \u63a7\u5236\u5668\u3002\u5c3d\u7ba1\u5982\u6b64\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0cMVC \u63a7\u5236\u5668\u662f\u6709\u610f\u4e49\u7684\u3002<\/p>\n<h2>18.6 Summary<\/h2>\n<p>18.6 \u603b\u7ed3<\/p>\n<p>With Tag Helpers, you can bind your data model to HTML elements, making it easier to generate dynamic HTML while remaining editor friendly.<br \/>\n\u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\uff0c\u60a8\u53ef\u4ee5\u5c06\u6570\u636e\u6a21\u578b\u7ed1\u5b9a\u5230 HTML \u5143\u7d20\uff0c\u4ece\u800c\u66f4\u8f7b\u677e\u5730\u751f\u6210\u52a8\u6001 HTML\uff0c\u540c\u65f6\u4fdd\u6301\u7f16\u8f91\u5668\u53cb\u597d\u6027\u3002<\/p>\n<p>As with Razor in general, Tag Helpers are for server-side rendering of HTML only. You can\u2019t use them directly in frontend frameworks, such as Angular or React.<br \/>\n\u4e0e\u4e00\u822c\u7684 Razor \u4e00\u6837\uff0c\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u4ec5\u7528\u4e8e HTML \u7684\u670d\u52a1\u5668\u7aef\u5448\u73b0\u3002\u60a8\u4e0d\u80fd\u76f4\u63a5\u5728\u524d\u7aef\u6846\u67b6\uff08\u5982 Angular \u6216 React\uff09\u4e2d\u4f7f\u7528\u5b83\u4eec\u3002<\/p>\n<p>Tag Helpers can be standalone elements or can attach to existing HTML using attributes. This lets you both customize HTML elements and add entirely new elements.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u4ee5\u662f\u72ec\u7acb\u5143\u7d20\uff0c\u4e5f\u53ef\u4ee5\u4f7f\u7528\u5c5e\u6027\u9644\u52a0\u5230\u73b0\u6709 HTML\u3002\u8fd9\u6837\uff0c\u60a8\u65e2\u53ef\u4ee5\u81ea\u5b9a\u4e49 HTML \u5143\u7d20\uff0c\u4e5f\u53ef\u4ee5\u6dfb\u52a0\u5168\u65b0\u7684\u5143\u7d20\u3002<\/p>\n<p>Tag Helpers can customize the elements they\u2019re attached to, add additional attributes, and customize how they\u2019re rendered to HTML. This can greatly reduce the amount of markup you need to write.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u4ee5\u81ea\u5b9a\u4e49\u5b83\u4eec\u6240\u9644\u52a0\u5230\u7684\u5143\u7d20\uff0c\u6dfb\u52a0\u5176\u4ed6\u5c5e\u6027\uff0c\u5e76\u81ea\u5b9a\u4e49\u5b83\u4eec\u5448\u73b0\u4e3a HTML \u7684\u65b9\u5f0f\u3002\u8fd9\u53ef\u4ee5\u5927\u5927\u51cf\u5c11\u60a8\u9700\u8981\u7f16\u5199\u7684\u6807\u8bb0\u91cf\u3002<\/p>\n<p>Tag Helpers can expose multiple attributes on a single element. This makes it easier to configure the Tag Helper, as you can set multiple, separate values.<br \/>\n\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u53ef\u4ee5\u5728\u5355\u4e2a\u5143\u7d20\u4e0a\u516c\u5f00\u591a\u4e2a\u5c5e\u6027\u3002\u8fd9\u4f7f\u5f97\u914d\u7f6e Tag Helper \u53d8\u5f97\u66f4\u52a0\u5bb9\u6613\uff0c\u56e0\u4e3a\u60a8\u53ef\u4ee5\u8bbe\u7f6e\u591a\u4e2a\u5355\u72ec\u7684\u503c\u3002<\/p>\n<p>You can add the asp-page and asp-page-handler attributes to the <code>&lt;form&gt;<\/code> element to set the action URL using the URL generation feature of Razor Pages.<br \/>\n\u53ef\u4ee5\u5c06 asp-page \u548c asp-page-handler \u5c5e\u6027\u6dfb\u52a0\u5230<code>&lt;form&gt;<\/code>\u5143\u7d20\uff0c\u4ee5\u4f7f\u7528 Razor Pages \u7684 URL \u751f\u6210\u529f\u80fd\u8bbe\u7f6e\u4f5c URL\u3002<\/p>\n<p>You specify route values to use during routing with the Form Tag Helper using asp-route-<em> attributes. These values are used to build the final URL or are passed as query data.<br \/>\n\u4f7f\u7528 asp-route-<\/em> \u5c5e\u6027\u901a\u8fc7\u8868\u5355\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u6307\u5b9a\u8981\u5728\u8def\u7531\u671f\u95f4\u4f7f\u7528\u7684\u8def\u7531\u503c\u3002\u8fd9\u4e9b\u503c\u7528\u4e8e\u6784\u5efa\u6700\u7ec8 URL \u6216\u4f5c\u4e3a\u67e5\u8be2\u6570\u636e\u4f20\u9012\u3002<\/p>\n<p>The Form Tag Helper also generates a hidden field that you can use to prevent CSRF attacks. This is added automatically and is an important security measure.<br \/>\nForm Tag Helper \u8fd8\u4f1a\u751f\u6210\u4e00\u4e2a\u9690\u85cf\u5b57\u6bb5\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u5b83\u6765\u9632\u6b62 CSRF \u653b\u51fb\u3002\u8fd9\u662f\u81ea\u52a8\u6dfb\u52a0\u7684\uff0c\u662f\u4e00\u9879\u91cd\u8981\u7684\u5b89\u5168\u63aa\u65bd\u3002<\/p>\n<p>You can attach the Label Tag Helper to a <code>&lt;label&gt;<\/code> using asp-for. It generates an appropriate for attribute and caption based on the [Display] DataAnnotation attribute and the PageModel property name.<br \/>\n\u60a8\u53ef\u4ee5\u5c06 Label Tag Helper \u9644\u52a0\u5230<code>&lt;label&gt;<\/code> \u4f7f\u7528 asp-for.\u5b83\u6839\u636e [Display] DataAnnotation \u5c5e\u6027\u548c PageModel \u5c5e\u6027\u540d\u79f0\u751f\u6210\u76f8\u5e94\u7684\u5c5e\u6027\u548c\u6807\u9898\u3002<\/p>\n<p>The Input Tag Helper sets the type attribute of an <code>&lt;input&gt;<\/code> element to the appropriate value based on a bound property\u2019s Type and any DataAnnotation attributes applied to it. It also generates the data-val-<em> attributes required for client-side validation. This significantly reduces the amount of HTML code you need to write.<br \/>\nInput Tag Helper \u6839\u636e\u7ed1\u5b9a\u5c5e\u6027\u7684 Type \u548c\u5e94\u7528\u4e8e\u5b83\u7684\u4efb\u4f55 DataAnnotation \u5c5e\u6027\uff0c\u5c06 <code>&lt;input&gt;<\/code>\u5143\u7d20\u7684 type \u5c5e\u6027\u8bbe\u7f6e\u4e3a\u9002\u5f53\u7684\u503c\u3002\u5b83\u8fd8\u751f\u6210\u5ba2\u6237\u7aef\u9a8c\u8bc1\u6240\u9700\u7684 data-val-<\/em> \u5c5e\u6027\u3002\u8fd9\u5927\u5927\u51cf\u5c11\u4e86\u60a8\u9700\u8981\u7f16\u5199\u7684 HTML \u4ee3\u7801\u91cf\u3002<\/p>\n<p>To enable client-side validation, you must add the necessary JavaScript files to your view for jQuery validation and unobtrusive validation.<br \/>\n\u8981\u542f\u7528\u5ba2\u6237\u7aef\u9a8c\u8bc1\uff0c\u60a8\u5fc5\u987b\u5c06\u5fc5\u8981\u7684 JavaScript \u6587\u4ef6\u6dfb\u52a0\u5230\u89c6\u56fe\u4e2d\uff0c\u4ee5\u8fdb\u884c jQuery \u9a8c\u8bc1\u548c\u4e0d\u5f15\u4eba\u6ce8\u76ee\u7684\u9a8c\u8bc1\u3002<\/p>\n<p>The Select Tag Helper can generate drop-down <code>&lt;select&gt;<\/code> elements as well as list boxes, using the asp-for and asp-items attributes. To generate a multiselect <code>&lt;select&gt;<\/code> element, bind the element to an IEnumerable property on the view model. You can use these approaches to generate several different styles of select box.<br \/>\nSelect Tag Helper \u53ef\u4ee5\u4f7f\u7528 asp-for \u548c asp-items \u5c5e\u6027\u751f\u6210\u4e0b\u62c9<code>&lt;select&gt;<\/code>\u5143\u7d20\u548c\u5217\u8868\u6846\u3002\u8981\u751f\u6210\u591a\u9009\u5143\u7d20\uff0c\u8bf7\u5c06\u8be5<code>&lt;select&gt;<\/code>\u5143\u7d20\u7ed1\u5b9a\u5230\u89c6\u56fe\u6a21\u578b\u4e0a\u7684 IEnumerable \u5c5e\u6027\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u65b9\u6cd5\u751f\u6210\u591a\u79cd\u4e0d\u540c\u6837\u5f0f\u7684\u9009\u62e9\u6846\u3002<\/p>\n<p>You can generate an <code>IEnumerable&lt;SelectListItem&gt;<\/code> for an enum TEnum using the <code>Html.GetEnumSelectList&lt;TEnum&gt;()<\/code> helper method. This saves you having to write the mapping code yourself.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 helper <code>Html.GetEnumSelectList&lt;TEnum&gt;()<\/code> \u65b9\u6cd5\u751f\u6210 <code>IEnumerable&lt;SelectListItem&gt;<\/code>  \u679a\u4e3e TEnum\u3002\u8fd9\u6837\u5c31\u4e0d\u5fc5\u81ea\u5df1\u7f16\u5199\u6620\u5c04\u4ee3\u7801\u3002<\/p>\n<p>The Select Tag Helper generates <code>&lt;optgroup&gt;<\/code> elements if the items supplied in asp-for have an associated SelectListGroup on the Group property. Groups can be used to separate items in select lists.<br \/>\n\u5982\u679c asp-for \u4e2d\u63d0\u4f9b\u7684\u9879\u5728 Group \u5c5e\u6027\u4e0a\u5177\u6709\u5173\u8054\u7684 SelectListGroup\uff0c\u5219 Select Tag Helper \u4f1a\u751f\u6210<code>&lt;optgroup&gt;<\/code> \u5143\u7d20\u3002\u7ec4\u53ef\u7528\u4e8e\u5206\u9694\u9009\u62e9\u5217\u8868\u4e2d\u7684\u9879\u76ee\u3002<\/p>\n<p>Any extra additional <code>&lt;option&gt;<\/code> elements added to the Razor markup are passed through to the final HTML unchanged. You can use these additional elements to easily add a \u201cno selection\u201d option to the <code>&lt;select&gt;<\/code> element.<br \/>\n\u6dfb\u52a0\u5230 Razor \u6807\u8bb0\u7684\u4efb\u4f55\u5176\u4ed6\u989d\u5916<code>&lt;option&gt;<\/code>\u5143\u7d20\u90fd\u5c06\u539f\u5c01\u4e0d\u52a8\u5730\u4f20\u9012\u5230\u6700\u7ec8 HTML\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u9644\u52a0\u5143\u7d20\u8f7b\u677e\u5730\u5411 <code>&lt;select&gt;<\/code>\u5143\u7d20\u6dfb\u52a0\u201c\u65e0\u9009\u62e9\u201d\u9009\u9879\u3002<\/p>\n<p>The Validation Message Tag Helper is used to render the client- and server-side validation error messages for a given property. This gives important feedback to your users when elements have errors. Use the asp-validation-for attribute to attach the Validation Message Tag Helper to a <code>&lt;span&gt;<\/code>.<br \/>\n\u9a8c\u8bc1\u6d88\u606f\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u7528\u4e8e\u5448\u73b0\u7ed9\u5b9a\u5c5e\u6027\u7684\u5ba2\u6237\u7aef\u548c\u670d\u52a1\u5668\u7aef\u9a8c\u8bc1\u9519\u8bef\u6d88\u606f\u3002\u5f53\u5143\u7d20\u6709\u9519\u8bef\u65f6\uff0c\u8fd9\u4f1a\u5411\u7528\u6237\u63d0\u4f9b\u91cd\u8981\u7684\u53cd\u9988\u3002\u4f7f\u7528 asp-validation-for \u5c5e\u6027\u5c06\u9a8c\u8bc1\u6d88\u606f\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u9644\u52a0\u5230<code>&lt;span&gt;<\/code> .<\/p>\n<p>The Validation Summary Tag Helper displays validation errors for the model, as well as for individual properties. You can use model-level properties to display additional validation that doesn\u2019t apply to just one property. Use the asp-validation-summary attribute to attach the Validation Summary Tag Helper to a <code>&lt;div&gt;<\/code>.<br \/>\nValidation Summary Tag Helper \u663e\u793a\u6a21\u578b\u4ee5\u53ca\u5404\u4e2a\u5c5e\u6027\u7684\u9a8c\u8bc1\u9519\u8bef\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u6a21\u578b\u7ea7\u5c5e\u6027\u6765\u663e\u793a\u4e0d\u4ec5\u9002\u7528\u4e8e\u4e00\u4e2a\u5c5e\u6027\u7684\u5176\u4ed6\u9a8c\u8bc1\u3002\u4f7f\u7528 asp-validation-summary \u5c5e\u6027\u5c06\u9a8c\u8bc1\u6458\u8981\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u9644\u52a0\u5230 <code>&lt;div&gt;<\/code>.<\/p>\n<p>You can generate <code>&lt;a&gt;<\/code> URLs using the Anchor Tag Helper. This helper uses routing to generate the href URL using asp-page, asp-page-handler, and asp-route-<em> attributes, giving you the full power of routing.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 Anchor Tag Helper \u751f\u6210<code>&lt;a&gt;<\/code>  URL\u3002\u6b64\u5e2e\u52a9\u7a0b\u5e8f\u4f7f\u7528\u8def\u7531\u901a\u8fc7 asp-page\u3001asp-page-handler \u548c asp-route-<\/em> \u5c5e\u6027\u751f\u6210 href URL\uff0c\u4ece\u800c\u4e3a\u60a8\u63d0\u4f9b\u5b8c\u6574\u7684\u8def\u7531\u529f\u80fd\u3002<\/p>\n<p>You can add the asp-append-version attribute to <code>&lt;link&gt;<\/code>, <code>&lt;script&gt;<\/code>, and <code>&lt;img&gt;<\/code> elements to provide cache-busting capabilities based on the file\u2019s contents. This ensures users cache files for performance reasons, yet still always get the latest version of files.<br \/>\n\u60a8\u53ef\u4ee5\u5c06 asp-append-version \u5c5e\u6027\u6dfb\u52a0\u5230 <code>&lt;link&gt;<\/code>, <code>&lt;script&gt;<\/code> \u548c <code>&lt;img&gt;<\/code>\u5143\u7d20\u4e2d\uff0c\u4ee5\u6839\u636e\u6587\u4ef6\u7684\u5185\u5bb9\u63d0\u4f9b\u7f13\u5b58\u6e05\u9664\u529f\u80fd\u3002\u8fd9\u53ef\u786e\u4fdd\u7528\u6237\u51fa\u4e8e\u6027\u80fd\u539f\u56e0\u7f13\u5b58\u6587\u4ef6\uff0c\u4f46\u4ecd\u59cb\u7ec8\u83b7\u5f97\u6700\u65b0\u7248\u672c\u7684\u6587\u4ef6\u3002<\/p>\n<p>You can use the Environment Tag Helper to conditionally render different HTML based on the app\u2019s current execution environment. You can use this to render completely different HTML in different environments if you wish.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 Environment Tag Helper \u6839\u636e\u5e94\u7528\u7a0b\u5e8f\u7684\u5f53\u524d\u6267\u884c\u73af\u5883\u6709\u6761\u4ef6\u5730\u5448\u73b0\u4e0d\u540c\u7684 HTML\u3002\u5982\u679c\u4f60\u613f\u610f\uff0c\u4f60\u53ef\u4ee5\u4f7f\u7528\u5b83\u6765\u5728\u4e0d\u540c\u7684\u73af\u5883\u4e2d\u5448\u73b0\u5b8c\u5168\u4e0d\u540c\u7684 HTML\u3002<\/p>\n<p>[1] I don\u2019t cover areas in detail in this book. They\u2019re an optional aspect of MVC that are often only used on large projects. You can read about them here: <a href=\"http:\/\/mng.bz\/3X64\">http:\/\/mng.bz\/3X64<\/a>.<br \/>\n[1] \u6211\u5728\u8fd9\u672c\u4e66\u4e2d\u6ca1\u6709\u8be6\u7ec6\u4ecb\u7ecd\u5404\u4e2a\u9886\u57df\u3002\u5b83\u4eec\u662f MVC \u7684\u4e00\u4e2a\u53ef\u9009\u65b9\u9762\uff0c\u901a\u5e38\u4ec5\u7528\u4e8e\u5927\u578b\u9879\u76ee\u3002\u60a8\u53ef\u4ee5\u5728\u6b64\u5904\u9605\u8bfb\u6709\u5173\u5b83\u4eec\u7684\u4fe1\u606f\uff1a<a href=\"http:\/\/mng.bz\/3X64\">http:\/\/mng.bz\/3X64<\/a>\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>18 Building forms with Tag Helpers 18 \u4f7f\u7528\u6807\u8bb0\u8f85\u52a9\u5bf9\u8c61\u6784\u5efa\u8868\u5355 This chapter covers \u672c\u7ae0\u6db5\u76d6 \u2022 Building forms easily with Tag Helpers \u4f7f\u7528\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u8f7b\u677e\u6784\u5efa\u8868\u5355 \u2022 Generating URLs with the Anchor Tag Helper \u4f7f\u7528\u951a\u70b9\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u751f\u6210 URL \u2022 Using Tag Helpers to add functionality to Razor \u4f7f\u7528\u6807\u7b7e\u5e2e\u52a9\u7a0b\u5e8f\u5411 Razor \u6dfb\u52a0\u529f\u80fd In chapter 17 you learned about Razor templates and how to use them [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-607","post","type-post","status-publish","format-standard","hentry","category-csharp"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/607","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=607"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/607\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=607"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=607"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=607"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}