{"id":599,"date":"2025-04-05T11:38:51","date_gmt":"2025-04-05T03:38:51","guid":{"rendered":"https:\/\/www.hyy.net\/?p=599"},"modified":"2025-04-05T11:38:51","modified_gmt":"2025-04-05T03:38:51","slug":"asp-net-core-in-action-14-mapping-urls-to-razor-pages-using-routing","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=599","title":{"rendered":"ASP.NET Core in Action 14 Mapping URLs to Razor Pages using routing"},"content":{"rendered":"<p>14 Mapping URLs to Razor Pages using routing<br \/>\n14 \u4f7f\u7528\u8def\u7531\u5c06 URL \u6620\u5c04\u5230 Razor Pages<\/p>\n<p>This chapter covers<br \/>\n\u672c\u7ae0\u4ecb\u7ecd\u4ee5\u4e0b\u5185\u5bb9<\/p>\n<p>\u2022  Routing requests to Razor Pages<br \/>\n\u5c06\u8bf7\u6c42\u8def\u7531\u5230 Razor Pages<br \/>\n\u2022  Customizing Razor Page route templates<br \/>\n\u81ea\u5b9a\u4e49 Razor Page \u8def\u7531\u6a21\u677f<br \/>\n\u2022  Generating URLs for Razor Pages<br \/>\n\u4e3a Razor Pages \u751f\u6210 URL<\/p>\n<p>In chapter 13 you learned about the Model-View-Controller (MVC) design pattern and how ASP.NET Core uses it to generate the UI for an application using Razor Pages. Razor Pages contain page handlers that act as mini controllers for a request. The page handler calls the application model to retrieve or save data. Then the handler passes data from the application model to the Razor view, which generates an HTML response.<br \/>\n\u5728\u7b2c 13 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u6a21\u578b-\u89c6\u56fe-\u63a7\u5236\u5668 \uff08MVC\uff09 \u8bbe\u8ba1\u6a21\u5f0f\uff0c\u4ee5\u53ca ASP.NET Core \u5982\u4f55\u4f7f\u7528\u5b83\u4e3a\u4f7f\u7528 Razor Pages \u7684\u5e94\u7528\u7a0b\u5e8f\u751f\u6210 UI\u3002Razor Pages \u5305\u542b\u5145\u5f53\u8bf7\u6c42\u7684\u5fae\u578b\u63a7\u5236\u5668\u7684\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u3002\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u8c03\u7528\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u6765\u68c0\u7d22\u6216\u4fdd\u5b58\u6570\u636e\u3002\u7136\u540e\uff0c\u5904\u7406\u7a0b\u5e8f\u5c06\u6570\u636e\u4ece\u5e94\u7528\u7a0b\u5e8f\u6a21\u578b\u4f20\u9012\u5230 Razor \u89c6\u56fe\uff0c\u8be5\u89c6\u56fe\u4f1a\u751f\u6210 HTML \u54cd\u5e94\u3002<\/p>\n<p>Although not part of the MVC design pattern per se, one crucial part of Razor Pages is selecting which Razor Page to invoke in response to a given request. Razor Pages use the same routing system as minimal APIs (introduced in chapter 6); this chapter focuses on how routing works with Razor Pages.<br \/>\n\u867d\u7136 Razor Pages \u672c\u8eab\u4e0d\u662f MVC \u8bbe\u8ba1\u6a21\u5f0f\u7684\u4e00\u90e8\u5206\uff0c\u4f46 Razor Pages \u7684\u4e00\u4e2a\u5173\u952e\u90e8\u5206\u662f\u9009\u62e9\u8981\u8c03\u7528\u7684 Razor Page \u4ee5\u54cd\u5e94\u7ed9\u5b9a\u8bf7\u6c42\u3002Razor Pages \u4f7f\u7528\u4e0e\u6700\u5c0f API \u76f8\u540c\u7684\u8def\u7531\u7cfb\u7edf\uff08\u5728\u7b2c 6 \u7ae0\u4e2d\u4ecb\u7ecd\uff09;\u672c\u7ae0\u91cd\u70b9\u4ecb\u7ecd\u8def\u7531\u5982\u4f55\u4e0e Razor Pages \u914d\u5408\u4f7f\u7528\u3002<\/p>\n<p>I start this chapter with a brief reminder about how routing works in ASP.NET Core. I\u2019ll touch on the two pieces of middleware that are crucial to endpoint routing in .NET 7 and the approach Razor Pages uses of mixing conventions with explicit route templates.<br \/>\n\u672c\u7ae0\u5f00\u59cb\u65f6\uff0c\u6211\u5c06\u7b80\u8981\u4ecb\u7ecd\u8def\u7531\u5728 ASP.NET Core \u4e2d\u7684\u5de5\u4f5c\u539f\u7406\u3002\u6211\u5c06\u4ecb\u7ecd\u5bf9 .NET 7 \u4e2d\u7684\u7ec8\u7ed3\u70b9\u8def\u7531\u81f3\u5173\u91cd\u8981\u7684\u4e24\u4e2a\u4e2d\u95f4\u4ef6\uff0c\u4ee5\u53ca Razor Pages \u4f7f\u7528\u5c06\u7ea6\u5b9a\u4e0e\u663e\u5f0f\u8def\u7531\u6a21\u677f\u6df7\u5408\u7684\u65b9\u6cd5\u3002<\/p>\n<p>In section 14.3 we look at the default routing behavior of Razor Pages, and in section 14.4 you\u2019ll learn how to customize the behavior by adding or changing route templates. Razor Pages have access to the same route template features that you learned about in chapter 6, and in section 14.4 you\u2019ll learn how to them.<br \/>\n\u5728\u7b2c 14.3 \u8282\u4e2d\uff0c\u6211\u4eec\u4ecb\u7ecd\u4e86 Razor Pages \u7684\u9ed8\u8ba4\u8def\u7531\u884c\u4e3a\uff0c\u5728\u7b2c 14.4 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u901a\u8fc7\u6dfb\u52a0\u6216\u66f4\u6539\u8def\u7531\u6a21\u677f\u6765\u81ea\u5b9a\u4e49\u884c\u4e3a\u3002Razor Pages \u53ef\u4ee5\u8bbf\u95ee\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4e86\u89e3\u7684\u76f8\u540c\u8def\u7531\u6a21\u677f\u529f\u80fd\uff0c\u5728\u7b2c 14.4 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u5b83\u4eec\u3002<\/p>\n<p>In section 14.5 I describe how to use the routing system to generate URLs for Razor Pages. Razor Pages provide some helper methods to simplify URL generation compared with minimal APIs, so I compare the two approaches and discuss the benefits of each.<br \/>\n\u5728\u7b2c 14.5 \u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528\u8def\u7531\u7cfb\u7edf\u4e3a Razor Pages \u751f\u6210 URL\u3002\u4e0e\u6700\u5c11\u7684 API \u76f8\u6bd4\uff0cRazor Pages \u63d0\u4f9b\u4e86\u4e00\u4e9b\u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\u6765\u7b80\u5316 URL \u751f\u6210\uff0c\u56e0\u6b64\u6211\u6bd4\u8f83\u4e86\u8fd9\u4e24\u79cd\u65b9\u6cd5\u5e76\u8ba8\u8bba\u4e86\u6bcf\u79cd\u65b9\u6cd5\u7684\u4f18\u70b9\u3002<\/p>\n<p>Finally, in section 14.6 I describe how to customize the conventions Razor Pages uses, giving you complete control of the URLs in your application. You\u2019ll see how to change the built-in conventions, such as using lowercase for your URLs, as well as how to write your own convention and apply it globally to your application.<br \/>\n\u6700\u540e\uff0c\u5728\u7b2c 14.6 \u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u81ea\u5b9a\u4e49 Razor Pages \u4f7f\u7528\u7684\u7ea6\u5b9a\uff0c\u4ece\u800c\u8ba9\u60a8\u5b8c\u5168\u63a7\u5236\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 URL\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u66f4\u6539\u5185\u7f6e\u7ea6\u5b9a\uff0c\u4f8b\u5982\u5bf9 URL \u4f7f\u7528\u5c0f\u5199\uff0c\u4ee5\u53ca\u5982\u4f55\u7f16\u5199\u81ea\u5df1\u7684\u7ea6\u5b9a\u5e76\u5c06\u5176\u5168\u5c40\u5e94\u7528\u4e8e\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>By the end of this chapter you should have a much clearer understanding of how an ASP.NET Core application works. You can think of routing as the glue that ties the middleware pipeline to Razor Pages and the MVC framework. With middleware, Razor Pages, and routing under your belt, you\u2019ll be writing web apps in no time!<br \/>\n\u5728\u672c\u7ae0\u7ed3\u675f\u65f6\uff0c\u60a8\u5e94\u8be5\u5bf9 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u7684\u5de5\u4f5c\u539f\u7406\u6709\u4e86\u66f4\u6e05\u6670\u7684\u4e86\u89e3\u3002\u53ef\u4ee5\u5c06\u8def\u7531\u89c6\u4e3a\u5c06\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e0e Razor Pages \u548c MVC \u6846\u67b6\u7ed1\u5b9a\u7684\u7c98\u9644\u3002\u501f\u52a9\u4e2d\u95f4\u4ef6\u3001Razor Pages \u548c\u8def\u7531\uff0c\u60a8\u5c06\u7acb\u5373\u7f16\u5199 Web \u5e94\u7528\u7a0b\u5e8f\uff01<\/p>\n<h2>14.1 Routing in ASP.NET Core<\/h2>\n<p>14.1 ASP.NET Core \u4e2d\u7684\u8def\u7531<\/p>\n<p>In chapter 6 we looked in detail at routing and some of the benefits it brings, such as the ability to have multiple URLs pointing to the same endpoint and extracting segments from the URL. You also learned how it\u2019s implemented in ASP.NET Core apps, using two pieces of middleware:<br \/>\n\u5728\u7b2c 6 \u7ae0\u4e2d\uff0c\u6211\u4eec\u8be6\u7ec6\u7814\u7a76\u4e86\u8def\u7531\u53ca\u5176\u5e26\u6765\u7684\u4e00\u4e9b\u597d\u5904\uff0c\u4f8b\u5982\u8ba9\u591a\u4e2a URL \u6307\u5411\u540c\u4e00\u7aef\u70b9\u5e76\u4ece URL \u4e2d\u63d0\u53d6\u7247\u6bb5\u7684\u80fd\u529b\u3002\u60a8\u8fd8\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528\u4e24\u4e2a\u4e2d\u95f4\u4ef6\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u5b9e\u73b0\u5b83\uff1a<\/p>\n<p>\u2022  EndpointMiddleware\u2014You use this middleware to register the endpoints in the routing system when you start your application. The middleware executes one of the endpoints at runtime.<br \/>\nEndpointMiddleware \u2014 \u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u6b64\u4e2d\u95f4\u4ef6\u5728\u8def\u7531\u7cfb\u7edf\u4e2d\u6ce8\u518c\u7ec8\u7aef\u8282\u70b9\u3002\u4e2d\u95f4\u4ef6\u5728\u8fd0\u884c\u65f6\u6267\u884c\u5176\u4e2d\u4e00\u4e2a\u7aef\u70b9\u3002<br \/>\n\u2022  RoutingMiddleware\u2014This middleware chooses which of the endpoints registered by the EndpointMiddleware should execute for a given request at runtime.<br \/>\nRoutingMiddleware \u2014 \u6b64\u4e2d\u95f4\u4ef6\u9009\u62e9 EndpointMiddleware \u6ce8\u518c\u7684\u7aef\u70b9\u5e94\u5728\u8fd0\u884c\u65f6\u4e3a\u7ed9\u5b9a\u8bf7\u6c42\u6267\u884c\u3002<\/p>\n<p>The EndpointMiddleware is where you register all the endpoints in your app, including minimal APIs, Razor Pages, and MVC controllers. It\u2019s easy to register all the Razor Pages in your application using the MapRazorPages() extension method, as shown in the following listing.<br \/>\n\u5728 EndpointMiddleware \u4e2d\uff0c\u4f60\u53ef\u4ee5\u6ce8\u518c\u5e94\u7528\u4e2d\u7684\u6240\u6709\u7ec8\u7ed3\u70b9\uff0c\u5305\u62ec\u6700\u5c0f\u7684 API\u3001Razor Pages \u548c MVC \u63a7\u5236\u5668\u3002\u4f7f\u7528 MapRazorPages\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u6240\u6709 Razor Pages \u5f88\u5bb9\u6613\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 14.1 Registering Razor Pages in Startup.Configure<br \/>\n\u793a\u4f8b 14.1 \u5728 Startup.Configure \u4e2d\u6ce8\u518c Razor \u9875\u9762<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddRazorPages(); \u2776\nvar app = builder.Build();\napp.UseStaticFiles();\napp.UseRouting(); \u2777\napp.UseAuthorization();\napp.MapRazorPages(); \u2778\napp.Run();<\/code><\/pre>\n<p>\u2776 Adds the required Razor Pages services to dependency injection<br \/>\n\u5c06\u6240\u9700\u7684 Razor Pages \u670d\u52a1\u6dfb\u52a0\u5230\u4f9d\u8d56\u9879\u6ce8\u5165<br \/>\n\u2777 Adds the RoutingMiddleware to the middleware pipeline<br \/>\n\u5c06 RoutingMiddleware \u6dfb\u52a0\u5230\u4e2d\u95f4\u4ef6\u7ba1\u9053<br \/>\n\u2778 Registers all the Razor Pages in the application with the EndpointMiddleware<br \/>\n\u4f7f\u7528 EndpointMiddleware \u6ce8\u518c\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Razor Pages<\/p>\n<p>Each endpoint, whether it\u2019s a Razor Page or a minimal API, has an associated route template that defines which URLs the endpoint should match. The EndpointMiddleware stores these route templates and endpoints in a dictionary, which it shares with the RoutingMiddleware. At runtime the RoutingMiddleware compares the incoming request with the routes in the dictionary and selects the matching endpoint. When the request reaches the EndpointMiddleware, the middleware checks to see which endpoint was selected and executes it, as shown in figure 14.1.<br \/>\n\u6bcf\u4e2a\u7ec8\u7ed3\u70b9\uff08\u65e0\u8bba\u662f Razor Page \u8fd8\u662f\u6700\u5c0f API\uff09\u90fd\u6709\u4e00\u4e2a\u5173\u8054\u7684\u8def\u7531\u6a21\u677f\uff0c\u7528\u4e8e\u5b9a\u4e49\u7ec8\u7ed3\u70b9\u5e94\u5339\u914d\u7684 URL\u3002EndpointMiddleware \u5c06\u8fd9\u4e9b\u8def\u7531\u6a21\u677f\u548c\u7aef\u70b9\u5b58\u50a8\u5728\u4e00\u4e2a\u5b57\u5178\u4e2d\uff0c\u5b83\u4e0e RoutingMiddleware \u5171\u4eab\u8be5\u5b57\u5178\u3002\u5728\u8fd0\u884c\u65f6\uff0cRoutingMiddleware \u5c06\u4f20\u5165\u8bf7\u6c42\u4e0e\u5b57\u5178\u4e2d\u7684\u8def\u7531\u8fdb\u884c\u6bd4\u8f83\uff0c\u5e76\u9009\u62e9\u5339\u914d\u7684\u7aef\u70b9\u3002\u5f53\u8bf7\u6c42\u5230\u8fbe EndpointMiddleware \u65f6\uff0c\u4e2d\u95f4\u4ef6\u4f1a\u68c0\u67e5\u5e76\u9009\u62e9\u4e86\u54ea\u4e2a\u7aef\u70b9\u5e76\u6267\u884c\u5b83\uff0c\u5982\u56fe 14.1 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1401.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 14.1 Endpoint routing uses a two-step process. The RoutingMiddleware selects which endpoint to execute, and the EndpointMiddleware executes it. If the request URL doesn\u2019t match a route template, the endpoint middleware will not generate a response.<br \/>\n\u56fe 14.1 \u7ec8\u7aef\u8282\u70b9\u8def\u7531\u4f7f\u7528\u4e24\u6b65\u8fc7\u7a0b\u3002RoutingMiddleware \u9009\u62e9\u8981\u6267\u884c\u7684\u7aef\u70b9\uff0cEndpointMiddleware \u6267\u884c\u5b83\u3002\u5982\u679c\u8bf7\u6c42 URL \u4e0e\u8def\u7531\u6a21\u677f\u4e0d\u5339\u914d\uff0c\u5219\u7ec8\u7aef\u8282\u70b9\u4e2d\u95f4\u4ef6\u4e0d\u4f1a\u751f\u6210\u54cd\u5e94\u3002<\/p>\n<p>As discussed in chapter 6, the advantage of having two separate pieces of middleware to handle this process is that any middleware placed after the RoutingMiddleware can see which endpoint is going to be executed before it is. You\u2019ll see this benefit in action when we look at authorization in chapter 24.<br \/>\n\u5982\u7b2c 6 \u7ae0\u6240\u8ff0\uff0c\u62e5\u6709\u4e24\u4e2a\u72ec\u7acb\u7684\u4e2d\u95f4\u4ef6\u6765\u5904\u7406\u6b64\u8fc7\u7a0b\u7684\u597d\u5904\u662f\uff0c\u653e\u7f6e\u5728 RoutingMiddleware \u4e4b\u540e\u7684\u4efb\u4f55\u4e2d\u95f4\u4ef6\u90fd\u53ef\u4ee5\u5728\u6267\u884c\u4e4b\u524d\u770b\u5230\u54ea\u4e2a\u7aef\u70b9\u5c06\u88ab\u6267\u884c\u3002\u5f53\u6211\u4eec\u5728\u7b2c 24 \u7ae0\u4e2d\u67e5\u770b\u6388\u6743\u65f6\uff0c\u60a8\u5c06\u770b\u5230\u6b64\u597d\u5904\u7684\u5b9e\u9645\u6548\u679c\u3002<\/p>\n<p>Routing in ASP.NET Core uses the same infrastructure and middleware whether you\u2019re building minimal APIs, Razor Pages, or MVC controllers, but there are some differences in how you define the mapping between your route templates and your handlers in each case. In section 14.2 you\u2019ll learn the different approaches each paradigm takes.<br \/>\n\u65e0\u8bba\u662f\u6784\u5efa\u6700\u5c0f\u7684 API\u3001Razor Pages \u8fd8\u662f MVC \u63a7\u5236\u5668\uff0cASP.NET Core \u4e2d\u7684\u8def\u7531\u90fd\u4f7f\u7528\u76f8\u540c\u7684\u57fa\u7840\u7ed3\u6784\u548c\u4e2d\u95f4\u4ef6\uff0c\u4f46\u5728\u6bcf\u79cd\u60c5\u51b5\u4e0b\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u548c\u5904\u7406\u7a0b\u5e8f\u4e4b\u95f4\u7684\u6620\u5c04\u7684\u65b9\u5f0f\u5b58\u5728\u4e00\u4e9b\u5dee\u5f02\u3002\u5728 14.2 \u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u6bcf\u79cd\u8303\u4f8b\u91c7\u7528\u7684\u4e0d\u540c\u65b9\u6cd5\u3002<\/p>\n<h2>14.2 Convention-based routing vs. explicit routing<\/h2>\n<p>14.2 \u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u4e0e\u663e\u5f0f\u8def\u7531<\/p>\n<p>Routing is a key part of ASP.NET Core, as it maps the incoming request\u2019s URL to a specific endpoint to execute. You have two ways to define these URL-endpoint mappings in your application:<br \/>\n\u8def\u7531\u662f ASP.NET Core \u7684\u5173\u952e\u90e8\u5206\uff0c\u56e0\u4e3a\u5b83\u5c06\u4f20\u5165\u8bf7\u6c42\u7684 URL \u6620\u5c04\u5230\u8981\u6267\u884c\u7684\u7279\u5b9a\u7ec8\u7aef\u8282\u70b9\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u4e24\u79cd\u65b9\u5f0f\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5b9a\u4e49\u8fd9\u4e9b URL-\u7ec8\u7aef\u8282\u70b9\u6620\u5c04\uff1a<\/p>\n<p>\u2022  Using global, convention-based routing<br \/>\n\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u5168\u5c40\u8def\u7531<br \/>\n\u2022  Using explicit routing, where each endpoint is mapped with a single route template<br \/>\n\u4f7f\u7528\u663e\u5f0f\u8def\u7531\uff0c\u5176\u4e2d\u6bcf\u4e2a\u7aef\u70b9\u90fd\u4f7f\u7528\u5355\u4e2a\u8def\u7531\u6a21\u677f\u8fdb\u884c\u6620\u5c04<\/p>\n<p>Which approach you use typically depends on whether you\u2019re using minimal APIs, Razor Pages, or MVC controllers and whether you\u2019re building an API or a website (using HTML). These days I lean heavily toward explicit routing, as you\u2019ll see.<br \/>\n\u4f7f\u7528\u54ea\u79cd\u65b9\u6cd5\u901a\u5e38\u53d6\u51b3\u4e8e\u4f60\u4f7f\u7528\u7684\u662f\u6700\u5c0f API\u3001Razor Pages \u8fd8\u662f MVC \u63a7\u5236\u5668\uff0c\u4ee5\u53ca\u4f60\u662f\u6784\u5efa API \u8fd8\u662f\u7f51\u7ad9\uff08\u4f7f\u7528 HTML\uff09\u3002\u6b63\u5982\u60a8\u5c06\u770b\u5230\u7684\uff0c\u8fd9\u4e9b\u5929\u6211\u975e\u5e38\u503e\u5411\u4e8e\u663e\u5f0f\u8def\u7531\u3002<\/p>\n<p>Convention-based routing is defined globally for your application. You can use convention-based routes to map endpoints (MVC controller actions specifically) to URLs, but those MVC controllers must adhere strictly to the conventions you define. Traditionally, applications using MVC controllers to generate HTML tend to use this approach to routing. The downside of this approach is that customizing URLs for a subset of controllers and actions is tricky.<br \/>\n\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u662f\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5168\u5c40\u5b9a\u4e49\u7684\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u5c06\u7aef\u70b9\uff08\u7279\u522b\u662f MVC \u63a7\u5236\u5668\u4f5c\uff09\u6620\u5c04\u5230 URL\uff0c\u4f46\u8fd9\u4e9b MVC \u63a7\u5236\u5668\u5fc5\u987b\u4e25\u683c\u9075\u5b88\u60a8\u5b9a\u4e49\u7684\u7ea6\u5b9a\u3002\u4f20\u7edf\u4e0a\uff0c\u4f7f\u7528 MVC \u63a7\u5236\u5668\u751f\u6210 HTML \u7684\u5e94\u7528\u7a0b\u5e8f\u503e\u5411\u4e8e\u4f7f\u7528\u8fd9\u79cd\u65b9\u6cd5\u8fdb\u884c\u8def\u7531\u3002\u8fd9\u79cd\u65b9\u6cd5\u7684\u7f3a\u70b9\u662f\uff0c\u4e3a\u63a7\u5236\u5668\u548c\u4f5c\u7684\u5b50\u96c6\u81ea\u5b9a\u4e49 URL \u5f88\u68d8\u624b\u3002<\/p>\n<p>Alternatively, you can use explicit routing to tie a given URL to a specific endpoint. You\u2019ve seen this approach with minimal APIs, where each endpoint is directly associated with a route template. You can also use explicit routing with MVC controllers by placing [Route] attributes on the action methods themselves, hence explicit-routing is also often called attribute-routing.<br \/>\n\u6216\u8005\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u663e\u5f0f\u8def\u7531\u5c06\u7ed9\u5b9a\u7684 URL \u7ed1\u5b9a\u5230\u7279\u5b9a\u7684\u7aef\u70b9\u3002\u60a8\u5df2\u7ecf\u89c1\u8fc7\u8fd9\u79cd\u65b9\u6cd5\u4f7f\u7528\u6700\u5c11\u7684 API\uff0c\u5176\u4e2d\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u90fd\u76f4\u63a5\u4e0e\u8def\u7531\u6a21\u677f\u5173\u8054\u3002\u60a8\u8fd8\u53ef\u4ee5\u901a\u8fc7\u5728\u4f5c\u65b9\u6cd5\u672c\u8eab\u4e0a\u653e\u7f6e [Route] \u5c5e\u6027\u6765\u5c06\u663e\u5f0f\u8def\u7531\u4e0e MVC \u63a7\u5236\u5668\u4e00\u8d77\u4f7f\u7528\uff0c\u56e0\u6b64\u663e\u5f0f\u8def\u7531\u901a\u5e38\u4e5f\u79f0\u4e3a\u5c5e\u6027\u8def\u7531\u3002<\/p>\n<p>Explicit routing provides more flexibility than convention-based based routing, as you can explicitly define the route template for every action method. Explicit routing is generally more verbose than the convention-based approach, as it requires applying attributes to every action method in your application. Despite this, the extra flexibility can be useful, especially when building APIs.<br \/>\n\u663e\u5f0f\u8def\u7531\u6bd4\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u63d0\u4f9b\u4e86\u66f4\u5927\u7684\u7075\u6d3b\u6027\uff0c\u56e0\u4e3a\u60a8\u53ef\u4ee5\u4e3a\u6bcf\u4e2a\u4f5c\u65b9\u6cd5\u663e\u5f0f\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u3002\u663e\u5f0f\u8def\u7531\u901a\u5e38\u6bd4\u57fa\u4e8e\u7ea6\u5b9a\u7684\u65b9\u6cd5\u66f4\u8be6\u7ec6\uff0c\u56e0\u4e3a\u5b83\u9700\u8981\u5c06\u5c5e\u6027\u5e94\u7528\u4e8e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6bcf\u4e2a\u4f5c\u65b9\u6cd5\u3002\u5c3d\u7ba1\u5982\u6b64\uff0c\u989d\u5916\u7684\u7075\u6d3b\u6027\u8fd8\u662f\u5f88\u6709\u7528\u7684\uff0c\u5c24\u5176\u662f\u5728\u6784\u5efa API \u65f6\u3002<\/p>\n<p>Somewhat confusingly, Razor Pages use conventions to generate explicit routes! In many ways this combination gives you the best of both worlds: the predictability and terseness of convention-based routing with the easy customization of explicit routing. There are tradeoffs to each of the approaches, as shown in table 14.1.<br \/>\n\u6709\u70b9\u4ee4\u4eba\u56f0\u60d1\u7684\u662f\uff0cRazor Pages \u4f7f\u7528\u7ea6\u5b9a\u6765\u751f\u6210\u663e\u5f0f\u8def\u7531\uff01\u5728\u8bb8\u591a\u65b9\u9762\uff0c\u8fd9\u79cd\u7ec4\u5408\u4e3a\u60a8\u63d0\u4f9b\u4e86\u4e24\u5168\u5176\u7f8e\u7684\u6548\u679c\uff1a\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u7684\u53ef\u9884\u6d4b\u6027\u548c\u7b80\u6d01\u6027\uff0c\u4ee5\u53ca\u663e\u5f0f\u8def\u7531\u7684\u8f7b\u677e\u81ea\u5b9a\u4e49\u3002\u6bcf\u79cd\u65b9\u6cd5\u90fd\u6709\u6743\u8861\uff0c\u5982\u8868 14.1 \u6240\u793a\u3002<\/p>\n<p>Table 14.1 The advantages and disadvantages of the routing styles available in ASP.NET Core<br \/>\n\u8868 14.1 ASP.NET Core \u4e2d\u53ef\u7528\u7684\u8def\u7531\u6837\u5f0f\u7684\u4f18\u7f3a\u70b9<\/p>\n<table>\n<thead>\n<tr>\n<th>Routing style<\/th>\n<th>Typical use<\/th>\n<th>Advantages<\/th>\n<th>Disadvantages<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Convention-based routes <br \/>\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531<\/td>\n<td>HTML-generating MVC controllers<br \/>HTML \u751f\u6210 MVC \u63a7\u5236\u5668<\/td>\n<td>Terse definition in one location in your application. Forces a consistent layout of MVC controllers. <br \/>\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4e00\u4e2a\u4f4d\u7f6e\u8fdb\u884c\u7b80\u6d01\u5b9a\u4e49\u3002\u5f3a\u5236 MVC \u63a7\u5236\u5668\u5e03\u5c40\u4e00\u81f4\u3002<\/td>\n<td>Routes are defined in a different place from your controllers. Overriding the route conventions can be tricky and error-prone. Adds an extra layer of indirection when routing a request. <br \/>\u8def\u7531\u5b9a\u4e49\u5728\u4e0e\u63a7\u5236\u5668\u4e0d\u540c\u7684\u4f4d\u7f6e\u3002\u8986\u76d6\u8def\u7531\u7ea6\u5b9a\u53ef\u80fd\u5f88\u68d8\u624b\u4e14\u5bb9\u6613\u51fa\u9519\u3002\u5728\u8def\u7531\u8bf7\u6c42\u65f6\u6dfb\u52a0\u989d\u5916\u7684\u95f4\u63a5\u5c42\u3002<\/td>\n<\/tr>\n<tr>\n<td>Explicit routes<br \/>\u663e\u5f0f\u8def\u7531<\/td>\n<td>Minimal API endpoints, Web API MVC controllers <br \/> \u6700\u5c0f API \u7aef\u70b9\u3001Web API MVC \u63a7\u5236\u5668<\/td>\n<td>Gives complete control of route templates for every endpoint.Routes are defined next to the endpoint they execute.<br \/>\u63d0\u4f9b\u5bf9\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u8def\u7531\u6a21\u677f\u7684\u5b8c\u5168\u63a7\u5236\u3002\u8def\u7531\u5728\u5b83\u4eec\u6267\u884c\u7684\u7ec8\u7aef\u8282\u70b9\u65c1\u8fb9\u5b9a\u4e49\u3002<\/td>\n<td>Verbose compared with convention-based routing.Can be easy to overcustomize route templates.Route templates may be scattered throughout your application rather than defined in one location. <br \/>\u4e0e\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u76f8\u6bd4\uff0c\u6bd4\u8f83\u8be6\u7ec6\u3002\u53ef\u4ee5\u5f88\u5bb9\u6613\u5730\u8fc7\u5ea6\u5b9a\u5236\u8def\u7ebf\u6a21\u677f\u3002\u8def\u7531\u6a21\u677f\u53ef\u80fd\u5206\u6563\u5728\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u800c\u4e0d\u662f\u5728\u4e00\u4e2a\u4f4d\u7f6e\u5b9a\u4e49\u3002<\/td>\n<\/tr>\n<tr>\n<td>Convention-based generation of explicit routes <br \/>\u57fa\u4e8e\u7ea6\u5b9a\u7684\u663e\u5f0f\u8def\u7531\u751f\u6210<\/td>\n<td>Razor Pages<br \/>Razor \u9875\u9762<\/td>\n<td>Encourages consistent set of exposed URLs. Terse when you stick to the conventions. Easily override the route template for a single page. Customize conventions globally to change exposed URLs. <br \/>\u9f13\u52b1\u4f7f\u7528\u4e00\u7ec4\u4e00\u81f4\u7684\u516c\u5f00 URL\u3002\u5f53\u4f60\u575a\u6301\u60ef\u4f8b\u65f6\uff0c\u7b80\u6d01\u3002\u8f7b\u677e\u8986\u76d6\u5355\u4e2a\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u3002\u5168\u5c40\u81ea\u5b9a\u4e49\u7ea6\u5b9a\u4ee5\u66f4\u6539\u516c\u5f00\u7684 URL\u3002<\/td>\n<td>Possible to overcustomize route templates. You must calculate what the route template for a page is, rather than its being explicitly defined in your app.  <br \/>\u53ef\u4ee5\u8fc7\u5ea6\u81ea\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u3002\u60a8\u5fc5\u987b\u8ba1\u7b97\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u662f\u4ec0\u4e48\uff0c\u800c\u4e0d\u662f\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u663e\u5f0f\u5b9a\u4e49\u5b83\u3002<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>So which approach should you use? I believe that convention-based routing is not worth the effort in 99 percent of cases and that you should stick to explicit routing. If you\u2019re following my advice to use Razor Pages for server-rendered applications, you\u2019re already using explicit routing under the covers. Also, if you\u2019re creating APIs using minimal APIs or MVC controllers, explicit routing is the best option and the recommended approach.<br \/>\n\u90a3\u4e48\u60a8\u5e94\u8be5\u4f7f\u7528\u54ea\u79cd\u65b9\u6cd5\u5462\uff1f\u6211\u8ba4\u4e3a\uff0c\u5728 99% \u7684\u60c5\u51b5\u4e0b\uff0c\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u4e0d\u503c\u5f97\u4ed8\u51fa\u52aa\u529b\uff0c\u60a8\u5e94\u8be5\u575a\u6301\u4f7f\u7528\u663e\u5f0f\u8def\u7531\u3002\u5982\u679c\u60a8\u6309\u7167\u6211\u7684\u5efa\u8bae\u5c06 Razor Pages \u7528\u4e8e\u670d\u52a1\u5668\u6e32\u67d3\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u90a3\u4e48\u60a8\u5df2\u7ecf\u5728\u5e55\u540e\u4f7f\u7528\u4e86\u663e\u5f0f\u8def\u7531\u3002\u6b64\u5916\uff0c\u5982\u679c\u8981\u4f7f\u7528\u6700\u5c11\u7684 API \u6216 MVC \u63a7\u5236\u5668\u521b\u5efa API\uff0c\u5219\u663e\u5f0f\u8def\u7531\u662f\u6700\u4f73\u9009\u9879\u548c\u63a8\u8350\u7684\u65b9\u6cd5\u3002<\/p>\n<p>The only scenario where convention-based routing is used traditionally is if you\u2019re using MVC controllers to generate HTML. But if you\u2019re following my advice from chapter 13, you\u2019ll be using Razor Pages for HTML-generating applications and falling back to MVC controllers only when necessary, as I discuss in more detail in chapter 19. For consistency, I would often stick with explicit routing with attributes in that scenario too.<br \/>\n\u4f20\u7edf\u4e0a\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u7684\u552f\u4e00\u60c5\u51b5\u662f\u4f7f\u7528 MVC \u63a7\u5236\u5668\u751f\u6210 HTML\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u9075\u5faa\u6211\u5728\u7b2c 13 \u7ae0\u4e2d\u7684\u5efa\u8bae\uff0c\u60a8\u5c06\u4f7f\u7528 Razor Pages \u751f\u6210 HTML \u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u4e14\u4ec5\u5728\u5fc5\u8981\u65f6\u56de\u9000\u5230 MVC \u63a7\u5236\u5668\uff0c\u6b63\u5982\u6211\u5728\u7b2c 19 \u7ae0\u4e2d\u66f4\u8be6\u7ec6\u5730\u8ba8\u8bba\u7684\u90a3\u6837\u3002\u4e3a\u4e86\u4fdd\u6301\u4e00\u81f4\u6027\uff0c\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u6211\u901a\u5e38\u4e5f\u4f1a\u575a\u6301\u4f7f\u7528\u5e26\u6709\u5c5e\u6027\u7684\u663e\u5f0f\u8def\u7531\u3002<\/p>\n<p><b>NOTE<\/b> For the reasons above, this book focuses on explicit\/attribute routing. For details on convention-based routing, see Microsoft\u2019s \u201cRouting to controller actions in ASP.NET Core\u201d documentation at <a href=\"http:\/\/mng.bz\/ZP0O\">http:\/\/mng.bz\/ZP0O<\/a>.<br \/>\n\u6ce8\u610f:\u7531\u4e8e\u4e0a\u8ff0\u539f\u56e0\uff0c\u672c\u4e66\u91cd\u70b9\u4ecb\u7ecd\u663e\u5f0f\/\u5c5e\u6027\u8def\u7531\u3002\u6709\u5173\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u201c\u8def\u7531\u5230 ASP.NET Core \u4e2d\u7684\u63a7\u5236\u5668\u4f5c\u201d\u6587\u6863\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/ZP0O\">http:\/\/mng.bz\/ZP0O<\/a>\u3002<\/p>\n<p>You learned about routing and route templates in chapter 6 in the context of minimal APIs. The good news is that exactly the same patterns and features are available with Razor Pages. The main difference with minimal APIs is that Razor Pages use conventions to generate the route template for a page, though you can easily change the template on a page-by-page basis. In section 14.3 we look at the default conventions and how routing maps a request\u2019s URL to a Razor Page in detail.<br \/>\n\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4e86\u89e3\u4e86\u6700\u5c0f API \u4e0a\u4e0b\u6587\u4e2d\u7684\u8def\u7531\u548c\u8def\u7531\u6a21\u677f\u3002\u597d\u6d88\u606f\u662f\uff0cRazor Pages \u63d0\u4f9b\u4e86\u5b8c\u5168\u76f8\u540c\u7684\u6a21\u5f0f\u548c\u529f\u80fd\u3002\u4e0e\u6700\u5c0f API \u7684\u4e3b\u8981\u533a\u522b\u5728\u4e8e\uff0cRazor Pages \u4f7f\u7528\u7ea6\u5b9a\u4e3a\u9875\u9762\u751f\u6210\u8def\u7531\u6a21\u677f\uff0c\u4f46\u4f60\u53ef\u4ee5\u8f7b\u677e\u5730\u9010\u9875\u66f4\u6539\u6a21\u677f\u3002\u5728\u7b2c 14.3 \u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u8be6\u7ec6\u4ecb\u7ecd\u9ed8\u8ba4\u7ea6\u5b9a\u4ee5\u53ca\u8def\u7531\u5982\u4f55\u5c06\u8bf7\u6c42\u7684 URL \u6620\u5c04\u5230 Razor \u9875\u9762\u3002<\/p>\n<h2>14.3 Routing requests to Razor Pages<\/h2>\n<p>14.3 \u5c06\u8bf7\u6c42\u8def\u7531\u5230 Razor Pages<\/p>\n<p>As I mentioned in section 14.2, Razor Pages use explicit routing by creating route templates based on conventions. ASP.NET Core creates a route template for every Razor Page in your app during app startup, when you call MapRazorPages() in Program.cs:<br \/>\n\u6b63\u5982\u6211\u5728\u7b2c 14.2 \u8282\u4e2d\u63d0\u5230\u7684\uff0cRazor Pages \u901a\u8fc7\u57fa\u4e8e\u7ea6\u5b9a\u521b\u5efa\u8def\u7531\u6a21\u677f\u6765\u4f7f\u7528\u663e\u5f0f\u8def\u7531\u3002ASP.NET Core \u5728\u5e94\u7528\u542f\u52a8\u671f\u95f4\uff0c\u5f53\u60a8\u5728 Program.cs \u4e2d\u8c03\u7528 MapRazorPages\uff08\uff09 \u65f6\uff0c\u4f1a\u4e3a\u5e94\u7528\u4e2d\u7684\u6bcf\u4e2a Razor \u9875\u9762\u521b\u5efa\u4e00\u4e2a\u8def\u7531\u6a21\u677f\uff1a<\/p>\n<pre><code>app.endpoints.MapRazorPages();<\/code><\/pre>\n<p>For every Razor Page in your application, the framework uses the path of the Razor Page file relative to the Razor Pages root directory (Pages\/), excluding the file extension (.cshtml). If you have a Razor Page located at the path Pages\/Products\/View.cshtml, the framework creates a route template with the value &quot;Products\/View&quot;, as shown in figure 14.2.<br \/>\n\u5bf9\u4e8e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6bcf\u4e2a Razor \u9875\u9762\uff0c\u6846\u67b6\u4f7f\u7528 Razor \u9875\u9762\u6587\u4ef6\u76f8\u5bf9\u4e8e Razor \u9875\u9762\u6839\u76ee\u5f55 \uff08Pages\/\uff09 \u7684\u8def\u5f84\uff0c\u4e0d\u5305\u62ec\u6587\u4ef6\u6269\u5c55\u540d \uff08.cshtml\uff09\u3002\u5982\u679c\u4f60\u7684 Razor \u9875\u9762\u4f4d\u4e8e\u8def\u5f84 Pages\/Products\/View.cshtml\uff0c\u5219\u6846\u67b6\u4f1a\u521b\u5efa\u4e00\u4e2a\u503c\u4e3a\u201cProducts\/View\u201d\u7684\u8def\u7531\u6a21\u677f\uff0c\u5982\u56fe 14.2 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1402.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 14.2 By default, route templates are generated for Razor Pages based on the path of the file relative to the root directory, Pages.<br \/>\n\u56fe 14.2 \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5c06\u6839\u636e\u6587\u4ef6\u76f8\u5bf9\u4e8e\u6839\u76ee\u5f55 Pages \u7684\u8def\u5f84\u4e3a Razor Pages \u751f\u6210\u8def\u7531\u6a21\u677f\u3002<\/p>\n<p>Requests to the URL \/products\/view match the route template &quot;Products\/View&quot;, which in turn corresponds to the View.cshtml Razor Page in the Pages\/Products folder. The RoutingMiddleware selects the View.cshtml Razor Page as the endpoint for the request, and the EndpointMiddleware executes the page\u2019s handler when the request reaches it in the middleware pipeline.<br \/>\n\u5bf9 URL \/products\/view \u7684\u8bf7\u6c42\u4e0e\u8def\u7531\u6a21\u677f\u201cProducts\/View\u201d\u5339\u914d\uff0c\u800c\u8def\u7531\u6a21\u677f\u53c8\u5bf9\u5e94\u4e8e Pages\/Products \u6587\u4ef6\u5939\u4e2d\u7684 View.cshtml Razor \u9875\u9762\u3002RoutingMiddleware \u9009\u62e9 View.cshtml Razor Page \u4f5c\u4e3a\u8bf7\u6c42\u7684\u7ec8\u7ed3\u70b9\uff0c\u5f53\u8bf7\u6c42\u5728\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e2d\u5230\u8fbe\u9875\u9762\u65f6\uff0cEndpointMiddleware \u5c06\u6267\u884c\u9875\u9762\u7684\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p><b>NOTE<\/b> Remember that routing is not case-sensitive, so the request URL will match even if it has a different URL casing from the route template.<br \/>\n\u6ce8\u610f:\u8bf7\u8bb0\u4f4f\uff0c\u8def\u7531\u4e0d\u533a\u5206\u5927\u5c0f\u5199\uff0c\u56e0\u6b64\u5373\u4f7f\u8bf7\u6c42 URL \u7684 URL \u5927\u5c0f\u5199\u4e0e\u8def\u7531\u6a21\u677f\u4e0d\u540c\uff0c\u8bf7\u6c42 URL \u4e5f\u4f1a\u5339\u914d\u3002<\/p>\n<p>In chapter 13 you learned that Razor Page handlers are the methods that are invoked on a Razor Page, such as OnGet. When we say \u201ca Razor Page is executed,\u201d we really mean \u201can instance of the Razor Page\u2019s PageModel is created, and a page handler on the model is invoked.\u201d Razor Pages can have multiple page handlers, so once the RoutingMiddleware selects a Razor Page, the EndpointMiddleware still needs to choose which handler to execute. You\u2019ll learn how the framework selects which page handler to invoke in chapter 15.<br \/>\n\u5728\u7b2c 13 \u7ae0\u4e2d\uff0c\u4f60\u4e86\u89e3\u4e86 Razor Page \u5904\u7406\u7a0b\u5e8f\u662f\u5728 Razor Page \u4e0a\u8c03\u7528\u7684\u65b9\u6cd5\uff0c\u4f8b\u5982 OnGet\u3002\u5f53\u6211\u4eec\u8bf4\u201c\u6267\u884c Razor \u9875\u9762\u201d\u65f6\uff0c\u6211\u4eec\u5b9e\u9645\u4e0a\u662f\u6307\u201c\u521b\u5efa Razor \u9875\u9762\u7684 PageModel \u7684\u5b9e\u4f8b\uff0c\u5e76\u8c03\u7528\u6a21\u578b\u4e0a\u7684\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u201d\u3002Razor Pages \u53ef\u4ee5\u6709\u591a\u4e2a\u9875\u9762\u5904\u7406\u7a0b\u5e8f\uff0c\u56e0\u6b64\uff0c\u4e00\u65e6 RoutingMiddleware \u9009\u62e9\u4e86 Razor Page\uff0cEndpointMiddleware \u4ecd\u9700\u8981\u9009\u62e9\u8981\u6267\u884c\u7684\u5904\u7406\u7a0b\u5e8f\u3002\u5728\u7b2c 15 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u6846\u67b6\u5982\u4f55\u9009\u62e9\u8981\u8c03\u7528\u7684\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>By default, each Razor Page creates a single route template based on its file path. The exception to this rule is for Razor Pages that are called Index.cshtml. Index.cshtml pages create two route templates, one ending with &quot;Index&quot; and the other without this suffix. If you have a Razor Page at the path Pages\/ToDo\/Index.cshtml, you have two route templates that point to the same page:<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6bcf\u4e2a Razor \u9875\u9762\u90fd\u4f1a\u6839\u636e\u5176\u6587\u4ef6\u8def\u5f84\u521b\u5efa\u4e00\u4e2a\u8def\u7531\u6a21\u677f\u3002\u6b64\u89c4\u5219\u7684\u4f8b\u5916\u60c5\u51b5\u662f\u540d\u4e3a Index.cshtml \u7684 Razor \u9875\u9762\u3002Index.cshtml \u9875\u9762\u521b\u5efa\u4e24\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u4e00\u4e2a\u4ee5\u201cIndex\u201d\u7ed3\u5c3e\uff0c\u53e6\u4e00\u4e2a\u4e0d\u5e26\u6b64\u540e\u7f00\u3002\u5982\u679c\u5728\u8def\u5f84 Pages\/ToDo\/Index.cshtml \u4e2d\u6709\u4e00\u4e2a Razor \u9875\u9762\uff0c\u5219\u6709\u4e24\u4e2a\u6307\u5411\u540c\u4e00\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\uff1a<\/p>\n<p>\u2022  &quot;ToDo&quot;<br \/>\n\u2022  &quot;ToDo\/Index&quot;<\/p>\n<p>When either of these routes is matched, the same Index.cshtml Razor Page is selected. If your application is running at the URL <a href=\"https:\/\/example.org\">https:\/\/example.org<\/a>, you can view the page by executing <a href=\"https:\/\/example.org\/ToDo\">https:\/\/example.org\/ToDo<\/a> or <a href=\"https:\/\/example.org\/ToDo\/Index\">https:\/\/example.org\/ToDo\/Index<\/a>.<br \/>\n\u5f53\u8fd9\u4e9b\u8def\u7531\u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a\u5339\u914d\u65f6\uff0c\u5c06\u9009\u62e9\u76f8\u540c\u7684 Index.cshtml Razor \u9875\u9762\u3002\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5728 URL <a href=\"https:\/\/example.org\">https:\/\/example.org<\/a> \u4e0a\u8fd0\u884c\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u6267\u884c <a href=\"https:\/\/example.org\/ToDo\">https:\/\/example.org\/ToDo<\/a> \u6216 <a href=\"https:\/\/example.org\/ToDo\/Index\">https:\/\/example.org\/ToDo\/Index<\/a> \u6765\u67e5\u770b\u9875\u9762\u3002<\/p>\n<p><b>Warning<\/b> You must watch out for overlapping routes when using Index.cshtml pages. For example, if you add the Pages\/ToDo\/Index.cshtml page in the above example you must not add a Pages\/ToDo.cshtml page, as you\u2019ll get an exception at runtime when you navigate to \/todo, as you\u2019ll see in section 14.6.<br \/>\n\u8b66\u544a:\u4f7f\u7528 Index.cshtml \u9875\u9762\u65f6\uff0c\u5fc5\u987b\u6ce8\u610f\u91cd\u53e0\u7684\u8def\u7531\u3002\u4f8b\u5982\uff0c\u5982\u679c\u5728\u4e0a\u8ff0\u793a\u4f8b\u4e2d\u6dfb\u52a0 Pages\/ToDo\/Index.cshtml \u9875\u9762\uff0c\u5219\u4e0d\u5f97\u6dfb\u52a0 Pages\/ToDo.cshtml \u9875\u9762\uff0c\u56e0\u4e3a\u5728\u5bfc\u822a\u5230 \/todo \u65f6\uff0c\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u5f02\u5e38\uff0c\u5982\u7b2c 14.6 \u8282\u6240\u793a\u3002<\/p>\n<p>As a final example, consider the Razor Pages created by default when you create a Razor Pages application by using Visual Studio or running dotnet new razor using the .NET command-line interface (CLI), as we did in chapter 13. The standard template includes three Razor Pages in the Pages directory:<br \/>\n\u4f5c\u4e3a\u6700\u540e\u4e00\u4e2a\u793a\u4f8b\uff0c\u8bf7\u8003\u8651\u4f7f\u7528 Visual Studio \u6216\u4f7f\u7528 .NET \u547d\u4ee4\u884c\u754c\u9762 \uff08CLI\uff09 \u8fd0\u884c dotnet new razor \u521b\u5efa Razor Pages \u5e94\u7528\u7a0b\u5e8f\u65f6\u9ed8\u8ba4\u521b\u5efa\u7684 Razor Pages\uff0c\u5c31\u50cf\u6211\u4eec\u5728\u7b2c 13 \u7ae0\u4e2d\u6240\u505a\u7684\u90a3\u6837\u3002\u6807\u51c6\u6a21\u677f\u5728 Pages \u76ee\u5f55\u4e2d\u5305\u542b\u4e09\u4e2a Razor Pages\uff1a<\/p>\n<p>\u2022  Pages\/Error.cshtml<br \/>\n\u2022  Pages\/Index.cshtml<br \/>\n\u2022  Pages\/Privacy.cshtml<\/p>\n<p>That creates a collection of four routes for the application, defined by the following templates:<br \/>\n\u8fd9\u5c06\u4e3a\u5e94\u7528\u7a0b\u5e8f\u521b\u5efa\u56db\u4e2a\u8def\u7531\u7684\u96c6\u5408\uff0c\u7531\u4ee5\u4e0b\u6a21\u677f\u5b9a\u4e49\uff1a<\/p>\n<p>\u2022  &quot;&quot; maps to Index.cshtml.<br \/>\n\u2022  &quot;Index&quot; maps to Index.cshtml.<br \/>\n\u2022  &quot;Error&quot; maps to Error.cshtml.<br \/>\n\u2022  &quot;Privacy&quot; maps to Privacy.cshtml.<\/p>\n<p>At this point, Razor Page routing probably feels laughably trivial, but this is the basics that you get for free with the default Razor Pages conventions, which are often sufficient for a large portion of any website. At some point, though, you\u2019ll find you need something more dynamic, such as using route parameters to include an ID in the URL. This is where the ability to customize your Razor Page route templates becomes useful.<br \/>\n\u5728\u8fd9\u4e00\u70b9\u4e0a\uff0cRazor Page \u8def\u7531\u53ef\u80fd\u611f\u89c9\u5fae\u4e0d\u8db3\u9053\uff0c\u4f46\u8fd9\u662f\u60a8\u901a\u8fc7\u9ed8\u8ba4 Razor Pages \u7ea6\u5b9a\u514d\u8d39\u83b7\u5f97\u7684\u57fa\u7840\u77e5\u8bc6\uff0c\u8fd9\u4e9b\u7ea6\u5b9a\u901a\u5e38\u8db3\u4ee5\u6ee1\u8db3\u4efb\u4f55\u7f51\u7ad9\u7684\u5927\u90e8\u5206\u9700\u6c42\u3002\u4f46\u662f\uff0c\u5728\u67d0\u4e9b\u65f6\u5019\uff0c\u60a8\u4f1a\u53d1\u73b0\u60a8\u9700\u8981\u66f4\u52a8\u6001\u7684\u4e1c\u897f\uff0c\u4f8b\u5982\u4f7f\u7528\u8def\u7531\u53c2\u6570\u5728 URL \u4e2d\u5305\u542b ID\u3002\u8fd9\u662f\u81ea\u5b9a\u4e49 Razor Page \u8def\u7531\u6a21\u677f\u7684\u529f\u80fd\u53d8\u5f97\u6709\u7528\u7684\u5730\u65b9\u3002<\/p>\n<h2>14.4 Customizing Razor Page route templates<\/h2>\n<p>14.4 \u81ea\u5b9a\u4e49 Razor Page \u8def\u7531\u6a21\u677f<\/p>\n<p>The route templates for a Razor Page are based on the file path by default, but you\u2019re also able to customize the final template for each page or even replace it. In this section I show how to customize the route templates for individual pages so you can customize your application\u2019s URLs and map multiple URLs to a single Razor Page.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cRazor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u57fa\u4e8e\u6587\u4ef6\u8def\u5f84\uff0c\u4f46\u4f60\u4e5f\u53ef\u4ee5\u4e3a\u6bcf\u4e2a\u9875\u9762\u81ea\u5b9a\u4e49\u6700\u7ec8\u6a21\u677f\uff0c\u751a\u81f3\u66ff\u6362\u5b83\u3002\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u81ea\u5b9a\u4e49\u5404\u4e2a\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\uff0c\u4ee5\u4fbf\u60a8\u53ef\u4ee5\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u7684 URL \u5e76\u5c06\u591a\u4e2a URL \u6620\u5c04\u5230\u5355\u4e2a Razor \u9875\u9762\u3002<\/p>\n<p>You may remember from chapter 6 that route templates consist of both literal segments and route parameters, as shown in figure 14.3. By default, Razor Pages have URLs consisting of a series of literal segments, such as &quot;ToDo\/Index&quot;.<br \/>\n\u60a8\u53ef\u80fd\u8fd8\u8bb0\u5f97\u7b2c 6 \u7ae0\u4e2d\u7684\u8def\u7531\u6a21\u677f\u7531\u6587\u5b57\u6bb5\u548c\u8def\u7531\u53c2\u6570\u7ec4\u6210\uff0c\u5982\u56fe 14.3 \u6240\u793a\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cRazor Pages \u7684 URL \u7531\u4e00\u7cfb\u5217\u6587\u672c\u6bb5\u7ec4\u6210\uff0c\u4f8b\u5982\u201cToDo\/Index\u201d\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1403.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 14.3 A simple route template showing a literal segment and two required route parameters<br \/>\n\u56fe 14.3 \u4e00\u4e2a\u7b80\u5355\u7684\u8def\u7531\u6a21\u677f\uff0c\u663e\u793a\u4e86\u4e00\u4e2a\u6587\u5b57\u6bb5\u548c\u4e24\u4e2a\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570<\/p>\n<p>Literal segments and route parameters are the two cornerstones of ASP.NET Core route templates, but how can you customize a Razor Page to use one of these patterns? In section 14.4.1 you\u2019ll see how to add a segment to the end of a Razor Page\u2019s route template, and in section 14.4.2 you\u2019ll see how to replace the route template completely.<br \/>\n\u6587\u672c\u6bb5\u548c\u8def\u7531\u53c2\u6570\u662f ASP.NET Core \u8def\u7531\u6a21\u677f\u7684\u4e24\u4e2a\u57fa\u77f3\uff0c\u4f46\u5982\u4f55\u81ea\u5b9a\u4e49 Razor \u9875\u9762\u4ee5\u4f7f\u7528\u8fd9\u4e9b\u6a21\u5f0f\u4e4b\u4e00\uff1f\u5728\u7b2c 14.4.1 \u8282\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5c06\u6bb5\u6dfb\u52a0\u5230 Razor \u9875\u9762\u8def\u7531\u6a21\u677f\u7684\u672b\u5c3e\uff0c\u5728\u7b2c 14.4.2 \u8282\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5b8c\u5168\u66ff\u6362\u8def\u7531\u6a21\u677f\u3002<\/p>\n<h3>14.4.1 Adding a segment to a Razor Page route template<\/h3>\n<p>14.4.1 \u5411 Razor Page \u8def\u7531\u6a21\u677f\u6dfb\u52a0\u533a\u6bb5<\/p>\n<p>To customize the Razor Page route template, you update the @page directive at the top of the Razor Page\u2019s .cshtml file. This directive must be the first thing in the Razor Page file for the page to be registered correctly.<br \/>\n\u82e5\u8981\u81ea\u5b9a\u4e49 Razor Page \u8def\u7531\u6a21\u677f\uff0c\u8bf7\u66f4\u65b0 Razor Page \u7684 .cshtml \u6587\u4ef6\u9876\u90e8\u7684 @page \u6307\u4ee4\u3002\u6b64\u6307\u4ee4\u5fc5\u987b\u662f Razor Page \u6587\u4ef6\u4e2d\u7684\u7b2c\u4e00\u9879\u4f5c\uff0c\u624d\u80fd\u6b63\u786e\u6ce8\u518c\u9875\u9762\u3002<\/p>\n<p>To add an extra segment to a Razor Page\u2019s route template, add a space followed by the extra route template segment, after the @page statement. To add &quot;Extra&quot; to a Razor Page\u2019s route template, for example, use<br \/>\n\u82e5\u8981\u5411 Razor Page \u7684\u8def\u7531\u6a21\u677f\u6dfb\u52a0\u989d\u5916\u6bb5\uff0c\u8bf7\u5728 @page \u8bed\u53e5\u540e\u6dfb\u52a0\u4e00\u4e2a\u7a7a\u683c\uff0c\u540e\u8ddf\u989d\u5916\u7684\u8def\u7531\u6a21\u677f\u6bb5\u3002\u4f8b\u5982\uff0c\u8981\u5c06\u201cExtra\u201d\u6dfb\u52a0\u5230 Razor Page \u7684\u8def\u7531\u6a21\u677f\uff0c\u8bf7\u4f7f\u7528<\/p>\n<pre><code>@page &quot;Extra&quot;<\/code><\/pre>\n<p>This appends the provided route template to the default template generated for the Razor Page. The default route template for the Razor Page at Pages\/Privacy.html, for example, is &quot;Privacy&quot;. With the preceding directive, the new route template for the page would be &quot;Privacy\/Extra&quot;.<br \/>\n\u8fd9\u4f1a\u5c06\u63d0\u4f9b\u7684\u8def\u7531\u6a21\u677f\u8ffd\u52a0\u5230\u4e3a Razor \u9875\u9762\u751f\u6210\u7684\u9ed8\u8ba4\u6a21\u677f\u3002\u4f8b\u5982\uff0cPages\/Privacy.html \u7684 Razor Page \u7684\u9ed8\u8ba4\u8def\u7531\u6a21\u677f\u4e3a\u201c\u9690\u79c1\u201d\u3002\u4f7f\u7528\u4e0a\u8ff0\u6307\u4ee4\uff0c\u9875\u9762\u7684\u65b0\u8def\u7531\u6a21\u677f\u5c06\u4e3a \u201cPrivacy\/Extra\u201d\u3002<\/p>\n<p>The most common reason for customizing a Razor Page\u2019s route template like this is to add a route parameter. You could have a single Razor Page for displaying the products in an e-commerce site at the path Pages\/Products.cshtml and use a route parameter in the @page directive<br \/>\n\u50cf\u8fd9\u6837\u81ea\u5b9a\u4e49 Razor Page \u7684\u8def\u7531\u6a21\u677f\u7684\u6700\u5e38\u89c1\u539f\u56e0\u662f\u6dfb\u52a0\u8def\u7531\u53c2\u6570\u3002\u53ef\u4ee5\u6709\u4e00\u4e2a Razor \u9875\u9762\uff0c\u7528\u4e8e\u5728\u8def\u5f84 Pages\/Products.cshtml \u7684\u7535\u5b50\u5546\u52a1\u7ad9\u70b9\u4e2d\u663e\u793a\u4ea7\u54c1\uff0c\u5e76\u5728 @page \u6307\u4ee4\u4e2d\u4f7f\u7528 route \u53c2\u6570<\/p>\n<pre><code>@page &quot;{category}\/{name}&quot;<\/code><\/pre>\n<p>This would give a final route template of Products\/{category}\/{name}, which would match all the following URLs:<br \/>\n\u8fd9\u5c06\u63d0\u4f9b Products\/{category}\/{name} \u7684\u6700\u7ec8\u8def\u7531\u6a21\u677f\uff0c\u8be5\u6a21\u677f\u5c06\u5339\u914d\u4ee5\u4e0b\u6240\u6709 URL\uff1a<\/p>\n<p>\u2022  \/products\/bags\/white-rucksack<br \/>\n\u2022  \/products\/shoes\/black-size9<br \/>\n\u2022  \/Products\/phones\/iPhoneX<\/p>\n<p><b>NOTE<\/b> You can use the same routing features you learned about in chapter 6 with Razor Pages, including optional parameters, default parameters, and constraints.<br \/>\n\u6ce8\u610f:\u60a8\u53ef\u4ee5\u5c06\u5728\u7b2c 6 \u7ae0\u4e2d\u5b66\u5230\u7684\u76f8\u540c\u8def\u7531\u529f\u80fd\u7528\u4e8e Razor Pages\uff0c\u5305\u62ec\u53ef\u9009\u53c2\u6570\u3001\u9ed8\u8ba4\u53c2\u6570\u548c\u7ea6\u675f\u3002<\/p>\n<p>It\u2019s common to add route segments to the Razor Page template like this, but what if that\u2019s not enough? Maybe you don\u2019t want to have the \/products segment at the start of the preceding URLs, or you want to use a completely custom URL for a page. Luckily, that\u2019s just as easy to achieve.<br \/>\n\u50cf\u8fd9\u6837\u5411 Razor Page \u6a21\u677f\u6dfb\u52a0\u8def\u7ebf\u6bb5\u662f\u5f88\u5e38\u89c1\u7684\uff0c\u4f46\u5982\u679c\u8fd9\u8fd8\u4e0d\u591f\u600e\u4e48\u529e\uff1f\u4e5f\u8bb8\u60a8\u4e0d\u5e0c\u671b\u5728\u524d\u9762\u7684 URL \u5f00\u5934\u6709 \/products \u6bb5\uff0c\u6216\u8005\u60a8\u5e0c\u671b\u5bf9\u9875\u9762\u4f7f\u7528\u5b8c\u5168\u81ea\u5b9a\u4e49\u7684 URL\u3002\u5e78\u8fd0\u7684\u662f\uff0c\u8fd9\u540c\u6837\u5bb9\u6613\u5b9e\u73b0\u3002<\/p>\n<h3>14.4.2 Replacing a Razor Page route template completely<\/h3>\n<p>14.4.2 \u5b8c\u5168\u66ff\u6362 Razor Page \u8def\u7531\u6a21\u677f<\/p>\n<p>You\u2019ll be most productive working with Razor Pages if you can stick to the default routing conventions where possible, adding extra segments for route parameters where necessary. But sometimes you need more control. That\u2019s often the case for important pages in your application, such as the checkout page for an e-commerce application or even product pages, as you saw in the previous section.<br \/>\n\u5982\u679c\u60a8\u53ef\u4ee5\u5c3d\u53ef\u80fd\u575a\u6301\u9ed8\u8ba4\u8def\u7531\u7ea6\u5b9a\uff0c\u5e76\u5728\u5fc5\u8981\u65f6\u4e3a\u8def\u7531\u53c2\u6570\u6dfb\u52a0\u989d\u5916\u7684\u6bb5\uff0c\u90a3\u4e48\u4f7f\u7528 Razor Pages \u7684\u6548\u7387\u5c06\u6700\u9ad8\u3002\u4f46\u6709\u65f6\u4f60\u9700\u8981\u66f4\u591a\u7684\u63a7\u5236\u3002\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u91cd\u8981\u9875\u9762\u901a\u5e38\u5c31\u662f\u8fd9\u79cd\u60c5\u51b5\uff0c\u4f8b\u5982\u7535\u5b50\u5546\u52a1\u5e94\u7528\u7a0b\u5e8f\u7684\u7ed3\u5e10\u9875\u9762\uff0c\u751a\u81f3\u662f\u4ea7\u54c1\u9875\u9762\uff0c\u5982\u4e0a\u4e00\u8282\u6240\u793a\u3002<\/p>\n<p>To specify a custom route for a Razor Page, prefix the route with \/ in the @page directive. To remove the &quot;product\/&quot; prefix from the route templates in section 14.4.1, use this directive:<br \/>\n\u82e5\u8981\u4e3a Razor Page \u6307\u5b9a\u81ea\u5b9a\u4e49\u8def\u7531\uff0c\u8bf7\u5728 @page \u6307\u4ee4\u4e2d\u4e3a\u8def\u7531\u6dfb\u52a0 \/ \u524d\u7f00\u3002\u8981\u4ece\u7b2c 14.4.1 \u8282\u4e2d\u7684\u8def\u7531\u6a21\u677f\u4e2d\u5220\u9664 \u201cproduct\/\u201d \u524d\u7f00\uff0c\u8bf7\u4f7f\u7528\u4ee5\u4e0b\u6307\u4ee4\uff1a<\/p>\n<pre><code>@page &quot;\/{category}\/{name}&quot;<\/code><\/pre>\n<p>Note that this directive includes the &quot;\/&quot; at the start of the route, indicating that this is a custom route template, instead of an addition. The route template for this page will be &quot;{category}\/{name}&quot; no matter which Razor Page it is applied to.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u6b64\u6307\u4ee4\u5728\u8def\u7531\u7684\u5f00\u5934\u5305\u542b \u201c\/\u201d\uff0c\u8868\u793a\u8fd9\u662f\u4e00\u4e2a\u81ea\u5b9a\u4e49\u8def\u7531\u6a21\u677f\uff0c\u800c\u4e0d\u662f\u9644\u52a0\u6a21\u677f\u3002\u6b64\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u5c06\u4e3a\u201c{category}\/{name}\u201d\uff0c\u65e0\u8bba\u5b83\u5e94\u7528\u4e8e\u54ea\u4e2a Razor \u9875\u9762\u3002<\/p>\n<p>Similarly, you can create a static custom template for a page by starting the template with a &quot;\/&quot; and using only literal segments:<br \/>\n\u540c\u6837\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ee5 \u201c\/\u201d \u5f00\u5934\u5e76\u4ec5\u4f7f\u7528\u6587\u5b57\u6bb5\u6765\u4e3a\u9875\u9762\u521b\u5efa\u9759\u6001\u81ea\u5b9a\u4e49\u6a21\u677f\uff1a<\/p>\n<pre><code>@page &quot;\/checkout&quot;<\/code><\/pre>\n<p>Wherever you place your checkout Razor Page within the Pages folder, using this directive ensures that it always has the route template &quot;checkout&quot;, so it always matches the request URL \/checkout.<br \/>\n\u5c06 checkout Razor Page \u653e\u5728 Pages \u6587\u4ef6\u5939\u4e2d\u7684\u54ea\u4e2a\u4f4d\u7f6e\uff0c\u4f7f\u7528\u6b64\u6307\u4ee4\u53ef\u786e\u4fdd\u5b83\u59cb\u7ec8\u5177\u6709\u8def\u7531\u6a21\u677f\u201ccheckout\u201d\uff0c\u56e0\u6b64\u5b83\u59cb\u7ec8\u4e0e\u8bf7\u6c42 URL \/checkout \u5339\u914d\u3002<\/p>\n<p><b>Tip<\/b> You can also think of custom route templates that start with \u201c\/\u201d as absolute route templates, whereas other route templates are relative to their location in the file hierarchy.<br \/>\n\u63d0\u793a\uff1a\u60a8\u8fd8\u53ef\u4ee5\u5c06\u4ee5 \u201c\/\u201d \u5f00\u5934\u7684\u81ea\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u89c6\u4e3a\u7edd\u5bf9\u8def\u7531\u6a21\u677f\uff0c\u800c\u5176\u4ed6\u8def\u7531\u6a21\u677f\u5219\u662f\u76f8\u5bf9\u4e8e\u5b83\u4eec\u5728\u6587\u4ef6\u5c42\u6b21\u7ed3\u6784\u4e2d\u7684\u4f4d\u7f6e\u7684\u6a21\u677f\u3002<\/p>\n<p>It\u2019s important to note that when you customize the route template for a Razor Page, both when appending to the default and when replacing it with a custom route, the default template is no longer valid. If you use the &quot;checkout&quot; route template above on a Razor Page located at Pages\/Payment.cshtml, you can access it only by using the URL \/checkout; the URL \/Payment is no longer valid and won\u2019t execute the Razor Page.<br \/>\n\u8bf7\u52a1\u5fc5\u6ce8\u610f\uff0c\u5f53\u60a8\u81ea\u5b9a\u4e49 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u65f6\uff0c\u65e0\u8bba\u662f\u8ffd\u52a0\u5230\u9ed8\u8ba4\u6a21\u677f\u8fd8\u662f\u5c06\u5176\u66ff\u6362\u4e3a\u81ea\u5b9a\u4e49\u8def\u7531\uff0c\u9ed8\u8ba4\u6a21\u677f\u90fd\u4e0d\u518d\u6709\u6548\u3002\u5982\u679c\u5728\u4f4d\u4e8e Pages\/Payment.cshtml \u7684 Razor \u9875\u9762\u4e0a\u4f7f\u7528\u4e0a\u8ff0\u201ccheckout\u201d\u8def\u7531\u6a21\u677f\uff0c\u5219\u53ea\u80fd\u4f7f\u7528 URL \/checkout \u8bbf\u95ee\u5b83;URL \/Payment \u4e0d\u518d\u6709\u6548\uff0c\u5e76\u4e14\u4e0d\u4f1a\u6267\u884c Razor \u9875\u9762\u3002<\/p>\n<p><b>Tip<\/b> Customizing the route template for a Razor Page using the @page directive replaces the default route template for the page. In section 14.6 I show how you can add extra routes while preserving the default route template.<br \/>\n\u63d0\u793a\uff1a\u4f7f\u7528 @page \u6307\u4ee4\u81ea\u5b9a\u4e49 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u4f1a\u66ff\u6362\u9875\u9762\u7684\u9ed8\u8ba4\u8def\u7531\u6a21\u677f\u3002\u5728\u7b2c 14.6 \u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u5728\u4fdd\u7559\u9ed8\u8ba4\u8def\u7531\u6a21\u677f\u7684\u540c\u65f6\u6dfb\u52a0\u989d\u5916\u7684\u8def\u7531\u3002<\/p>\n<p>In this section you learned how to customize the route template for a Razor Page. For the most part, routing to Razor Pages works like minimal APIs, the main difference being that the route templates are created using conventions. When it comes to the other half of routing\u2014generating URLs\u2014Razor Pages and minimal APIs are also similar, but Razor Pages gives you some nice helpers.<br \/>\n\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u4f60\u5b66\u4e60\u4e86\u5982\u4f55\u81ea\u5b9a\u4e49 Razor Page \u7684\u8def\u7531\u6a21\u677f\u3002\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u8def\u7531\u5230 Razor Pages \u7684\u5de5\u4f5c\u65b9\u5f0f\u7c7b\u4f3c\u4e8e\u6700\u5c0f\u7684 API\uff0c\u4e3b\u8981\u533a\u522b\u5728\u4e8e\u8def\u7531\u6a21\u677f\u662f\u4f7f\u7528\u7ea6\u5b9a\u521b\u5efa\u7684\u3002\u5f53\u6d89\u53ca\u5230\u8def\u7531\u7684\u53e6\u4e00\u534a\uff08\u751f\u6210 URL\uff09\u65f6\uff0cRazor Pages \u548c\u6700\u5c0f API \u4e5f\u7c7b\u4f3c\uff0c\u4f46 Razor Pages \u63d0\u4f9b\u4e86\u4e00\u4e9b\u4e0d\u9519\u7684\u5e2e\u624b\u3002<\/p>\n<h2>14.5 Generating URLs for Razor Pages<\/h2>\n<p>14.5 \u4e3a Razor Pages \u751f\u6210 URL<\/p>\n<p>In this section you\u2019ll learn how to generate URLs for your Razor Pages using the IUrlHelper that\u2019s part of the Razor Pages PageModel type. You\u2019ll also learn to use the LinkGenerator service you saw in chapter 6 for generating URLs with minimal APIs.<br \/>\n\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528 IUrlHelper\uff08\u5c5e\u4e8e Razor Pages PageModel \u7c7b\u578b\u7684\u4e00\u90e8\u5206\uff09\u4e3a Razor Pages \u751f\u6210 URL\u3002\u60a8\u8fd8\u5c06\u5b66\u4e60\u4f7f\u7528\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u770b\u5230\u7684 LinkGenerator \u670d\u52a1\u6765\u751f\u6210\u5177\u6709\u6700\u5c11 API \u7684 URL\u3002<\/p>\n<p>One of the benefits of using convention-based routing in Razor Pages is that your URLs can be somewhat fluid. If you rename a Razor Page, the URL associated with that page also changes. Renaming the Pages\/Cart.cshtml page to Pages\/Basket\/View.cshtml, for example, causes the URL you use to access the page to change from \/Cart to \/Basket\/View.<br \/>\n\u5728 Razor Pages \u4e2d\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u7684\u597d\u5904\u4e4b\u4e00\u662f\uff0c\u60a8\u7684 URL \u53ef\u4ee5\u4fdd\u6301\u4e00\u5b9a\u7684\u6d41\u52a8\u6027\u3002\u5982\u679c\u91cd\u547d\u540d Razor \u9875\u9762\uff0c\u5219\u4e0e\u8be5\u9875\u9762\u5173\u8054\u7684 URL \u4e5f\u4f1a\u66f4\u6539\u3002\u4f8b\u5982\uff0c\u5c06 Pages\/Cart.cshtml \u9875\u9762\u91cd\u547d\u540d\u4e3a Pages\/Basket\/View.cshtml \u4f1a\u5bfc\u81f4\u7528\u4e8e\u8bbf\u95ee\u9875\u9762\u7684 URL \u4ece \/Cart \u66f4\u6539\u4e3a \/Basket\/View\u3002<\/p>\n<p>To track these changes (and to avoid broken links), you can use the routing infrastructure to generate the URLs that you output in your Razor Page HTML and that you include in your HTTP responses. In chapter 6 you saw how to generate URLs for your minimal API endpoints, and in this section, you\u2019ll see how to do the same for your Razor Pages. I also describe how to generate URLs for MVC controllers, as the mechanism is virtually identical to that used by Razor Pages.<br \/>\n\u82e5\u8981\u8ddf\u8e2a\u8fd9\u4e9b\u66f4\u6539\uff08\u5e76\u907f\u514d\u94fe\u63a5\u65ad\u5f00\uff09\uff0c\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u57fa\u7840\u7ed3\u6784\u751f\u6210\u5728 Razor \u9875\u9762 HTML \u4e2d\u8f93\u51fa\u5e76\u5305\u542b\u5728 HTTP \u54cd\u5e94\u4e2d\u7684 URL\u3002\u5728\u7b2c 6 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u5982\u4f55\u4e3a\u6700\u5c0f API \u7aef\u70b9\u751f\u6210 URL\uff0c\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4e3a Razor \u9875\u9762\u6267\u884c\u76f8\u540c\u7684\u4f5c\u3002\u6211\u8fd8\u4ecb\u7ecd\u4e86\u5982\u4f55\u4e3a MVC \u63a7\u5236\u5668\u751f\u6210 URL\uff0c\u56e0\u4e3a\u8be5\u673a\u5236\u4e0e Razor Pages \u4f7f\u7528\u7684\u673a\u5236\u51e0\u4e4e\u76f8\u540c\u3002<\/p>\n<h3>14.5.1 Generating URLs for a Razor Page<\/h3>\n<p>14.5.1 \u4e3a Razor \u9875\u9762\u751f\u6210 URL<\/p>\n<p>You\u2019ll need to generate URLs in various places in your application, and one common location is in your Razor Pages and MVC controllers. The following listing shows how you could generate a link to the Pages\/Currency\/View.cshtml Razor Page, using the Url helper from the PageModel base class.<br \/>\n\u60a8\u9700\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u4e0d\u540c\u4f4d\u7f6e\u751f\u6210 URL\uff0c\u4e00\u4e2a\u5e38\u89c1\u4f4d\u7f6e\u4f4d\u4e8e Razor Pages \u548c MVC \u63a7\u5236\u5668\u4e2d\u3002\u4ee5\u4e0b\u5217\u8868\u663e\u793a\u4e86\u5982\u4f55\u4f7f\u7528 PageModel \u57fa\u7c7b\u4e2d\u7684 Url \u5e2e\u52a9\u7a0b\u5e8f\u751f\u6210\u6307\u5411 Pages\/Currency\/View.cshtml Razor \u9875\u9762\u7684\u94fe\u63a5\u3002<\/p>\n<p>Listing 14.2 Generating a URL using IUrlHelper and the Razor Page name<br \/>\n\u5217\u8868 14.2 \u4f7f\u7528 IUrlHelper \u548c Razor \u9875\u9762\u540d\u79f0\u751f\u6210 URL<\/p>\n<pre><code>public class IndexModel : PageModel \u2776\n{\npublic void OnGet()\n{\nvar url = Url.Page(&quot;Currency\/View&quot;, new { code = &quot;USD&quot; }); \u2777\n}\n}<\/code><\/pre>\n<p>\u2776 Deriving from PageModel gives access to the Url property.<br \/>\n\u4ece PageModel \u6d3e\u751f\u63d0\u4f9b\u5bf9 Url \u5c5e\u6027\u7684\u8bbf\u95ee\u6743\u9650\u3002<br \/>\n\u2777 You provide the relative path to the Razor Page, along with any additional route values.<br \/>\n\u63d0\u4f9b Razor \u9875\u9762\u7684\u76f8\u5bf9\u8def\u5f84\uff0c\u4ee5\u53ca\u4efb\u4f55\u5176\u4ed6\u8def\u7531\u503c\u3002<\/p>\n<p>The Url property is an instance of IUrlHelper that allows you to easily generate URLs for your application by referencing other Razor Pages by their file path.<br \/>\nUrl \u5c5e\u6027\u662f IUrlHelper \u7684\u4e00\u4e2a\u5b9e\u4f8b\uff0c\u5b83\u5141\u8bb8\u4f60\u901a\u8fc7\u6309\u6587\u4ef6\u8def\u5f84\u5f15\u7528\u5176\u4ed6 Razor \u9875\u9762\uff0c\u8f7b\u677e\u5730\u4e3a\u5e94\u7528\u7a0b\u5e8f\u751f\u6210 URL\u3002<\/p>\n<p><b>NOTE<\/b> IUrlHelper is a wrapper around the LinkGenerator class you learned about in chapter 6. IUrlHelper adds some shortcuts for generating URLs based on the current request.<br \/>\n\u6ce8\u610f\uff1aIUrlHelper \u662f\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4e86\u89e3\u7684 LinkGenerator \u7c7b\u7684\u5305\u88c5\u5668\u3002IUrlHelper \u6dfb\u52a0\u4e86\u4e00\u4e9b\u5feb\u6377\u65b9\u5f0f\uff0c\u7528\u4e8e\u6839\u636e\u5f53\u524d\u8bf7\u6c42\u751f\u6210 URL\u3002<\/p>\n<p>IUrlHelper exposes a Page() method to which you pass the name of the Razor Page and any additional route data as an anonymous object. Then the helper generates a URL based on the referenced page\u2019s route template.<br \/>\nIUrlHelper \u516c\u5f00\u4e00\u4e2a Page\uff08\uff09 \u65b9\u6cd5\uff0c\u5c06 Razor Page \u7684\u540d\u79f0\u548c\u4efb\u4f55\u5176\u4ed6\u8def\u7531\u6570\u636e\u4f5c\u4e3a\u533f\u540d\u5bf9\u8c61\u4f20\u9012\u7ed9\u8be5\u65b9\u6cd5\u3002\u7136\u540e\uff0c\u5e2e\u52a9\u7a0b\u5e8f\u6839\u636e\u5f15\u7528\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u751f\u6210 URL\u3002<\/p>\n<p><b>Tip<\/b> You can provide the relative file path to the Razor Page, as shown in listing 14.2. Alternatively, you can provide the absolute file path (relative to the Pages folder) by starting the path with a &quot;\/&quot;, such as &quot;\/Currency\/View&quot;.<br \/>\n\u63d0\u793a\uff1a\u60a8\u53ef\u4ee5\u63d0\u4f9b Razor Page \u7684\u76f8\u5bf9\u6587\u4ef6\u8def\u5f84\uff0c\u5982\u6e05\u5355 14.2 \u6240\u793a\u3002\u6216\u8005\uff0c\u60a8\u4e5f\u53ef\u4ee5\u901a\u8fc7\u4ee5\u201c\/\u201d\u5f00\u5934\u6765\u63d0\u4f9b\u7edd\u5bf9\u6587\u4ef6\u8def\u5f84\uff08\u76f8\u5bf9\u4e8e Pages \u6587\u4ef6\u5939\uff09\uff0c\u4f8b\u5982\u201c\/Currency\/View\u201d\u3002<\/p>\n<p>IUrlHelper has several different overloads of the Page() method. Some of these methods allow you to specify a specific page handler, others let you generate an absolute URL instead of a relative URL, and some let you pass in additional route values.<br \/>\nIUrlHelper \u5177\u6709 Page\uff08\uff09 \u65b9\u6cd5\u7684\u51e0\u4e2a\u4e0d\u540c\u7684\u91cd\u8f7d\u3002\u5176\u4e2d\u4e00\u4e9b\u65b9\u6cd5\u5141\u8bb8\u60a8\u6307\u5b9a\u7279\u5b9a\u7684\u9875\u9762\u5904\u7406\u7a0b\u5e8f\uff0c\u5176\u4ed6\u65b9\u6cd5\u5141\u8bb8\u60a8\u751f\u6210\u7edd\u5bf9 URL \u800c\u4e0d\u662f\u76f8\u5bf9 URL\uff0c\u800c\u53e6\u4e00\u4e9b\u65b9\u6cd5\u5141\u8bb8\u60a8\u4f20\u5165\u5176\u4ed6\u8def\u7531\u503c\u3002<\/p>\n<p>In listing 14.2, as well as providing the file path I passed in an anonymous object, new { code = &quot;USD&quot; }. This object provides additional route values when generating the URL, in this case setting the code parameter to &quot;USD&quot;, as you did when generating URLs for minimal APIs with LinkGenerator in chapter 6. As before, the code value is used in the URL directly if it corresponds to a route parameter. Otherwise, it\u2019s appended as additional data in the query string.<br \/>\n\u5728\u6e05\u5355 14.2 \u4e2d\uff0c\u9664\u4e86\u63d0\u4f9b\u6211\u5728\u533f\u540d\u5bf9\u8c61\u4e2d\u4f20\u9012\u7684\u6587\u4ef6\u8def\u5f84\u5916\uff0cnew { code = \u201cUSD\u201d }.\u6b64\u5bf9\u8c61\u5728\u751f\u6210 URL \u65f6\u63d0\u4f9b\u989d\u5916\u7684\u8def\u7531\u503c\uff0c\u5728\u672c\u4f8b\u4e2d\u5c06 code \u53c2\u6570\u8bbe\u7f6e\u4e3a \u201cUSD\u201d\uff0c\u5c31\u50cf\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4f7f\u7528 LinkGenerator \u4e3a\u6700\u5c0f API \u751f\u6210 URL \u65f6\u6240\u505a\u7684\u90a3\u6837\u3002\u4e0e\u4ee5\u524d\u4e00\u6837\uff0c\u5982\u679c code \u503c\u5bf9\u5e94\u4e8e route \u53c2\u6570\uff0c\u5219\u76f4\u63a5\u5728 URL \u4e2d\u4f7f\u7528 code \u503c\u3002\u5426\u5219\uff0c\u5b83\u5c06\u4f5c\u4e3a\u9644\u52a0\u6570\u636e\u9644\u52a0\u5230\u67e5\u8be2\u5b57\u7b26\u4e32\u4e2d\u3002<\/p>\n<p>Generating URLs based on the page you want to execute is convenient, and it\u2019s the usual approach taken in most cases. If you\u2019re using MVC controllers for your APIs, the process is much the same as for Razor Pages, though the methods are slightly different.<br \/>\n\u6839\u636e\u60a8\u8981\u6267\u884c\u7684\u9875\u9762\u751f\u6210 URL \u5f88\u65b9\u4fbf\uff0c\u8fd9\u662f\u5927\u591a\u6570\u60c5\u51b5\u4e0b\u901a\u5e38\u91c7\u7528\u7684\u65b9\u6cd5\u3002\u5982\u679c\u5bf9 API \u4f7f\u7528 MVC \u63a7\u5236\u5668\uff0c\u5219\u8fc7\u7a0b\u4e0e Razor Pages \u5927\u81f4\u76f8\u540c\uff0c\u4f46\u65b9\u6cd5\u7565\u6709\u4e0d\u540c\u3002<\/p>\n<h3>14.5.2 Generating URLs for an MVC controller<\/h3>\n<p>14.5.2 \u4e3a MVC \u63a7\u5236\u5668\u751f\u6210 URL<\/p>\n<p>Generating URLs for MVC controllers is similar to Razor Pages. The main difference is that you use the Action method on the IUrlHelper, and you provide an MVC controller name and action name instead of a page path.<br \/>\n\u4e3a MVC \u63a7\u5236\u5668\u751f\u6210 URL \u7c7b\u4f3c\u4e8e Razor Pages\u3002\u4e3b\u8981\u533a\u522b\u5728\u4e8e\uff0c\u5728 IUrlHelper \u4e0a\u4f7f\u7528 Action \u65b9\u6cd5\uff0c\u5e76\u63d0\u4f9b MVC \u63a7\u5236\u5668\u540d\u79f0\u548c\u4f5c\u540d\u79f0\uff0c\u800c\u4e0d\u662f\u9875\u9762\u8def\u5f84\u3002<\/p>\n<p><b>NOTE<\/b> I\u2019ve covered MVC controllers only in passing, as I generally don\u2019t recommend them over Razor Pages or minimal APIs, so don\u2019t worry too much about them. We\u2019ll come back to MVC controllers in chapters 19 and 20; the main reason for mentioning them here is to point out how similar MVC controllers are to Razor Pages.<br \/>\n\u6ce8\u610f\uff1a\u6211\u53ea\u662f\u987a\u4fbf\u4ecb\u7ecd\u4e86 MVC \u63a7\u5236\u5668\uff0c\u56e0\u4e3a\u6211\u901a\u5e38\u4e0d\u5efa\u8bae\u5728 Razor Pages \u6216\u6700\u5c0f API \u4e0a\u4f7f\u7528\u5b83\u4eec\uff0c\u56e0\u6b64\u4e0d\u5fc5\u592a\u62c5\u5fc3\u5b83\u4eec\u3002\u6211\u4eec\u5c06\u5728\u7b2c 19 \u7ae0\u548c\u7b2c 20 \u7ae0\u56de\u5230 MVC \u63a7\u5236\u5668;\u5728\u8fd9\u91cc\u63d0\u5230\u5b83\u4eec\u7684\u4e3b\u8981\u539f\u56e0\u662f\u6307\u51fa MVC \u63a7\u5236\u5668\u4e0e Razor Pages \u7684\u76f8\u4f3c\u4e4b\u5904\u3002<\/p>\n<p>The following listing shows an MVC controller generating a link from one action method to another, using the Url helper from the Controller base class.<br \/>\n\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u4e00\u4e2a MVC \u63a7\u5236\u5668\uff0c\u5b83\u4f7f\u7528 Controller \u57fa\u7c7b\u4e2d\u7684 Url \u5e2e\u52a9\u7a0b\u5e8f\u751f\u6210\u4ece\u4e00\u4e2a\u4f5c\u65b9\u6cd5\u5230\u53e6\u4e00\u4e2a\u4f5c\u65b9\u6cd5\u7684\u94fe\u63a5\u3002<\/p>\n<p>Listing 14.3 Generating a URL using IUrlHelper and the action name<br \/>\n\u793a\u4f8b 14.3 \u4f7f\u7528 IUrlHelper \u548c\u4f5c\u540d\u79f0\u751f\u6210 URL<\/p>\n<pre><code>public class CurrencyController : Controller \u2776\n{\n[HttpGet(&quot;currency\/index&quot;)] \u2777\npublic IActionResult Index()\n{\nvar url = Url.Action(&quot;View&quot;, &quot;Currency&quot;, \u2778\nnew { code = &quot;USD&quot; }); \u2778\nreturn Content($&quot;The URL is {url}&quot;); \u2779\n}\n[HttpGet(&quot;currency\/view\/{code}&quot;)]\npublic IActionResult View(string code) \u277a\n{\n    \/* method implementation*\/\n}\n}<\/code><\/pre>\n<p>\u2776 Deriving from Controller gives access to the Url property.<br \/>\n\u4ece Controller \u6d3e\u751f\u53ef\u8bbf\u95ee Url \u5c5e\u6027\u3002<br \/>\n\u2777 Explicit route templates using attributes<br \/>\n\u4f7f\u7528\u5c5e\u6027\u7684\u663e\u5f0f\u8def\u7531\u6a21\u677f<br \/>\n\u2778 You provide the action and controller name to generate, along with any additional route values.<br \/>\n\u60a8\u63d0\u4f9b\u8981\u751f\u6210\u7684\u4f5c\u548c\u63a7\u5236\u5668\u540d\u79f0\uff0c\u4ee5\u53ca\u4efb\u4f55\u5176\u4ed6\u8def\u7531\u503c\u3002<br \/>\n\u2779 Returns \u201cThe URL is \/Currency\/View\/USD\u201d<br \/>\n\u8fd4\u56de\u201cURL is \/Currency\/View\/USD\u201d<br \/>\n\u277a The URL generated a route to this action method.<br \/>\nURL \u751f\u6210\u4e86\u5230\u6b64\u4f5c\u65b9\u6cd5\u7684\u8def\u7531\u3002<\/p>\n<p>You can call the Action and Page methods on IUrlHelper from both Razor Pages and MVC controllers, so you can generate links back and forth between them if you need to. The important question is, what is the destination of the URL? If the URL you need refers to a Razor Page, use the Page() method. If the destination is an MVC action, use the Action() method.<br \/>\n\u53ef\u4ee5\u4ece Razor Pages \u548c MVC \u63a7\u5236\u5668\u8c03\u7528 IUrlHelper \u4e0a\u7684 Action \u548c Page \u65b9\u6cd5\uff0c\u4ee5\u4fbf\u5728\u9700\u8981\u65f6\u53ef\u4ee5\u5728\u5b83\u4eec\u4e4b\u95f4\u6765\u56de\u751f\u6210\u94fe\u63a5\u3002\u91cd\u8981\u7684\u95ee\u9898\u662f\uff0cURL \u7684\u76ee\u7684\u5730\u662f\u4ec0\u4e48\uff1f\u5982\u679c\u60a8\u9700\u8981\u7684 URL \u5f15\u7528 Razor \u9875\u9762\uff0c\u8bf7\u4f7f\u7528 Page\uff08\uff09 \u65b9\u6cd5\u3002\u5982\u679c\u76ee\u6807\u662f MVC\u4f5c\uff0c\u8bf7\u4f7f\u7528 Action\uff08\uff09 \u65b9\u6cd5\u3002<\/p>\n<p><b>Tip<\/b> Instead of using strings for the name of the action method, use the C# 6 nameof operator to make the value refactor-safe, such as nameof(View).<br \/>\n\u63d0\u793a\uff1a\u4e0d\u8981\u4f7f\u7528\u5b57\u7b26\u4e32\u4f5c\u4e3a\u4f5c\u65b9\u6cd5\u7684\u540d\u79f0\uff0c\u800c\u662f\u4f7f\u7528 C# 6 nameof \u8fd0\u7b97\u7b26\u4f7f\u503c\u6210\u4e3a\u91cd\u6784\u5b89\u5168\u7684\uff0c\u4f8b\u5982 nameof\uff08View\uff09\u3002<\/p>\n<p>If you\u2019re routing to an action in the same controller, you can use a different overload of Action() that omits the controller name when generating the URL. The IUrlHelper uses ambient values from the current request and overrides them with any specific values you provide.<br \/>\n\u5982\u679c\u8981\u8def\u7531\u5230\u540c\u4e00\u63a7\u5236\u5668\u4e2d\u7684\u4f5c\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u4e0d\u540c\u7684 Action\uff08\uff09 \u91cd\u8f7d\uff0c\u5728\u751f\u6210 URL \u65f6\u7701\u7565\u63a7\u5236\u5668\u540d\u79f0\u3002IUrlHelper \u4f7f\u7528\u5f53\u524d\u8bf7\u6c42\u4e2d\u7684\u73af\u5883\u503c\uff0c\u5e76\u4f7f\u7528\u4f60\u63d0\u4f9b\u7684\u4efb\u4f55\u7279\u5b9a\u503c\u66ff\u4ee3\u5b83\u4eec\u3002<\/p>\n<p><b>DEFINITION<\/b> Ambient values are the route values for the current request. They include Controller and Action when called from an MVC controller and Page when called from a Razor Page. Ambient values can also include additional route values that were set when the action or Razor Page was initially located using routing. See Microsoft\u2019s \u201cRouting in ASP.NET Core\u201d documentation for further details: <a href=\"http:\/\/mng.bz\/OxoE\">http:\/\/mng.bz\/OxoE<\/a>.<br \/>\n\u5b9a\u4e49\uff1aAmbient \u503c\u662f\u5f53\u524d\u8bf7\u6c42\u7684\u8def\u7531\u503c\u3002\u5b83\u4eec\u5305\u62ec\u4ece MVC \u63a7\u5236\u5668\u8c03\u7528\u65f6\u7684 Controller \u548c Action\uff0c\u4ee5\u53ca\u4ece Razor \u9875\u9762\u8c03\u7528\u65f6\u7684 Page\u3002\u73af\u5883\u503c\u8fd8\u53ef\u4ee5\u5305\u62ec\u6700\u521d\u4f7f\u7528\u8def\u7531\u5b9a\u4f4d\u4f5c\u6216 Razor \u9875\u9762\u65f6\u8bbe\u7f6e\u7684\u5176\u4ed6\u8def\u7531\u503c\u3002\u6709\u5173\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u201cASP.NET Core \u4e2d\u7684\u8def\u7531\u201d\u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/OxoE\">http:\/\/mng.bz\/OxoE<\/a>\u3002<\/p>\n<p>IUrlHelper can make it simpler to generate URLs by reusing ambient values from the current request, though it also adds a layer of complexity, as the same method arguments can give a different generated URL depending on the page the method is called from.<br \/>\nIUrlHelper \u53ef\u4ee5\u901a\u8fc7\u91cd\u7528\u5f53\u524d\u8bf7\u6c42\u4e2d\u7684\u73af\u5883\u503c\u6765\u7b80\u5316\u751f\u6210 URL \u7684\u8fc7\u7a0b\uff0c\u4f46\u5b83\u4e5f\u589e\u52a0\u4e86\u4e00\u5c42\u590d\u6742\u6027\uff0c\u56e0\u4e3a\u76f8\u540c\u7684\u65b9\u6cd5\u53c2\u6570\u53ef\u80fd\u4f1a\u6839\u636e\u8c03\u7528\u8be5\u65b9\u6cd5\u7684\u9875\u9762\u63d0\u4f9b\u4e0d\u540c\u7684\u751f\u6210 URL\u3002<\/p>\n<p>If you need to generate URLs from parts of your application outside the Razor Page or MVC infrastructure, you won\u2019t be able to use the IUrlHelper helper. Instead, you can use the LinkGenerator class.<br \/>\n\u5982\u679c\u9700\u8981\u4ece Razor Page \u6216 MVC \u57fa\u7840\u7ed3\u6784\u5916\u90e8\u7684\u5e94\u7528\u7a0b\u5e8f\u90e8\u5206\u751f\u6210 URL\uff0c\u5219\u65e0\u6cd5\u4f7f\u7528 IUrlHelper \u5e2e\u52a9\u7a0b\u5e8f\u3002\u76f8\u53cd\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 LinkGenerator \u7c7b\u3002<\/p>\n<h3>14.5.3 Generating URLs with LinkGenerator<\/h3>\n<p>14.5.3 \u4f7f\u7528 LinkGenerator \u751f\u6210 URL<\/p>\n<p>In chapter 6 I described how to generate links to minimal API endpoints using the LinkGenerator class. By contrast with IUrlHelper, LinkGenerator requires that you always provide sufficient arguments to uniquely define the URL to generate. This makes it more verbose but also more consistent and has the advantage that it can be used anywhere in your application. This differs from IUrlHelper, which should be used only inside the context of a request.<br \/>\n\u5728\u7b2c 6 \u7ae0\u4e2d\uff0c\u6211\u4ecb\u7ecd\u4e86\u5982\u4f55\u4f7f\u7528 LinkGenerator \u7c7b\u751f\u6210\u6307\u5411\u6700\u5c0f API \u7aef\u70b9\u7684\u94fe\u63a5\u3002\u4e0e IUrlHelper \u76f8\u6bd4\uff0cLinkGenerator \u8981\u6c42\u60a8\u59cb\u7ec8\u63d0\u4f9b\u8db3\u591f\u7684\u53c2\u6570\u6765\u552f\u4e00\u5b9a\u4e49\u8981\u751f\u6210\u7684 URL\u3002\u8fd9\u4f7f\u5f97\u5b83\u66f4\u8be6\u7ec6\uff0c\u4f46\u4e5f\u66f4\u4e00\u81f4\uff0c\u5e76\u4e14\u5176\u4f18\u70b9\u662f\u5b83\u53ef\u4ee5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4efb\u4f55\u4f4d\u7f6e\u4f7f\u7528\u3002\u8fd9\u4e0e IUrlHelper \u4e0d\u540c\uff0c\u540e\u8005\u5e94\u4ec5\u5728\u8bf7\u6c42\u7684\u4e0a\u4e0b\u6587\u4e2d\u4f7f\u7528\u3002<\/p>\n<p>If you\u2019re writing your Razor Pages and MVC controllers following the advice from chapter 13, you should be trying to keep your Razor Pages relatively simple. That requires you to execute your application\u2019s business and domain logic in separate classes and services.<br \/>\n\u5982\u679c\u4f60\u6309\u7167\u7b2c 13 \u7ae0\u4e2d\u7684\u5efa\u8bae\u7f16\u5199 Razor Pages \u548c MVC \u63a7\u5236\u5668\uff0c\u4f60\u5e94\u8be5\u5c3d\u91cf\u4fdd\u6301 Razor Pages \u76f8\u5bf9\u7b80\u5355\u3002\u8fd9\u8981\u6c42\u60a8\u5728\u5355\u72ec\u7684\u7c7b\u548c\u670d\u52a1\u4e2d\u6267\u884c\u5e94\u7528\u7a0b\u5e8f\u7684\u4e1a\u52a1\u548c\u57df\u903b\u8f91\u3002<\/p>\n<p>For the most part, the URLs your application uses shouldn\u2019t be part of your domain logic. That makes it easier for your application to evolve over time or even to change completely. You may want to create a mobile application that reuses the business logic from an ASP.NET Core app, for example. In that case, using URLs in the business logic wouldn\u2019t make sense, as they wouldn\u2019t be correct when the logic is called from the mobile app!<br \/>\n\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u7684 URL \u4e0d\u5e94\u6210\u4e3a\u57df\u903b\u8f91\u7684\u4e00\u90e8\u5206\u3002\u8fd9\u4f7f\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u66f4\u5bb9\u6613\u968f\u7740\u65f6\u95f4\u7684\u63a8\u79fb\u800c\u53d1\u5c55\uff0c\u751a\u81f3\u66f4\u5bb9\u6613\u5b8c\u5168\u6539\u53d8\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u80fd\u5e0c\u671b\u521b\u5efa\u4e00\u4e2a\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u8be5\u5e94\u7528\u7a0b\u5e8f\u91cd\u7528 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4e1a\u52a1\u903b\u8f91\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u5728\u4e1a\u52a1\u903b\u8f91\u4e2d\u4f7f\u7528 URL \u5c31\u6ca1\u6709\u610f\u4e49\u4e86\uff0c\u56e0\u4e3a\u5f53\u4ece\u79fb\u52a8\u5e94\u7528\u7a0b\u5e8f\u8c03\u7528\u903b\u8f91\u65f6\uff0c\u5b83\u4eec\u5c06\u4e0d\u6b63\u786e\uff01<\/p>\n<p><b>Tip<\/b> Where possible, try to keep knowledge of the frontend application design out of your business logic. This pattern is known generally as the Dependency Inversion principle.<br \/>\n\u63d0\u793a\uff1a\u5728\u53ef\u80fd\u7684\u60c5\u51b5\u4e0b\uff0c\u5c3d\u91cf\u5c06\u524d\u7aef\u5e94\u7528\u7a0b\u5e8f\u8bbe\u8ba1\u77e5\u8bc6\u6392\u9664\u5728\u60a8\u7684\u4e1a\u52a1\u903b\u8f91\u4e4b\u5916\u3002\u6b64\u6a21\u5f0f\u901a\u5e38\u79f0\u4e3a Dependency Inversion \u539f\u5219\u3002<\/p>\n<p>Unfortunately, sometimes that separation is not possible, or it makes things significantly more complicated. One example might be when you\u2019re creating emails in a background service; it\u2019s likely you\u2019ll need to include a link to your application in the email. The LinkGenerator class lets you generate that URL so that it updates automatically if the routes in your application change.<br \/>\n\u4e0d\u5e78\u7684\u662f\uff0c\u6709\u65f6\u8fd9\u79cd\u5206\u79bb\u662f\u4e0d\u53ef\u80fd\u7684\uff0c\u6216\u8005\u5b83\u4f7f\u4e8b\u60c5\u53d8\u5f97\u66f4\u52a0\u590d\u6742\u3002\u4f8b\u5982\uff0c\u5f53\u60a8\u5728\u540e\u53f0\u670d\u52a1\u4e2d\u521b\u5efa\u7535\u5b50\u90ae\u4ef6\u65f6;\u60a8\u53ef\u80fd\u9700\u8981\u5728\u7535\u5b50\u90ae\u4ef6\u4e2d\u5305\u542b\u6307\u5411\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684\u94fe\u63a5\u3002LinkGenerator \u7c7b\u5141\u8bb8\u60a8\u751f\u6210\u8be5 URL\uff0c\u4ee5\u4fbf\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u8def\u7531\u53d1\u751f\u66f4\u6539\u65f6\u81ea\u52a8\u66f4\u65b0\u3002<\/p>\n<p>As you saw in chapter 6, the LinkGenerator class is available everywhere in your application, so you can use it inside middleware, minimal API endpoints, or any other services. You can use it from Razor Pages and MVC too, if you want, though the IUrlHelper is often more convenient and hides some details of using the LinkGenerator.<br \/>\n\u6b63\u5982\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u6240\u770b\u5230\u7684\uff0cLinkGenerator \u7c7b\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u968f\u5904\u53ef\u89c1\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u5728\u4e2d\u95f4\u4ef6\u3001\u6700\u5c0f API \u7aef\u70b9\u6216\u4efb\u4f55\u5176\u4ed6\u670d\u52a1\u4e2d\u4f7f\u7528\u5b83\u3002\u5982\u679c\u9700\u8981\uff0c\u60a8\u4e5f\u53ef\u4ee5\u4ece Razor Pages \u548c MVC \u4f7f\u7528\u5b83\uff0c\u5c3d\u7ba1 IUrlHelper \u901a\u5e38\u66f4\u65b9\u4fbf\uff0c\u5e76\u4e14\u9690\u85cf\u4e86\u4f7f\u7528 LinkGenerator \u7684\u4e00\u4e9b\u7ec6\u8282\u3002<\/p>\n<p>You\u2019ve already seen how to generate links to minimal API endpoints with LinkGenerator using methods like GetPathByName() and GetUriByName(). LinkGenerator has various analogous methods for generating URLs for Razor Pages and MVC actions, such as GetPathByPage(), GetPathByAction(), and GetUriByPage(), as shown in the following listing.<br \/>\n\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528 GetPathByName\uff08\uff09 \u548c GetUriByName\uff08\uff09 \u7b49\u65b9\u6cd5\u901a\u8fc7 LinkGenerator \u751f\u6210\u6307\u5411\u6700\u5c0f API \u7aef\u70b9\u7684\u94fe\u63a5\u3002LinkGenerator \u5177\u6709\u5404\u79cd\u7c7b\u4f3c\u7684\u65b9\u6cd5\uff0c\u7528\u4e8e\u4e3a Razor Pages \u548c MVC\u4f5c\u751f\u6210 URL\uff0c\u4f8b\u5982 GetPathByPage\uff08\uff09\u3001GetPathByAction\uff08\uff09 \u548c GetUriByPage\uff08\uff09\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 14.4 Generating URLs using the LinkGeneratorClass<br \/>\n\u6e05\u5355 14.4 \u4f7f\u7528 LinkGeneratorClass \u751f\u6210 URL<\/p>\n<pre><code>public class CurrencyModel : PageModel\n{\nprivate readonly LinkGenerator _link; \u2776\npublic CurrencyModel(LinkGenerator linkGenerator) \u2776\n{ \u2776\n_link = linkGenerator; \u2776\n} \u2776\npublic void OnGet ()\n{\nvar url1 = Url.Page(&quot;Currency\/View&quot;, new { id = 5 }); \u2777\nvar url3 = _link.GetPathByPage( \u2778\nHttpContext, \u2778\n&quot;\/Currency\/View&quot;, \u2778\nvalues: new { id = 5 }); \u2778\nvar url2 = _link.GetPathByPage( \u2779\n&quot;\/Currency\/View&quot;, \u2779\nvalues: new { id = 5 }); \u2779\nvar url4 = _link.GetUriByPage( \u277a\npage: &quot;\/Currency\/View&quot;, \u277a\nhandler: null, \u277a\nvalues: new { id = 5 }, \u277a\nscheme: &quot;https&quot;, \u277a\nhost: new HostString(&quot;example.com&quot;)); \u277a\n}\n}<\/code><\/pre>\n<p>\u2776 LinkGenerator can be accessed using dependency injection.<br \/>\nLinkGenerator \u53ef\u4ee5\u4f7f\u7528\u4f9d\u8d56\u9879\u6ce8\u5165\u8fdb\u884c\u8bbf\u95ee\u3002<br \/>\n\u2777 You can generate relative paths using Url.Page. You can use relative or absolute Page paths.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 Url.Page \u751f\u6210\u76f8\u5bf9\u8def\u5f84\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u76f8\u5bf9\u6216\u7edd\u5bf9 Page \u8def\u5f84\u3002<br \/>\n\u2778 GetPathByPage is equivalent to Url.Page and generates a relative URL.<br \/>\nGetPathByPage \u7b49\u6548\u4e8e Url.Page \u5e76\u751f\u6210\u76f8\u5bf9 URL\u3002<br \/>\n\u2779 Other overloads don\u2019t require an HttpContext.<br \/>\n\u5176\u4ed6\u91cd\u8f7d\u4e0d\u9700\u8981 HttpContext\u3002<br \/>\n\u277a GetUriByPage generates an absolute URL instead of a relative URL.<br \/>\nGetUriByPage \u751f\u6210\u7edd\u5bf9 URL \u800c\u4e0d\u662f\u76f8\u5bf9 URL\u3002<\/p>\n<p><b>Warning<\/b> As always, you need to be careful when generating URLs, whether you\u2019re using IUrlHelper or LinkGenerator. If you get anything wrong\u2014use the wrong path or don\u2019t provide a required route parameter\u2014the URL generated will be null.<br \/>\n\u8b66\u544a\uff1a\u4e0e\u5f80\u5e38\u4e00\u6837\uff0c\u5728\u751f\u6210 URL \u65f6\u9700\u8981\u5c0f\u5fc3\uff0c\u65e0\u8bba\u60a8\u4f7f\u7528\u7684\u662f IUrlHelper \u8fd8\u662f LinkGenerator\u3002\u5982\u679c\u51fa\u73b0\u4efb\u4f55\u9519\u8bef\uff08\u4f7f\u7528\u9519\u8bef\u7684\u8def\u5f84\u6216\u672a\u63d0\u4f9b\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570\uff09\uff0c\u5219\u751f\u6210\u7684 URL \u5c06\u4e3a null\u3002<\/p>\n<p>At this point we\u2019ve covered mapping request URLs to Razor Pages and generating URLs, but most of the URLs we\u2019ve used have been kind of ugly. If seeing capital letters in your URLs bothers you, the next section is for you. In section 14.6 we customize the conventions your application uses to calculate route templates.<br \/>\n\u5728\u8fd9\u4e00\u70b9\u4e0a\uff0c\u6211\u4eec\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u5c06\u8bf7\u6c42 URL \u6620\u5c04\u5230 Razor Pages \u548c\u751f\u6210 URL\uff0c\u4f46\u6211\u4eec\u4f7f\u7528\u7684\u5927\u591a\u6570 URL \u90fd\u6709\u4e9b\u96be\u770b\u3002\u5982\u679c\u5728 URL \u4e2d\u770b\u5230\u5927\u5199\u5b57\u6bcd\u8ba9\u60a8\u611f\u5230\u56f0\u6270\uff0c\u90a3\u4e48\u4e0b\u4e00\u90e8\u5206\u9002\u5408\u60a8\u3002\u5728 Section 14.6 \u4e2d\uff0c\u6211\u4eec\u81ea\u5b9a\u4e49\u4e86\u5e94\u7528\u7a0b\u5e8f\u7528\u4e8e\u8ba1\u7b97\u8def\u7531\u6a21\u677f\u7684\u7ea6\u5b9a\u3002<\/p>\n<h2>14.6 Customizing conventions with Razor Pages<\/h2>\n<p>\u4f7f\u7528 Razor Pages \u81ea\u5b9a\u4e49\u7ea6\u5b9a<\/p>\n<p>Razor Pages is built on a series of conventions that are designed to reduce the amount of boilerplate code you need to write. In this section you\u2019ll see some of the ways you can customize those conventions. By customizing the conventions Razor Pages uses in your application, you get full control of your application\u2019s URLs without having to customize every Razor Page\u2019s route template manually.<br \/>\nRazor Pages \u57fa\u4e8e\u4e00\u7cfb\u5217\u7ea6\u5b9a\u6784\u5efa\uff0c\u65e8\u5728\u51cf\u5c11\u9700\u8981\u7f16\u5199\u7684\u6837\u677f\u4ee3\u7801\u91cf\u3002\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u4e00\u4e9b\u53ef\u4ee5\u81ea\u5b9a\u4e49\u8fd9\u4e9b\u7ea6\u5b9a\u7684\u65b9\u6cd5\u3002\u901a\u8fc7\u81ea\u5b9a\u4e49 Razor Pages \u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u7684\u7ea6\u5b9a\uff0c\u53ef\u4ee5\u5b8c\u5168\u63a7\u5236\u5e94\u7528\u7a0b\u5e8f\u7684 URL\uff0c\u800c\u65e0\u9700\u624b\u52a8\u81ea\u5b9a\u4e49\u6bcf\u4e2a Razor Page \u7684\u8def\u7531\u6a21\u677f\u3002<\/p>\n<p>By default, ASP.NET Core generates URLs that match the filenames of your Razor Pages very closely. The Razor Page located at the path Pages\/Products\/ProductDetails.cshtml, for example, corresponds to the route template Products\/ProductDetails.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cASP.NET Core \u751f\u6210\u7684 URL \u4e0e Razor Pages \u7684\u6587\u4ef6\u540d\u975e\u5e38\u5339\u914d\u3002\u4f8b\u5982\uff0c\u4f4d\u4e8e\u8def\u5f84 Pages\/Products\/ProductDetails.cshtml \u7684 Razor \u9875\u9762\u5bf9\u5e94\u4e8e\u8def\u7531\u6a21\u677f Products\/ProductDetails\u3002<\/p>\n<p>These days, it\u2019s not common to see capital letters in URLs. Similarly, words in URLs are usually separated using kebab-case rather than PascalCase\u2014product-details instead of ProductDetails. Finally, it\u2019s also common to ensure that your URLs always end with a slash, for example\u2014\/product-details\/ instead of \/product-details. Razor Pages gives you complete control of the conventions your application uses to generate route templates, but these are some of the common changes I often make.<br \/>\n\u5982\u4eca\uff0c\u5728 URL \u4e2d\u770b\u5230\u5927\u5199\u5b57\u6bcd\u5e76\u4e0d\u5e38\u89c1\u3002\u540c\u6837\uff0cURL \u4e2d\u7684\u5355\u8bcd\u901a\u5e38\u4f7f\u7528 kebab-case \u800c\u4e0d\u662f PascalCase \u5206\u9694\uff0c\u5373 product-details \u800c\u4e0d\u662f ProductDetails\u3002\u6700\u540e\uff0c\u786e\u4fdd\u60a8\u7684 URL \u59cb\u7ec8\u4ee5\u659c\u6760\u7ed3\u5c3e\u4e5f\u662f\u5f88\u5e38\u89c1\u7684\uff0c\u4f8b\u5982 \/product-details\/ \u800c\u4e0d\u662f \/product-details\u3002Razor Pages \u8ba9\u60a8\u53ef\u4ee5\u5b8c\u5168\u63a7\u5236\u5e94\u7528\u7a0b\u5e8f\u7528\u4e8e\u751f\u6210\u8def\u7531\u6a21\u677f\u7684\u7ea6\u5b9a\uff0c\u4f46\u8fd9\u4e9b\u662f\u6211\u7ecf\u5e38\u8fdb\u884c\u7684\u4e00\u4e9b\u5e38\u89c1\u66f4\u6539\u3002<\/p>\n<p>You saw how to make some of these changes in chapter 6, by customizing the RouteOptions for your application. You can make your URLs lowercase and ensure that they already have a trailing slash as shown in the following listing.<br \/>\n\u60a8\u5728\u7b2c 6 \u7ae0\u4e2d\u4e86\u89e3\u4e86\u5982\u4f55\u901a\u8fc7\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u7684 RouteOptions \u6765\u8fdb\u884c\u5176\u4e2d\u4e00\u4e9b\u66f4\u6539\u3002\u60a8\u53ef\u4ee5\u5c06 URL \u8bbe\u7f6e\u4e3a\u5c0f\u5199\uff0c\u5e76\u786e\u4fdd\u5b83\u4eec\u5df2\u7ecf\u6709\u4e00\u4e2a\u5c3e\u90e8\u659c\u6760\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 14.5 Configuring routing conventions using RouteOptions in Program.cs<br \/>\n\u5217\u8868 14.5 \u5728 Program.cs \u4e2d\u4f7f\u7528 RouteOptions \u914d\u7f6e\u8def\u7531\u7ea6\u5b9a<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddRazorPages();\nbuilder.Services.Configure&lt;RouteOptions&gt;(o =&gt; \u2776\n{ \u2776\no.LowercaseUrls = true; \u2776\no.LowercaseQueryStrings = true; \u2776\no.AppendTrailingSlash = true; \u2776\n});\nWebApplication app = builder.Build();\napp.MapRazorPages();\napp.Run();<\/code><\/pre>\n<p>\u2776 Changes the conventions used to generate URLs. By default, these properties are false.<br \/>\n\u66f4\u6539\u7528\u4e8e\u751f\u6210 URL \u7684\u7ea6\u5b9a\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u8fd9\u4e9b\u5c5e\u6027\u4e3a false\u3002<\/p>\n<p>To use kebab-case for your application, annoyingly you must create a custom parameter transformer. This is a somewhat advanced topic, but it\u2019s relatively simple to implement in this case. The following listing shows how you can create a parameter transformer that uses a regular expression to replace PascalCase values in a generated URL with kebab-case.<br \/>\n\u8981\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528 kebab-case\uff0c\u4ee4\u4eba\u8ba8\u538c\u7684\u662f\uff0c\u60a8\u5fc5\u987b\u521b\u5efa\u4e00\u4e2a\u81ea\u5b9a\u4e49\u53c2\u6570 transformer\u3002\u8fd9\u662f\u4e00\u4e2a\u6709\u70b9\u9ad8\u7ea7\u7684\u4e3b\u9898\uff0c\u4f46\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\u5b9e\u73b0\u8d77\u6765\u76f8\u5bf9\u7b80\u5355\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u521b\u5efa\u4e00\u4e2a\u53c2\u6570\u8f6c\u6362\u5668\uff0c\u8be5\u8f6c\u6362\u5668\u4f7f\u7528\u6b63\u5219\u8868\u8fbe\u5f0f\u5c06\u751f\u6210\u7684 URL \u4e2d\u7684 PascalCase \u503c\u66ff\u6362\u4e3a kebab-case\u3002<\/p>\n<p>Listing 14.6 Creating a kebab-case parameter transformer<br \/>\nListing 14.6 \u521b\u5efa\u4e00\u4e2a kebab-case \u53c2\u6570\u8f6c\u6362\u5668<\/p>\n<pre><code>public class KebabCaseParameterTransformer \u2776\n: IOutboundParameterTransformer \u2776\n{\npublic string TransformOutbound(object? value)\n{\nif (value is null) return null; \u2777\nreturn Regex.Replace(value.ToString(), \u2778\n&quot;([a-z])([A-Z])&quot;, &quot;$1-$2&quot;).ToLower(); \u2778\n}\n}<\/code><\/pre>\n<p>\u2776 Creates a class that implements the parameter transformer interface<br \/>\n\u521b\u5efa\u4e00\u4e2a\u5b9e\u73b0\u53c2\u6570 transformer \u63a5\u53e3\u7684\u7c7b<br \/>\n\u2777 Guards against null values to prevent runtime exceptions<br \/>\n\u9632\u6b62\u7a7a\u503c\u4ee5\u9632\u6b62\u8fd0\u884c\u65f6\u5f02\u5e38<br \/>\n\u2778 The regular expression replaces PascalCase patterns with kebab-case.<br \/>\n\u6b63\u5219\u8868\u8fbe\u5f0f\u5c06 PascalCase \u6a21\u5f0f\u66ff\u6362\u4e3a kebab-case\u3002<\/p>\n<blockquote>\n<p>Source generators in .NET 7<br \/>\n.NET 7 \u4e2d\u7684\u6e90\u751f\u6210\u5668<\/p>\n<p>One of the exciting features introduced in C# 9 was source generators. Source generators are a compiler feature that let you inspect code as it\u2019s compiled and generate new C# files on the fly, which are included in the compilation. Source generators have the potential to dramatically reduce the boilerplate required for some features and to improve performance by relying on compile-time analysis instead of runtime reflection.<br \/>\nC# 9 \u4e2d\u5f15\u5165\u7684\u4e00\u4e2a\u4ee4\u4eba\u5174\u594b\u7684\u529f\u80fd\u662f\u6e90\u751f\u6210\u5668\u3002\u6e90\u751f\u6210\u5668\u662f\u4e00\u9879\u7f16\u8bd1\u5668\u529f\u80fd\uff0c\u53ef\u8ba9\u60a8\u5728\u7f16\u8bd1\u4ee3\u7801\u65f6\u68c0\u67e5\u4ee3\u7801\uff0c\u5e76\u52a8\u6001\u751f\u6210\u65b0\u7684 C# \u6587\u4ef6\uff0c\u8fd9\u4e9b\u6587\u4ef6\u5305\u542b\u5728\u7f16\u8bd1\u4e2d\u3002\u6e90\u751f\u6210\u5668\u6709\u53ef\u80fd\u663e\u8457\u51cf\u5c11\u67d0\u4e9b\u529f\u80fd\u6240\u9700\u7684\u6837\u677f\uff0c\u5e76\u901a\u8fc7\u4f9d\u8d56\u7f16\u8bd1\u65f6\u5206\u6790\u800c\u4e0d\u662f\u8fd0\u884c\u65f6\u53cd\u5c04\u6765\u63d0\u9ad8\u6027\u80fd\u3002<\/p>\n<p>.NET 6 introduced several source generator implementations, such as a high-performance logging API, which I discuss in this post: <a href=\"http:\/\/mng.bz\/Y1GA\">http:\/\/mng.bz\/Y1GA<\/a>. Even the Razor compiler used to compile .cshtml files was rewritten to use source generators!<br \/>\n.NET 6 \u5f15\u5165\u4e86\u51e0\u4e2a\u6e90\u751f\u6210\u5668\u5b9e\u73b0\uff0c\u4f8b\u5982\u9ad8\u6027\u80fd\u65e5\u5fd7\u8bb0\u5f55 API\uff0c\u6211\u5c06\u5728\u672c\u6587\u4e2d\u8ba8\u8bba\uff1a<a href=\"http:\/\/mng.bz\/Y1GA\u3002\u751a\u81f3\u7528\u4e8e\u7f16\u8bd1\">http:\/\/mng.bz\/Y1GA\u3002\u751a\u81f3\u7528\u4e8e\u7f16\u8bd1<\/a> .cshtml \u6587\u4ef6\u7684 Razor \u7f16\u8bd1\u5668\u4e5f\u88ab\u91cd\u5199\u4e3a\u4f7f\u7528\u6e90\u751f\u6210\u5668\uff01<\/p>\n<p>In .NET 7, many new source generators were added. One such generator is the regular-expression generator, which can improve performance of your Regex instances, such as the one in listing 14.6. In fact, if you\u2019re using an IDE like Visual Studio, you should see a code fix suggesting that you use the new pattern. After you apply the code fix, listing 14.6 should look like the following instead, which is functionally identical but will likely be faster:<br \/>\n\u5728 .NET 7 \u4e2d\uff0c\u6dfb\u52a0\u4e86\u8bb8\u591a\u65b0\u7684\u6e90\u751f\u6210\u5668\u3002\u4e00\u4e2a\u8fd9\u6837\u7684\u751f\u6210\u5668\u662f\u6b63\u5219\u8868\u8fbe\u5f0f\u751f\u6210\u5668\uff0c\u5b83\u53ef\u4ee5\u63d0\u9ad8 Regex \u5b9e\u4f8b\u7684\u6027\u80fd\uff0c\u4f8b\u5982\u6e05\u5355 14.6 \u4e2d\u7684\u90a3\u4e2a\u3002\u4e8b\u5b9e\u4e0a\uff0c\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f Visual Studio \u4e4b\u7c7b\u7684 IDE\uff0c\u60a8\u5e94\u8be5\u4f1a\u770b\u5230\u4e00\u4e2a\u4ee3\u7801\u4fee\u590d\uff0c\u5efa\u8bae\u60a8\u4f7f\u7528\u65b0\u6a21\u5f0f\u3002\u5e94\u7528\u4ee3\u7801\u4fee\u590d\u540e\uff0c\u6e05\u5355 14.6 \u5e94\u5982\u4e0b\u6240\u793a\uff0c\u5b83\u5728\u529f\u80fd\u4e0a\u76f8\u540c\uff0c\u4f46\u53ef\u80fd\u4f1a\u66f4\u5feb\uff1a<\/p>\n<\/blockquote>\n<pre><code>partial class KebabCaseParameterTransformer : IOutboundParameterTransformer\n{\n    public string? TransformOutbound(object? value)\n    {\n        if (value is null) return null;\n        return MyRegex().Replace(value.ToString(), &quot;$1-$2&quot;).ToLower();\n    }\n    [GeneratedRegex(&quot;([a-z])([A-Z])&quot;)]\n    private static partial Regex MyRegex();\n}<\/code><\/pre>\n<blockquote>\n<p>If you\u2019d like to know more about how this source generator works and how it can improve performance, see this post at <a href=\"http:\/\/mng.bz\/GyEO\">http:\/\/mng.bz\/GyEO<\/a>. If you\u2019d like to learn more about source generators or even write your own, see my series on the process at <a href=\"http:\/\/mng.bz\/zX4Q\">http:\/\/mng.bz\/zX4Q<\/a>.<br \/>\n\u5982\u679c\u60a8\u60f3\u8fdb\u4e00\u6b65\u4e86\u89e3\u6b64\u6e90\u4ee3\u7801\u751f\u6210\u5668\u7684\u5de5\u4f5c\u539f\u7406\u4ee5\u53ca\u5b83\u5982\u4f55\u63d0\u9ad8\u6027\u80fd\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/GyEO\">http:\/\/mng.bz\/GyEO<\/a> \u4e0a\u7684\u8fd9\u7bc7\u6587\u7ae0\u3002\u5982\u679c\u60a8\u60f3\u4e86\u89e3\u6709\u5173\u6e90\u751f\u6210\u5668\u7684\u66f4\u591a\u4fe1\u606f\uff0c\u751a\u81f3\u7f16\u5199\u81ea\u5df1\u7684\u6e90\u751f\u6210\u5668\uff0c\u8bf7\u53c2\u9605\u6211\u5728 <a href=\"http:\/\/mng.bz\/zX4Q\">http:\/\/mng.bz\/zX4Q<\/a> \u4e0a\u5173\u4e8e\u8be5\u8fc7\u7a0b\u7684\u7cfb\u5217\u6587\u7ae0\u3002<\/p>\n<\/blockquote>\n<p>You can register the parameter transformer in your application with the AddRazorPagesOptions() extension method in Program.cs. This method is chained after the AddRazorPages() method and can be used to customize the conventions used by Razor Pages. The following listing shows how to register the kebab-case transformer. It also shows how to add an extra page route convention for a given Razor Page.<br \/>\n\u60a8\u53ef\u4ee5\u5728 Program.cs \u4e2d\u4f7f\u7528 AddRazorPagesOptions\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u53c2\u6570\u8f6c\u6362\u5668\u3002\u6b64\u65b9\u6cd5\u5728 AddRazorPages\uff08\uff09 \u65b9\u6cd5\u4e4b\u540e\u94fe\u63a5\uff0c\u53ef\u7528\u4e8e\u81ea\u5b9a\u4e49 Razor Pages \u4f7f\u7528\u7684\u7ea6\u5b9a\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u6ce8\u518c kebab-case \u8f6c\u6362\u5668\u3002\u5b83\u8fd8\u6f14\u793a\u5982\u4f55\u4e3a\u7ed9\u5b9a\u7684 Razor \u9875\u9762\u6dfb\u52a0\u989d\u5916\u7684\u9875\u9762\u8def\u7531\u7ea6\u5b9a\u3002<\/p>\n<p>Listing 14.7 Registering a parameter transformer using RazorPagesOptions<br \/>\n\u6e05\u5355 14.7 \u4f7f\u7528 RazorPagesOptions \u6ce8\u518c\u53c2\u6570\u8f6c\u6362\u5668<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddRazorPages()\n.AddRazorPagesOptions(opts =&gt; \u2776\n{\nopts.Conventions.Add( \u2777\nnew PageRouteTransformerConvention( \u2777\nnew KebabCaseParameterTransformer())); \u2777\nopts.Conventions.AddPageRoute( \u2778\n&quot;\/Search\/Products\/StartSearch&quot;, &quot;\/search-products&quot;); \u2778\n});\nWebApplication app = builder.Build();\napp.MapRazorPages();\napp.Run();<\/code><\/pre>\n<p>\u2776 AddRazorPagesOptions can be used to customize the conventions used by Razor Pages<br \/>\nAddRazorPagesOptions \u53ef\u7528\u4e8e\u81ea\u5b9a\u4e49 Razor Pages\u4f7f\u7528\u7684\u7ea6\u5b9a<br \/>\n\u2777 Registers the parameter transformer as a convention used by all Razor Pages<br \/>\n\u5c06\u53c2\u6570\u8f6c\u6362\u5668\u6ce8\u518c\u4e3a\u6240\u6709 Razor Pages\u4f7f\u7528\u7684\u7ea6\u5b9a<br \/>\n\u2778 AddPageRoute adds a route template to Pages\/Search\/Products\/StartSearch.cshtml.<br \/>\nAddPageRoute \u5c06\u8def\u7531\u6a21\u677f\u6dfb\u52a0\u5230 Pages\/Search\/Products\/StartSearch.cshtml\u3002<\/p>\n<p>The AddPageRoute() convention adds an alternative way to execute a single Razor Page. Unlike when you customize the route template for a Razor Page using the @page directive, using AddPageRoute() adds an extra route template to the page instead of replacing the default. That means there are two route templates that can access the page.<br \/>\nAddPageRoute\uff08\uff09 \u7ea6\u5b9a\u6dfb\u52a0\u4e86\u4e00\u79cd\u6267\u884c\u5355\u4e2a Razor Page \u7684\u66ff\u4ee3\u65b9\u6cd5\u3002\u4e0e\u4f7f\u7528 @page \u6307\u4ee4\u81ea\u5b9a\u4e49 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u4e0d\u540c\uff0c\u4f7f\u7528 AddPageRoute\uff08\uff09 \u4f1a\u5411\u9875\u9762\u6dfb\u52a0\u989d\u5916\u7684\u8def\u7531\u6a21\u677f\uff0c\u800c\u4e0d\u662f\u66ff\u6362\u9ed8\u8ba4\u503c\u3002\u8fd9\u610f\u5473\u7740\u6709\u4e24\u4e2a\u8def\u7531\u6a21\u677f\u53ef\u4ee5\u8bbf\u95ee\u8be5\u9875\u9762\u3002<\/p>\n<p><b>Tip<\/b> Even the name of the Pages root folder is a convention that you can customize! You can customize it by setting the RootDirectory property inside the AddRazorPageOptions() configuration lambda.<br \/>\n\u63d0\u793a\uff1a\u751a\u81f3 Pages \u6839\u6587\u4ef6\u5939\u7684\u540d\u79f0\u4e5f\u662f\u60a8\u53ef\u4ee5\u81ea\u5b9a\u4e49\u7684\u7ea6\u5b9a\uff01\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728 AddRazorPageOptions\uff08\uff09 \u914d\u7f6e lambda \u4e2d\u8bbe\u7f6e RootDirectory \u5c5e\u6027\u6765\u81ea\u5b9a\u4e49\u5b83\u3002<\/p>\n<p>If you want even more control of your Razor Pages route templates, you can implement a custom convention by implementing the IPageRouteModelConvention interface and registering it as a custom convention. IPageRouteModelConvention is one of three powerful Razor Pages interfaces which let you customize how your Razor Pages app works:<br \/>\n\u5982\u679c\u8981\u5bf9 Razor Pages \u8def\u7531\u6a21\u677f\u8fdb\u884c\u66f4\u591a\u63a7\u5236\uff0c\u53ef\u4ee5\u901a\u8fc7\u5b9e\u73b0 IPageRouteModelConvention \u63a5\u53e3\u5e76\u5c06\u5176\u6ce8\u518c\u4e3a\u81ea\u5b9a\u4e49\u7ea6\u5b9a\u6765\u5b9e\u73b0\u81ea\u5b9a\u4e49\u7ea6\u5b9a\u3002IPageRouteModelConvention \u662f\u4e09\u4e2a\u529f\u80fd\u5f3a\u5927\u7684 Razor Pages \u63a5\u53e3\u4e4b\u4e00\uff0c\u53ef\u7528\u4e8e\u81ea\u5b9a\u4e49 Razor Pages \u5e94\u7528\u7684\u5de5\u4f5c\u65b9\u5f0f\uff1a<\/p>\n<p>\u2022  IPageRouteModelConvention\u2014Used to customize the route templates for all the Razor Pages in your app.<br \/>\nIPageRouteModelConvention - \u7528\u4e8e\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6240\u6709 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u3002<br \/>\n\u2022  IPageApplicationModelConvention\u2014Used to customize how the Razor Page is processed, such as to add filters to your Razor Page automatically. You\u2019ll learn about filters in Razor Pages in chapters 21 and 22.<br \/>\nIPageApplicationModelConvention - \u7528\u4e8e\u81ea\u5b9a\u4e49 Razor \u9875\u9762\u7684\u5904\u7406\u65b9\u5f0f\uff0c\u4f8b\u5982\u81ea\u52a8\u5411 Razor \u9875\u9762\u6dfb\u52a0\u7b5b\u9009\u5668\u3002\u60a8\u5c06\u5728\u7b2c 21 \u7ae0\u548c\u7b2c 22 \u7ae0\u4e2d\u4e86\u89e3 Razor Pages \u4e2d\u7684\u8fc7\u6ee4\u5668\u3002<br \/>\n\u2022  IPageHandlerModelConvention\u2014Used to customize how page handlers are discovered and selected.<br \/>\nIPageHandlerModelConvention - \u7528\u4e8e\u81ea\u5b9a\u4e49\u53d1\u73b0\u548c\u9009\u62e9\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u7684\u65b9\u5f0f\u3002<\/p>\n<p>These interfaces are powerful, as they give you access to all the internals of your Razor Page conventions and configuration. You can use the IPageRouteModelConvention, for example, to rewrite all the route templates for your Razor Pages or to add routes automatically. This is particularly useful if you need to localize an application so that you can use URLs in multiple languages, all of which map to the same Razor Page.<br \/>\n\u8fd9\u4e9b\u63a5\u53e3\u529f\u80fd\u5f3a\u5927\uff0c\u56e0\u4e3a\u5b83\u4eec\u4f7f\u4f60\u80fd\u591f\u8bbf\u95ee Razor Page \u7ea6\u5b9a\u548c\u914d\u7f6e\u7684\u6240\u6709\u5185\u90e8\u7ed3\u6784\u3002\u4f8b\u5982\uff0c\u53ef\u4ee5\u4f7f\u7528 IPageRouteModelConvention \u91cd\u5199 Razor Pages \u7684\u6240\u6709\u8def\u7531\u6a21\u677f\u6216\u81ea\u52a8\u6dfb\u52a0\u8def\u7531\u3002\u5982\u679c\u60a8\u9700\u8981\u672c\u5730\u5316\u5e94\u7528\u7a0b\u5e8f\uff0c\u4ee5\u4fbf\u53ef\u4ee5\u4f7f\u7528\u591a\u79cd\u8bed\u8a00\u7684 URL\uff0c\u6240\u6709\u8fd9\u4e9b URL \u90fd\u6620\u5c04\u5230\u540c\u4e00\u4e2a Razor \u9875\u9762\uff0c\u8fd9\u5c06\u7279\u522b\u6709\u7528\u3002<\/p>\n<p>Listing 14.8 shows a simple example of an IPageRouteModelConvention that adds a fixed prefix, &quot;page&quot;, to all the routes in your application. If you have a Razor Page at Pages\/Privacy.cshtml, with a default route template of &quot;Privacy&quot;, after adding the following convention it would also have the route template &quot;page\/Privacy\u201d.<br \/>\n\u6e05\u5355 14.8 \u663e\u793a\u4e86\u4e00\u4e2a IPageRouteModelConvention \u7684\u7b80\u5355\u793a\u4f8b\uff0c\u8be5\u793a\u4f8b\u4e3a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u8def\u7531\u6dfb\u52a0\u4e86\u56fa\u5b9a\u524d\u7f00 \u201cpage\u201d\u3002\u5982\u679c\u4f60\u5728 Pages\/Privacy.cshtml \u4e0a\u6709\u4e00\u4e2a Razor \u9875\u9762\uff0c\u5e76\u4e14\u9ed8\u8ba4\u8def\u7531\u6a21\u677f\u4e3a\u201cPrivacy\u201d\uff0c\u5219\u5728\u6dfb\u52a0\u4ee5\u4e0b\u7ea6\u5b9a\u540e\uff0c\u5b83\u8fd8\u5c06\u5177\u6709\u8def\u7531\u6a21\u677f\u201cpage\/Privacy\u201d\u3002<\/p>\n<p>Listing 14.8 Creating a custom IPageRouteModelConvention<br \/>\n\u6e05\u5355 14.8 \u521b\u5efa\u81ea\u5b9a\u4e49 IPageRouteModelConvention<\/p>\n<pre><code>public class PrefixingPageRouteModelConvention\n: IpageRouteModelConvention \u2776\n{\npublic void Apply(PageRouteModel model) \u2777\n{\nvar selectors = model.Selectors\n.Select(selector =&gt; new SelectorModel \u2778\n{ \u2778\nAttributeRouteModel = new AttributeRouteModel \u2778\n{ \u2778\nTemplate = AttributeRouteModel.CombineTemplates( \u2778\n&quot;page&quot;, \u2778\nselector.AttributeRouteModel!.Template), \u2778\n} \u2778\n}) \u2778\n.ToList();\nforeach(var newSelector in selectors) \u2779\n{\nmodel.Selectors.Add(newSelector);\n}\n}\n}<\/code><\/pre>\n<p>\u2776 The convention implements IPageRouteModelConvention.<br \/>\n\u8be5\u7ea6\u5b9a\u5b9e\u73b0 IPageRouteModelConvention\u3002<br \/>\n\u2777 ASP.NET Core calls Apply on app startup.<br \/>\nASP.NET Core \u5728\u5e94\u7528\u7a0b\u5e8f\u542f\u52a8\u65f6\u8c03\u7528 Apply\u3002<br \/>\n\u2778 Creates a new SelectorModel, defining a new route template for the page<br \/>\n\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 SelectorModel\uff0c\u4e3a\u9875\u9762\u5b9a\u4e49\u4e00\u4e2a\u65b0\u7684\u8def\u7531\u6a21\u677f<br \/>\n\u2779 Adds the new selector to the page\u2019s route template collection<br \/>\n\u5c06\u65b0\u7684\u9009\u62e9\u5668\u6dfb\u52a0\u5230\u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u96c6\u5408\u4e2d<\/p>\n<p>You can add the convention to your application inside the call to AddRazorPagesOptions(). The following applies the contention to all pages:<br \/>\n\u60a8\u53ef\u4ee5\u5728\u5bf9 AddRazorPagesOptions\uff08\uff09 \u7684\u8c03\u7528\u4e2d\u5c06\u7ea6\u5b9a\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u4ee5\u4e0b\u5c06\u4e89\u7528\u5e94\u7528\u4e8e\u6240\u6709\u9875\u9762\uff1a<\/p>\n<pre><code>builder.Services.AddRazorPages().AddRazorPagesOptions(opts =&gt;\n{\n    opts.Conventions.Add(new PrefixingPageRouteModelConvention());\n});<\/code><\/pre>\n<p>There are many ways you can customize the conventions in your Razor Page applications, but a lot of the time that\u2019s not necessary. If you do find you need to customize all the pages in your application in some way, Microsoft\u2019s \u201cRazor Pages route and app conventions in ASP.NET Core\u201d documentation contains further details on everything that\u2019s available: <a href=\"http:\/\/mng.bz\/A0BK\">http:\/\/mng.bz\/A0BK<\/a>.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u81ea\u5b9a\u4e49 Razor Page \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u7ea6\u5b9a\uff0c\u4f46\u5f88\u591a\u65f6\u5019\u8fd9\u4e0d\u662f\u5fc5\u9700\u7684\u3002\u5982\u679c\u4f60\u786e\u5b9e\u53d1\u73b0\u9700\u8981\u4ee5\u67d0\u79cd\u65b9\u5f0f\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u9875\u9762\uff0cMicrosoft \u7684\u201cASP.NET Core \u4e2d\u7684 Razor Pages \u8def\u7531\u548c\u5e94\u7528\u7ea6\u5b9a\u201d\u6587\u6863\u5305\u542b\u6709\u5173\u6240\u6709\u53ef\u7528\u5185\u5bb9\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff1a<a href=\"http:\/\/mng.bz\/A0BK\">http:\/\/mng.bz\/A0BK<\/a>\u3002<\/p>\n<p>Conventions are a key feature of Razor Pages, and you should lean on them whenever you can. Although you can override the route templates for individual Razor Pages manually, as you\u2019ve seen in previous sections, I advise against it where possible. In particular,<br \/>\n\u7ea6\u5b9a\u662f Razor Pages \u7684\u4e00\u9879\u5173\u952e\u529f\u80fd\uff0c\u60a8\u5e94\u8be5\u5c3d\u53ef\u80fd\u4f9d\u8d56\u5b83\u4eec\u3002\u5c3d\u7ba1\u60a8\u53ef\u4ee5\u624b\u52a8\u8986\u76d6\u5355\u4e2a Razor Pages \u7684\u8def\u7531\u6a21\u677f\uff0c\u4f46\u6b63\u5982\u60a8\u5728\u524d\u9762\u7684\u90e8\u5206\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0c\u6211\u5efa\u8bae\u5c3d\u53ef\u80fd\u4e0d\u8981\u8fd9\u6837\u505a\u3002\u7279\u522b<\/p>\n<p>\u2022  Avoid replacing the route template with an absolute path in a page\u2019s @page directive.<br \/>\n\u907f\u514d\u5728\u9875\u9762\u7684 @page \u6307\u4ee4\u4e2d\u5c06\u8def\u7531\u6a21\u677f\u66ff\u6362\u4e3a\u7edd\u5bf9\u8def\u5f84\u3002<\/p>\n<p>\u2022  Avoid adding literal segments to the @page directive. Rely on the file hierarchy instead.<br \/>\n\u907f\u514d\u5411 @page \u6307\u4ee4\u6dfb\u52a0\u6587\u5b57\u6bb5\u3002\u8bf7\u6539\u7528\u6587\u4ef6\u5c42\u6b21\u7ed3\u6784\u3002<\/p>\n<p>\u2022  Avoid adding additional route templates to a Razor Page with the AddPageRoute() convention. Having multiple URLs to access a page can often be confusing.<br \/>\n\u907f\u514d\u4f7f\u7528 AddPageRoute\uff08\uff09 \u7ea6\u5b9a\u5411 Razor \u9875\u9762\u6dfb\u52a0\u5176\u4ed6\u8def\u7531\u6a21\u677f\u3002\u4f7f\u7528\u591a\u4e2a URL \u6765\u8bbf\u95ee\u9875\u9762\u901a\u5e38\u4f1a\u4ee4\u4eba\u56f0\u60d1\u3002<\/p>\n<p>\u2022  Do add route parameters to the @page directive to make your routes dynamic, as in @page \u201c{name}&quot;.<br \/>\n\u52a1\u5fc5\u5c06\u8def\u7531\u53c2\u6570\u6dfb\u52a0\u5230 @page \u6307\u4ee4\u4e2d\uff0c\u4ee5\u4f7f\u60a8\u7684\u8def\u7531\u52a8\u6001\u5316\uff0c\u5982 @page \u201c{name}\u201d\u3002<\/p>\n<p>\u2022  Do consider using global conventions when you want to change the route templates for all your Razor Pages, such as using kebab-case, as you saw earlier.<br \/>\n\u5f53\u60a8\u60f3\u8981\u66f4\u6539\u6240\u6709 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\u65f6\uff0c\u8bf7\u8003\u8651\u4f7f\u7528\u5168\u5c40\u7ea6\u5b9a\uff0c\u4f8b\u5982\u4f7f\u7528 kebab-case\uff0c\u5982\u524d\u6240\u8ff0\u3002<\/p>\n<p>In a nutshell, these rules say \u201cStick to the conventions.\u201d The danger, if you don\u2019t, is that you may accidentally create two Razor Pages that have overlapping route templates. Unfortunately, if you end up in that situation, you won\u2019t get an error at compile time. Instead, you\u2019ll get an exception at runtime when your application receives a request that matches multiple route templates, as shown in figure 14.4.<br \/>\n\u7b80\u800c\u8a00\u4e4b\uff0c\u8fd9\u4e9b\u89c4\u5219\u8bf4 \u201cAdhere the conventions\u201d\u3002\u5982\u679c\u4e0d\u8fd9\u6837\u505a\uff0c\u5371\u9669\u5728\u4e8e\u53ef\u80fd\u4f1a\u610f\u5916\u521b\u5efa\u4e24\u4e2a\u5177\u6709\u91cd\u53e0\u8def\u7531\u6a21\u677f\u7684 Razor \u9875\u9762\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u5982\u679c\u4f60\u6700\u7ec8\u9047\u5230\u8fd9\u79cd\u60c5\u51b5\uff0c\u4f60\u4e0d\u4f1a\u5728\u7f16\u8bd1\u65f6\u6536\u5230\u9519\u8bef\u3002\u76f8\u53cd\uff0c\u5f53\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6536\u5230\u4e0e\u591a\u4e2a\u8def\u7531\u6a21\u677f\u5339\u914d\u7684\u8bf7\u6c42\u65f6\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u5f02\u5e38\uff0c\u5982\u56fe 14.4 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1404.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 14.4 If multiple Razor Pages are registered with overlapping route templates, you\u2019ll get an exception at runtime when the router can\u2019t work out which one to select.<br \/>\n\u56fe 14.4 \u5982\u679c\u591a\u4e2a Razor \u9875\u9762\u6ce8\u518c\u4e86\u91cd\u53e0\u7684\u8def\u7531\u6a21\u677f\uff0c\u5219\u5f53\u8def\u7531\u5668\u65e0\u6cd5\u786e\u5b9a\u8981\u9009\u62e9\u54ea\u4e2a\u9875\u9762\u65f6\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u5f02\u5e38\u3002<\/p>\n<p>We\u2019ve covered pretty much everything about routing to Razor Pages now. For the most part, routing to Razor Pages works like minimal APIs, the main difference being that the route templates are created using conventions. When it comes to the other half of routing\u2014generating URLs\u2014Razor Pages and minimal APIs are also similar, but Razor Pages gives you some nice helpers.<br \/>\n\u6211\u4eec\u73b0\u5728\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u51e0\u4e4e\u6240\u6709\u5173\u4e8e\u8def\u7531\u5230 Razor Pages \u7684\u5185\u5bb9\u3002\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u8def\u7531\u5230 Razor Pages \u7684\u5de5\u4f5c\u65b9\u5f0f\u7c7b\u4f3c\u4e8e\u6700\u5c0f\u7684 API\uff0c\u4e3b\u8981\u533a\u522b\u5728\u4e8e\u8def\u7531\u6a21\u677f\u662f\u4f7f\u7528\u7ea6\u5b9a\u521b\u5efa\u7684\u3002\u5f53\u6d89\u53ca\u5230\u8def\u7531\u7684\u53e6\u4e00\u534a\uff08\u751f\u6210 URL\uff09\u65f6\uff0cRazor Pages \u548c\u6700\u5c0f API \u4e5f\u7c7b\u4f3c\uff0c\u4f46 Razor Pages \u63d0\u4f9b\u4e86\u4e00\u4e9b\u4e0d\u9519\u7684\u5e2e\u624b\u3002<\/p>\n<p>Congratulations\u2014you\u2019ve made it all the way through this detailed discussion on Razor Page routing! I hope you weren\u2019t too fazed by the differences from minimal API routing. We\u2019ll revisit routing again when I describe how to create Web APIs in chapter 20, but rest assured that we\u2019ve already covered all the tricky details in this chapter!<br \/>\n\u606d\u559c \u2014 \u60a8\u5df2\u7ecf\u5b8c\u6210\u4e86\u6709\u5173 Razor Page \u8def\u7531\u7684\u8be6\u7ec6\u8ba8\u8bba\uff01\u6211\u5e0c\u671b\u60a8\u4e0d\u4f1a\u5bf9\u6700\u5c0f API \u8def\u7531\u7684\u5dee\u5f02\u611f\u5230\u592a\u56f0\u6270\u3002\u5f53\u6211\u5728\u7b2c 20 \u7ae0\u4e2d\u63cf\u8ff0\u5982\u4f55\u521b\u5efa Web API \u65f6\uff0c\u6211\u4eec\u5c06\u518d\u6b21\u56de\u987e\u8def\u7531\uff0c\u4f46\u8bf7\u653e\u5fc3\uff0c\u6211\u4eec\u5df2\u7ecf\u5728\u672c\u7ae0\u4e2d\u4ecb\u7ecd\u4e86\u6240\u6709\u68d8\u624b\u7684\u7ec6\u8282\uff01<\/p>\n<p>Routing controls how incoming requests are bound to your Razor Page, but we haven\u2019t seen where page handlers come into it. In chapter 15 you\u2019ll learn all about page handlers\u2014how they\u2019re selected, how they generate responses, and how to handle error responses gracefully.<br \/>\n\u8def\u7531\u63a7\u5236\u4f20\u5165\u8bf7\u6c42\u5982\u4f55\u7ed1\u5b9a\u5230 Razor \u9875\u9762\uff0c\u4f46\u6211\u4eec\u5c1a\u672a\u770b\u5230\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u7684\u6765\u6e90\u3002\u5728\u7b2c 15 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u6709\u5173\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u7684\u6240\u6709\u4fe1\u606f \u2014 \u5982\u4f55\u9009\u62e9\u5b83\u4eec\uff0c\u5982\u4f55\u751f\u6210\u54cd\u5e94\uff0c\u4ee5\u53ca\u5982\u4f55\u4f18\u96c5\u5730\u5904\u7406\u9519\u8bef\u54cd\u5e94\u3002<\/p>\n<h2>14.7 Summary<\/h2>\n<p>14.7 \u603b\u7ed3<\/p>\n<p>Routing is the process of mapping an incoming request URL to an endpoint that will execute to generate a response. Each Razor Page is an endpoint, and a single page handler executes for each request.<br \/>\n\u8def\u7531\u662f\u5c06\u4f20\u5165\u8bf7\u6c42 URL \u6620\u5c04\u5230\u5c06\u6267\u884c\u4ee5\u751f\u6210\u54cd\u5e94\u7684\u7ec8\u7aef\u8282\u70b9\u7684\u8fc7\u7a0b\u3002\u6bcf\u4e2a Razor \u9875\u9762\u90fd\u662f\u4e00\u4e2a\u7aef\u70b9\uff0c\u6bcf\u4e2a\u8bf7\u6c42\u90fd\u4f1a\u6267\u884c\u4e00\u4e2a\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>You can define the mapping between URLs and endpoint in your application using either convention-based routing or explicit routing. Minimal APIs use explicit routing, where each endpoint has a corresponding route template. MVC controllers often use conventional routing in which a single pattern matches multiple controllers but may also use explicit\/attribute routing. Razor Pages lies in between; it uses conventions to generate explicit route templates for each page.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\u6216\u663e\u5f0f\u8def\u7531\u6765\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u4e2d URL \u548c endpoint \u4e4b\u95f4\u7684\u6620\u5c04\u3002\u6700\u5c0f API \u4f7f\u7528\u663e\u5f0f\u8def\u7531\uff0c\u5176\u4e2d\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u90fd\u6709\u76f8\u5e94\u7684\u8def\u7531\u6a21\u677f\u3002MVC \u63a7\u5236\u5668\u901a\u5e38\u4f7f\u7528\u4f20\u7edf\u8def\u7531\uff0c\u5176\u4e2d\u5355\u4e2a\u6a21\u5f0f\u5339\u914d\u591a\u4e2a\u63a7\u5236\u5668\uff0c\u4f46\u4e5f\u53ef\u80fd\u4f7f\u7528\u663e\u5f0f\/\u5c5e\u6027\u8def\u7531\u3002Razor Pages \u4ecb\u4e8e\u4e24\u8005\u4e4b\u95f4;\u5b83\u4f7f\u7528\u7ea6\u5b9a\u4e3a\u6bcf\u4e2a\u9875\u9762\u751f\u6210\u663e\u5f0f\u8def\u7531\u6a21\u677f\u3002<\/p>\n<p>By default, each Razor Page has a single route template that matches its path inside the Pages folder, so the Razor Page Pages\/Products\/View.cshtml has route template Products\/View. These file-based defaults make it easy to visualize the URLs your application exposes.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6bcf\u4e2a Razor \u9875\u9762\u90fd\u6709\u4e00\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u8be5\u6a21\u677f\u4e0e\u5176\u5728 Pages \u6587\u4ef6\u5939\u4e2d\u7684\u8def\u5f84\u5339\u914d\uff0c\u56e0\u6b64 Razor Page Pages\/Products\/View.cshtml \u5177\u6709\u8def\u7531\u6a21\u677f Products\/View\u3002\u8fd9\u4e9b\u57fa\u4e8e\u6587\u4ef6\u7684\u9ed8\u8ba4\u503c\u4f7f\u53ef\u89c6\u5316\u5e94\u7528\u7a0b\u5e8f\u516c\u5f00\u7684 URL \u53d8\u5f97\u5bb9\u6613\u3002<\/p>\n<p>Index.cshtml Razor Pages have two route templates, one with an \/Index suffix and one without. Pages\/Products\/Index.cshtml, for example, has two route templates: Products\/Index and Products. This is in keeping with the common behavior of index.html files in traditional HTML applications.<br \/>\nIndex.cshtml Razor Pages \u6709\u4e24\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u4e00\u4e2a\u5e26\u6709 \/Index \u540e\u7f00\uff0c\u53e6\u4e00\u4e2a\u6ca1\u6709\u3002\u4f8b\u5982\uff0cPages\/Products\/Index.cshtml \u6709\u4e24\u4e2a\u8def\u7531\u6a21\u677f\uff1aProducts\/Index \u548c Products\u3002\u8fd9\u4e0e\u4f20\u7edf HTML \u5e94\u7528\u7a0b\u5e8f\u4e2dindex.html\u6587\u4ef6\u7684\u5e38\u89c1\u884c\u4e3a\u4e00\u81f4\u3002<\/p>\n<p>You can add segments to a Razor Page\u2019s template by appending it to the @page directive, as in @page &quot;{id}&quot;. Any extra segments are appended to the Razor Page\u2019s default route template. You can include both literal and route template segments, which can be used to make your Razor Pages dynamic. You can replace the route template for a Razor Page by starting the template with a &quot;\/&quot;, as in @page &quot;\/contact&quot;.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06 Razor \u9875\u9762\u7684\u6a21\u677f\u9644\u52a0\u5230 @page \u6307\u4ee4\u6765\u5411 Razor \u9875\u9762\u7684\u6a21\u677f\u6dfb\u52a0\u533a\u6bb5\uff0c\u5c31\u50cf@page \u201c{id}\u201d \u4e00\u6837\u3002\u4efb\u4f55\u989d\u5916\u7684\u6bb5\u90fd\u5c06\u9644\u52a0\u5230 Razor Page \u7684\u9ed8\u8ba4\u8def\u7531\u6a21\u677f\u4e2d\u3002\u60a8\u53ef\u4ee5\u540c\u65f6\u5305\u542b\u6587\u672c\u548c\u8def\u7531\u6a21\u677f\u6bb5\uff0c\u8fd9\u53ef\u7528\u4e8e\u4f7f Razor \u9875\u9762\u52a8\u6001\u5316\u3002\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528\u201c\/\u201d\u5f00\u5934\u6765\u66ff\u6362 Razor \u9875\u9762\u7684\u8def\u7531\u6a21\u677f\uff0c\u5982@page\u201c\/contact\u201d\u3002<\/p>\n<p>You can use IUrlHelper to generate URLs as a string based on an action name or Razor Page. IUrlHelper can be used only in the context of a request and uses ambient routing values from the current request. This makes it easier to generate links for Razor Pages in the same folder as the currently executing request but also adds inconsistency, as the same method call generates different URLs depending on where it\u2019s called.<br \/>\n\u53ef\u4ee5\u4f7f\u7528 IUrlHelper \u6839\u636e\u4f5c\u540d\u79f0\u6216 Razor \u9875\u9762\u5c06 URL \u751f\u6210\u4e3a\u5b57\u7b26\u4e32\u3002IUrlHelper \u53ea\u80fd\u5728\u8bf7\u6c42\u7684\u4e0a\u4e0b\u6587\u4e2d\u4f7f\u7528\uff0c\u5e76\u4f7f\u7528\u5f53\u524d\u8bf7\u6c42\u4e2d\u7684\u73af\u5883\u8def\u7531\u503c\u3002\u8fd9\u6837\u53ef\u4ee5\u66f4\u8f7b\u677e\u5730\u5728\u4e0e\u5f53\u524d\u6267\u884c\u7684\u8bf7\u6c42\u76f8\u540c\u7684\u6587\u4ef6\u5939\u4e2d\u4e3a Razor Pages \u751f\u6210\u94fe\u63a5\uff0c\u4f46\u4e5f\u589e\u52a0\u4e86\u4e0d\u4e00\u81f4\u6027\uff0c\u56e0\u4e3a\u76f8\u540c\u7684\u65b9\u6cd5\u8c03\u7528\u4f1a\u6839\u636e\u8c03\u7528\u4f4d\u7f6e\u751f\u6210\u4e0d\u540c\u7684 URL\u3002<\/p>\n<p>The LinkGenerator can be used to generate URLs from other services in your application, where you don\u2019t have access to an HttpContext object. The LinkGenerator methods are more verbose than the equivalents on IUrlHelper, but they are unambiguous as they don\u2019t use ambient values from the current request.<br \/>\nLinkGenerator \u53ef\u7528\u4e8e\u4ece\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5176\u4ed6\u670d\u52a1\u751f\u6210 URL\uff0c\u5728\u8fd9\u4e9b\u670d\u52a1\u4e2d\uff0c\u60a8\u65e0\u6743\u8bbf\u95ee HttpContext \u5bf9\u8c61\u3002LinkGenerator \u65b9\u6cd5\u6bd4 IUrlHelper \u4e0a\u7684\u7b49\u6548\u65b9\u6cd5\u66f4\u8be6\u7ec6\uff0c\u4f46\u5b83\u4eec\u662f\u660e\u786e\u7684\uff0c\u56e0\u4e3a\u5b83\u4eec\u4e0d\u4f7f\u7528\u5f53\u524d\u8bf7\u6c42\u4e2d\u7684\u73af\u5883\u503c\u3002<\/p>\n<p>You can control the routing conventions used by ASP.NET Core by configuring the RouteOptions object, such as to force all URLs to be lowercase or to always append a trailing slash.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u914d\u7f6e RouteOptions \u5bf9\u8c61\u6765\u63a7\u5236 ASP.NET Core \u4f7f\u7528\u7684\u8def\u7531\u7ea6\u5b9a\uff0c\u4f8b\u5982\u5f3a\u5236\u6240\u6709 URL \u4e3a\u5c0f\u5199\u6216\u59cb\u7ec8\u9644\u52a0\u5c3e\u90e8\u659c\u6760\u3002<\/p>\n<p>You can add extra routing conventions for Razor Pages by calling AddRazorPagesOptions() after AddRazorPages() in Program.cs. These conventions can control how route parameters are displayed and can add extra route templates for specific Razor Pages.<br \/>\n\u53ef\u4ee5\u901a\u8fc7\u5728 Program.cs \u4e2d\u7684 AddRazorPages\uff08\uff09 \u540e\u8c03\u7528 AddRazorPagesOptions\uff08\uff09 \u6765\u4e3a Razor Pages \u6dfb\u52a0\u989d\u5916\u7684\u8def\u7531\u7ea6\u5b9a\u3002\u8fd9\u4e9b\u7ea6\u5b9a\u53ef\u4ee5\u63a7\u5236\u8def\u7531\u53c2\u6570\u7684\u663e\u793a\u65b9\u5f0f\uff0c\u5e76\u4e14\u53ef\u4ee5\u4e3a\u7279\u5b9a Razor \u9875\u9762\u6dfb\u52a0\u989d\u5916\u7684\u8def\u7531\u6a21\u677f\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>14 Mapping URLs to Razor Pages using routing 14 \u4f7f\u7528\u8def\u7531\u5c06 URL \u6620\u5c04\u5230 Razor Pages This chapter covers \u672c\u7ae0\u4ecb\u7ecd\u4ee5\u4e0b\u5185\u5bb9 \u2022 Routing requests to Razor Pages \u5c06\u8bf7\u6c42\u8def\u7531\u5230 Razor Pages \u2022 Customizing Razor Page route templates \u81ea\u5b9a\u4e49 Razor Page \u8def\u7531\u6a21\u677f \u2022 Generating URLs for Razor Pages \u4e3a Razor Pages \u751f\u6210 URL In chapter 13 you learned about the [&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":[19],"class_list":["post-599","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-asp-net-core-in-action"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/599","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=599"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/599\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=599"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=599"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=599"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}