{"id":580,"date":"2025-04-05T03:26:17","date_gmt":"2025-04-04T19:26:17","guid":{"rendered":"https:\/\/www.hyy.net\/?p=580"},"modified":"2025-04-05T03:26:17","modified_gmt":"2025-04-04T19:26:17","slug":"asp-net-core-in-action-6-mapping-urls-to-endpoints-using-routing","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=580","title":{"rendered":"ASP.NET Core in Action 6 Mapping URLs to endpoints using routing"},"content":{"rendered":"<p>6 Mapping URLs to endpoints using routing<br \/>\n6 \u4f7f\u7528\u8def\u7531\u5c06 URL \u6620\u5c04\u5230\u7aef\u70b9<\/p>\n<h2>This chapter covers<\/h2>\n<h2>\u672c\u7ae0\u6db5\u76d6<\/h2>\n<ul>\n<li>Mapping URLs to endpoint handlers<br \/>\n\u5c06 URL \u6620\u5c04\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f<\/li>\n<li>Using constraints and default values to match URLs<br \/>\n\u4f7f\u7528\u7ea6\u675f\u548c\u9ed8\u8ba4\u503c\u6765\u5339\u914d URL<\/li>\n<li>Generating URLs from route parameters<br \/>\n\u4ece\u8def\u7531\u53c2\u6570\u751f\u6210 URL<\/li>\n<\/ul>\n<p>In chapter 5 you learned how to define minimal APIs, how to return responses, and how to work with filters and route groups. One crucial aspect of minimal APIs that we touched on only lightly is how ASP.NET Core selects a specific endpoint from all the handlers defined, based on the incoming request URL. This process, called routing, is the focus of this chapter.<\/p>\n<p>\u5728\u7b2c 5 \u7ae0\u4e2d\uff0c\u60a8\u5b66\u4e60\u4e86\u5982\u4f55\u5b9a\u4e49\u6700\u5c0f API\u3001\u5982\u4f55\u8fd4\u56de\u54cd\u5e94\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u7b5b\u9009\u6761\u4ef6\u548c\u8def\u7531\u7ec4\u3002\u6211\u4eec\u4ec5\u7565\u5fae\u6d89\u53ca\u7684\u6700\u5c0f API \u7684\u4e00\u4e2a\u5173\u952e\u65b9\u9762\u662f ASP.NET Core \u5982\u4f55\u6839\u636e\u4f20\u5165\u8bf7\u6c42 URL \u4ece\u5b9a\u4e49\u7684\u6240\u6709\u5904\u7406\u7a0b\u5e8f\u4e2d\u9009\u62e9\u7279\u5b9a\u7aef\u70b9\u3002\u6b64\u8fc7\u7a0b\u79f0\u4e3a routing\uff0c\u662f\u672c\u7ae0\u7684\u91cd\u70b9\u3002<\/p>\n<p>This chapter begins by identifying the need for routing and why it\u2019s useful. You\u2019ll learn about the endpoint routing system introduced in ASP.NET Core 3.0 and why it was introduced, and explore the flexibility routing can bring to the URLs you expose.<\/p>\n<p>\u672c\u7ae0\u9996\u5148\u786e\u5b9a routing \u7684\u9700\u6c42\u4ee5\u53ca\u5b83\u4e3a\u4ec0\u4e48\u6709\u7528\u3002\u60a8\u5c06\u4e86\u89e3 ASP.NET Core 3.0 \u4e2d\u5f15\u5165\u7684\u7aef\u70b9\u8def\u7531\u7cfb\u7edf\u53ca\u5176\u5f15\u5165\u539f\u56e0\uff0c\u5e76\u63a2\u7d22\u8def\u7531\u53ef\u4ee5\u4e3a\u60a8\u516c\u5f00\u7684 URL \u5e26\u6765\u7684\u7075\u6d3b\u6027\u3002<\/p>\n<p>The bulk of this chapter focuses on the route template syntax and how it can be used with minimal APIs. You\u2019ll learn about features such as optional parameters, default parameters, and constraints, as well as how to extract values from the URL automatically. Although we\u2019re focusing on minimal APIs in this chapter, the same routing system is used with Razor Pages and Model-View-Controller (MVC), as you\u2019ll see in chapter 14.<\/p>\n<p>\u672c\u7ae0\u7684\u5927\u90e8\u5206\u5185\u5bb9\u91cd\u70b9\u4ecb\u7ecd\u8def\u7531\u6a21\u677f\u8bed\u6cd5\u4ee5\u53ca\u5982\u4f55\u5c06\u5176\u4e0e\u6700\u5c11\u7684 API \u4e00\u8d77\u4f7f\u7528\u3002\u60a8\u5c06\u4e86\u89e3\u53ef\u9009\u53c2\u6570\u3001\u9ed8\u8ba4\u53c2\u6570\u548c\u7ea6\u675f\u7b49\u529f\u80fd\uff0c\u4ee5\u53ca\u5982\u4f55\u81ea\u52a8\u4ece URL \u4e2d\u63d0\u53d6\u503c\u3002\u5c3d\u7ba1\u6211\u4eec\u5728\u672c\u7ae0\u4e2d\u91cd\u70b9\u4ecb\u7ecd\u4e86\u6700\u5c11\u7684 API\uff0c\u4f46 Razor Pages \u548c\u6a21\u578b\u89c6\u56fe\u63a7\u5236\u5668 \uff08MVC\uff09 \u4f7f\u7528\u76f8\u540c\u7684\u8def\u7531\u7cfb\u7edf\uff0c\u5982\u7b2c 14 \u7ae0\u6240\u793a\u3002<\/p>\n<p>In section 6.4 I describe how to use the routing system to generate URLs, which you can use to create links and redirect requests for your application. One benefit of using a routing system is that it decouples your handlers from the underlying URLs they\u2019re associated with. You can use URL generation to avoid littering your code with hardcoded URLs like \/product\/view\/3. Instead, you can generate the URLs at runtime, based on the routing system. This approach makes changing the URL for a given endpoint easier: instead of your having to hunt down every place where you used the endpoint\u2019s URL, the URLs are updated for you automatically, with no other changes required.<\/p>\n<p>\u5728 6.4 \u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528\u8def\u7531\u7cfb\u7edf\u751f\u6210 URL\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd9\u4e9b URL \u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u521b\u5efa\u94fe\u63a5\u548c\u91cd\u5b9a\u5411\u8bf7\u6c42\u3002\u4f7f\u7528\u8def\u7531\u7cfb\u7edf\u7684\u4e00\u4e2a\u597d\u5904\u662f\uff0c\u5b83\u5c06\u5904\u7406\u7a0b\u5e8f\u4e0e\u5b83\u4eec\u5173\u8054\u7684\u5e95\u5c42 URL \u5206\u79bb\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 URL \u751f\u6210\u6765\u907f\u514d\u5c06\u4ee3\u7801\u4e0e\u786c\u7f16\u7801\u7684 URL \uff08\u5982 \/product\/view\/3\uff09\u6df7\u5728\u4e00\u8d77\u3002\u76f8\u53cd\uff0c\u60a8\u53ef\u4ee5\u5728\u8fd0\u884c\u65f6\u6839\u636e\u8def\u7531\u7cfb\u7edf\u751f\u6210 URL\u3002\u8fd9\u79cd\u65b9\u6cd5\u4f7f\u66f4\u6539\u7ed9\u5b9a\u7ec8\u7aef\u8282\u70b9\u7684 URL \u53d8\u5f97\u66f4\u52a0\u5bb9\u6613\uff1a\u60a8\u4e0d\u5fc5\u5bfb\u627e\u4f7f\u7528\u7ec8\u7aef\u8282\u70b9 URL \u7684\u6bcf\u4e2a\u4f4d\u7f6e\uff0cURL \u4f1a\u81ea\u52a8\u4e3a\u60a8\u66f4\u65b0\uff0c\u65e0\u9700\u8fdb\u884c\u5176\u4ed6\u66f4\u6539\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 being the glue that ties the middleware pipeline to endpoints. With middleware, endpoints, and routing under your belt, you\u2019ll be writing web apps in no time!<\/p>\n<p>\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\u60a8\u53ef\u4ee5\u5c06\u8def\u7531\u89c6\u4e3a\u5c06\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e0e\u7ec8\u7aef\u8282\u70b9\u8054\u7cfb\u8d77\u6765\u7684\u7c98\u5408\u5242\u3002\u6709\u4e86\u4e2d\u95f4\u4ef6\u3001\u7ec8\u7aef\u8282\u70b9\u548c\u8def\u7531\uff0c\u60a8\u5c06\u7acb\u5373\u7f16\u5199 Web \u5e94\u7528\u7a0b\u5e8f\uff01<\/p>\n<h2>6.1 What is routing?<\/h2>\n<h2>6.1 \u4ec0\u4e48\u662f\u8def\u7531\uff1f<\/h2>\n<p>Routing is the process of mapping an incoming request to a method that will handle it. You can use routing to control the URLs you expose in your application. You can also use routing to enable powerful features such as mapping multiple URLs to the same handler and automatically extracting data from a request\u2019s URL.<\/p>\n<p>\u8def\u7531\u662f\u5c06\u4f20\u5165\u8bf7\u6c42\u6620\u5c04\u5230\u5c06\u5904\u7406\u8be5\u8bf7\u6c42\u7684\u65b9\u6cd5\u7684\u8fc7\u7a0b\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u6765\u63a7\u5236\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u516c\u5f00\u7684 URL\u3002\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u6765\u542f\u7528\u5f3a\u5927\u7684\u529f\u80fd\uff0c\u4f8b\u5982\u6620\u5c04\u591a\u4e2a URL \u6307\u5411\u540c\u4e00\u5904\u7406\u7a0b\u5e8f\uff0c\u5e76\u81ea\u52a8\u4ece\u8bf7\u6c42\u7684 URL \u4e2d\u63d0\u53d6\u6570\u636e\u3002<\/p>\n<p>In chapter 4 you saw that an ASP.NET Core application contains a middleware pipeline, which defines the behavior of your application. Middleware is well suited to handling both cross-cutting concerns, such as logging and error handling, and narrowly focused requests, such as requests for images and CSS files.<\/p>\n<p>\u5728\u7b2c 4 \u7ae0\u4e2d\uff0c\u60a8\u770b\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u5305\u542b\u4e00\u4e2a\u4e2d\u95f4\u4ef6\u7ba1\u9053\uff0c\u5b83\u5b9a\u4e49\u4e86\u5e94\u7528\u7a0b\u5e8f\u7684\u884c\u4e3a\u3002\u4e2d\u95f4\u4ef6\u975e\u5e38\u9002\u5408\u5904\u7406\u6a2a\u5207\u5173\u6ce8\u70b9\uff08\u5982\u65e5\u5fd7\u8bb0\u5f55\u548c\u9519\u8bef\u5904\u7406\uff09\u548c\u8303\u56f4\u72ed\u7a84\u7684\u8bf7\u6c42\uff08\u5982\u56fe\u50cf\u548c CSS \u6587\u4ef6\u8bf7\u6c42\uff09\u3002<\/p>\n<p>To handle more complex application logic, you\u2019ll typically use the EndpointMiddleware at the end of your middleware pipeline. This middleware can handle an appropriate request by invoking a method known as a handler and using the result to generate a response. Previous chapters described using minimal API endpoint handlers, but there are other types of handlers, such as MVC Action methods and Razor Pages, as you\u2019ll learn in part 2 of this book.<\/p>\n<p>\u4e3a\u4e86\u5904\u7406\u66f4\u590d\u6742\u7684\u5e94\u7528\u7a0b\u5e8f\u903b\u8f91\uff0c\u60a8\u901a\u5e38\u4f1a\u5728\u4e2d\u95f4\u4ef6\u7ba1\u9053\u7684\u672b\u5c3e\u4f7f\u7528 EndpointMiddleware\u3002\u6b64\u4e2d\u95f4\u4ef6\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528\u79f0\u4e3a\u5904\u7406\u7a0b\u5e8f\u7684\u65b9\u6cd5\u5e76\u4f7f\u7528\u7ed3\u679c\u751f\u6210\u54cd\u5e94\u6765\u5904\u7406\u9002\u5f53\u7684\u8bf7\u6c42\u3002\u524d\u9762\u7684\u7ae0\u8282\u4ecb\u7ecd\u4e86\u4f7f\u7528\u6700\u5c11\u7684 API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\uff0c\u4f46\u8fd8\u6709\u5176\u4ed6\u7c7b\u578b\u7684\u5904\u7406\u7a0b\u5e8f\uff0c\u4f8b\u5982 MVC\u4f5c\u65b9\u6cd5\u548c Razor Pages\uff0c\u60a8\u5c06\u5728\u672c\u4e66\u7684\u7b2c 2 \u90e8\u5206\u4e2d\u5b66\u4e60\u3002<\/p>\n<p>One aspect that I\u2019ve glossed over so far is how the EndpointMiddleware selects which handler executes when you receive a request. What makes a request appropriate for a given handler? The process of mapping a request to a handler is routing.<\/p>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u6240\u8ba8\u8bba\u7684\u4e00\u4e2a\u65b9\u9762\u662f EndpointMiddleware \u5982\u4f55\u9009\u62e9\u5728\u60a8\u6536\u5230\u8bf7\u6c42\u65f6\u6267\u884c\u54ea\u4e2a\u5904\u7406\u7a0b\u5e8f\u3002\u4ec0\u4e48\u4f7f\u8bf7\u6c42\u9002\u5408\u7ed9\u5b9a\u7684\u5904\u7406\u7a0b\u5e8f\uff1f\u5c06\u8bf7\u6c42\u6620\u5c04\u5230\u5904\u7406\u7a0b\u5e8f\u7684\u8fc7\u7a0b\u662f\u8def\u7531\u3002<\/p>\n<p><strong>Definition<\/strong> Routing in ASP.NET Core is the process of selecting a specific handler for an incoming HTTP request. In minimal APIs, the handler is the endpoint handler associated with a route. In Razor Pages, the handler is a page handler method defined in a Razor Page. In MVC, the handler is an action method in a controller.<br \/>\n<strong>\u5b9a\u4e49<\/strong> ASP.NET Core \u4e2d\u7684\u8def\u7531\u662f\u4e3a\u4f20\u5165\u7684 HTTP \u8bf7\u6c42\u9009\u62e9\u7279\u5b9a\u5904\u7406\u7a0b\u5e8f\u7684\u8fc7\u7a0b\u3002\u5728\u6700\u5c0f API \u4e2d\uff0c\u5904\u7406\u7a0b\u5e8f\u662f\u4e0e\u8def\u7531\u5173\u8054\u7684\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u3002\u5728 Razor Pages \u4e2d\uff0c\u5904\u7406\u7a0b\u5e8f\u662f\u5728 Razor Page \u4e2d\u5b9a\u4e49\u7684\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\u3002\u5728 MVC \u4e2d\uff0c\u5904\u7406\u7a0b\u5e8f\u662f\u63a7\u5236\u5668\u4e2d\u7684\u4f5c\u65b9\u6cd5\u3002<\/p>\n<p>In chapters 3 to 5, you saw several simple applications built with minimal APIs. In chapter 5, you learned the basics of routing for minimal APIs, but it\u2019s worth exploring why routing is useful as well as how to use it. Even a simple URL path such as \/person uses routing to determine which handler should be executed, as shown in figure 6.1.<\/p>\n<p>\u5728\u7b2c 3 \u7ae0\u5230\u7b2c 5 \u7ae0\u4e2d\uff0c\u60a8\u770b\u5230\u4e86\u51e0\u4e2a\u4f7f\u7528\u6700\u5c11 API \u6784\u5efa\u7684\u7b80\u5355\u5e94\u7528\u7a0b\u5e8f\u3002\u5728\u7b2c 5 \u7ae0\u4e2d\uff0c\u60a8\u5b66\u4e60\u4e86\u6700\u5c0f API \u7684\u8def\u7531\u57fa\u7840\u77e5\u8bc6\uff0c\u4f46\u503c\u5f97\u63a2\u7d22\u4e3a\u4ec0\u4e48\u8def\u7531\u5f88\u6709\u7528\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u5b83\u3002\u5373\u4f7f\u662f\u7b80\u5355\u7684 URL \u8def\u5f84\uff0c\u5982 \/person\uff0c\u4e5f\u4f7f\u7528\u8def\u7531\u6765\u786e\u5b9a\u5e94\u8be5\u6267\u884c\u54ea\u4e2a\u5904\u7406\u7a0b\u5e8f\uff0c\u5982\u56fe 6.1 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0601.png\" alt=\"\" \/><\/p>\n<p>Figure 6.1 The router compares the request URL with a list of configured route templates to determine which handler to execute.<br \/>\n\u56fe 6.1 \u8def\u7531\u5668\u5c06\u8bf7\u6c42 URL \u4e0e\u5df2\u914d\u7f6e\u7684\u8def\u7531\u6a21\u677f\u5217\u8868\u8fdb\u884c\u6bd4\u8f83\uff0c\u4ee5\u786e\u5b9a\u8981\u6267\u884c\u54ea\u4e2a\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>On the face of it, that seems pretty simple. You may wonder why I need a whole chapter to explain that obvious mapping. The simplicity of the mapping in this case belies how powerful routing can be. If this approach, using a direct comparison with static strings, were the only one available, you\u2019d be severely limited in the applications you could feasibly build.<\/p>\n<p>\u4ece\u8868\u9762\u4e0a\u770b\uff0c\u8fd9\u4f3c\u4e4e\u5f88\u7b80\u5355\u3002\u4f60\u53ef\u80fd\u4f1a\u60f3\u4e3a\u4ec0\u4e48\u6211\u9700\u8981\u4e00\u6574\u7ae0\u6765\u89e3\u91ca\u8fd9\u4e2a\u660e\u663e\u7684\u6620\u5c04\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u6620\u5c04\u7684\u7b80\u5355\u6027\u63a9\u76d6\u4e86\u8def\u7531\u7684\u5f3a\u5927\u529f\u80fd\u3002\u5982\u679c\u8fd9\u79cd\u65b9\u6cd5\uff08\u4f7f\u7528\u4e0e\u9759\u6001\u5b57\u7b26\u4e32\u7684\u76f4\u63a5\u6bd4\u8f83\uff09\u662f\u552f\u4e00\u53ef\u7528\u7684\u65b9\u6cd5\uff0c\u90a3\u4e48\u60a8\u53ef\u4ee5\u6784\u5efa\u7684\u5e94\u7528\u7a0b\u5e8f\u5c06\u53d7\u5230\u4e25\u91cd\u9650\u5236\u3002<\/p>\n<p>Consider an e-commerce application that sells multiple products. Each product needs to have its own URL, so if you were using a purely static routing system, you\u2019d have only two options:<\/p>\n<p>\u8003\u8651\u4e00\u4e2a\u9500\u552e\u591a\u79cd\u4ea7\u54c1\u7684\u7535\u5b50\u5546\u52a1\u5e94\u7528\u7a0b\u5e8f\u3002\u6bcf\u4e2a\u4ea7\u54c1\u90fd\u9700\u8981\u6709\u81ea\u5df1\u7684 URL\uff0c\u56e0\u6b64\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u7eaf\u9759\u6001\u8def\u7531\u7cfb\u7edf\uff0c\u5219\u53ea\u6709\u4e24\u4e2a\u9009\u9879\uff1a<\/p>\n<ul>\n<li>\n<p>Use a different handler for every product in your product range. That approach would be unfeasible for almost any realistically sized product range.<br \/>\n\u4e3a\u60a8\u7684\u4ea7\u54c1\u7cfb\u5217\u4e2d\u7684\u6bcf\u4e2a\u4ea7\u54c1\u4f7f\u7528\u4e0d\u540c\u7684\u5904\u7406\u7a0b\u5e8f\u3002\u8fd9\u79cd\u65b9\u6cd5\u5bf9\u4e8e\u51e0\u4e4e\u4efb\u4f55\u5b9e\u9645\u89c4\u6a21\u7684\u4ea7\u54c1\u7cfb\u5217\u90fd\u662f\u4e0d\u53ef\u884c\u7684\u3002<\/p>\n<\/li>\n<li>\n<p>Use a single handler, and use the query string to differentiate among products. This approach is much more practical, but you\u2019d end up with somewhat-ugly URLs, such as &quot;\/product?name=big-widget&quot; or &quot;\/product?id=12&quot;.<br \/>\n\u4f7f\u7528\u5355\u4e2a\u5904\u7406\u7a0b\u5e8f\uff0c\u5e76\u4f7f\u7528\u67e5\u8be2\u5b57\u7b26\u4e32\u6765\u533a\u5206\u4ea7\u54c1\u3002 \u8fd9\u79cd\u65b9\u6cd5\u8981\u5b9e\u7528\u5f97\u591a\uff0c\u4f46\u60a8\u6700\u7ec8\u4f1a\u5f97\u5230\u4e00\u4e9b\u96be\u770b\u7684 URL\uff0c\u4f8b\u5982\u201c\/product\uff1fname=big-widget\u201c \u6216 \u201d\/product\uff1fid=12\u201c \u7684 API \u7248\u672c\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Definition<\/strong> The query string is part of a URL containing additional data that doesn\u2019t fit in the path. It isn\u2019t used by the routing infrastructure to identify which handler to execute, but ASP.NET Core can extract values from the query string automatically in a process called model binding, as you\u2019ll see in chapter 7. The query string in the preceding example is id=12.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u67e5\u8be2\u5b57\u7b26\u4e32\u662f URL \u7684\u4e00\u90e8\u5206\uff0c\u5176\u4e2d\u5305\u542b\u4e0d\u9002\u5408\u8def\u5f84\u7684\u5176\u4ed6\u6570\u636e\u3002\u8def\u7531\u57fa\u7840\u8bbe\u65bd\u4e0d\u4f7f\u7528\u5b83\u6765\u6807\u8bc6\u8981\u6267\u884c\u7684\u5904\u7406\u7a0b\u5e8f\uff0c\u4f46 ASP.NET Core \u53ef\u4ee5\u5728\u79f0\u4e3a\u6a21\u578b\u7ed1\u5b9a\u7684\u8fc7\u7a0b\u4e2d\u81ea\u52a8\u4ece\u67e5\u8be2\u5b57\u7b26\u4e32\u4e2d\u63d0\u53d6\u503c\uff0c\u5982\u7b2c 7 \u7ae0\u6240\u793a\u3002\u524d\u9762\u793a\u4f8b\u4e2d\u7684\u67e5\u8be2\u5b57\u7b26\u4e32\u4e3a id=12\u3002<\/p>\n<p>With routing, you can have a single endpoint handler that can handle multiple URLs without having to resort to ugly query strings. From the point of the view of the endpoint handler, the query string and routing approaches are similar; the handler returns the results for the correct product dynamically as appropriate. The difference is that with routing, you can completely customize the URLs, as shown in figure 6.2. This feature gives you much more flexibility and can be important in real-life applications for search engine optimization (SEO).<\/p>\n<p>\u901a\u8fc7\u8def\u7531\uff0c\u60a8\u53ef\u4ee5\u62e5\u6709\u4e00\u4e2a\u53ef\u4ee5\u5904\u7406\u591a\u4e2a URL \u7684\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\uff0c\u800c\u4e0d\u5fc5\u6c42\u52a9\u4e8e\u96be\u61c2\u7684\u67e5\u8be2\u5b57\u7b26\u4e32\u3002\u4ece\u7aef\u70b9\u7684\u89c6\u89d2handler\uff0c\u5219\u67e5\u8be2\u5b57\u7b26\u4e32\u548c\u8def\u7531\u65b9\u6cd5\u7c7b\u4f3c;\u5904\u7406\u7a0b\u5e8f\u4f1a\u6839\u636e\u9700\u8981\u52a8\u6001\u8fd4\u56de\u6b63\u786e\u4ea7\u54c1\u7684\u7ed3\u679c\u3002\u533a\u522b\u5728\u4e8e\uff0c\u4f7f\u7528\u8def\u7531\uff0c\u60a8\u53ef\u4ee5\u5b8c\u5168\u81ea\u5b9a\u4e49 URL\uff0c\u5982\u56fe 6.2 \u6240\u793a\u3002\u6b64\u529f\u80fd\u4e3a\u60a8\u63d0\u4f9b\u4e86\u66f4\u5927\u7684\u7075\u6d3b\u6027\uff0c\u5e76\u4e14\u5728\u641c\u7d22\u5f15\u64ce\u4f18\u5316 \uff08SEO\uff09 \u7684\u5b9e\u9645\u5e94\u7528\u7a0b\u5e8f\u4e2d\u53ef\u80fd\u5f88\u91cd\u8981\u3002<\/p>\n<p><strong>Note<\/strong> With the flexibility of routing, you can encode the hierarchy of your site properly in your URLs, as described in Google\u2019s SEO starter guide at <a href=\"http:\/\/mng.bz\/EQ2J\">http:\/\/mng.bz\/EQ2J<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u501f\u52a9\u8def\u7531\u7684\u7075\u6d3b\u6027\uff0c\u60a8\u53ef\u4ee5\u5728 URL \u4e2d\u6b63\u786e\u7f16\u7801\u7f51\u7ad9\u7684\u5c42\u6b21\u7ed3\u6784\uff0c\u5982 <a href=\"http:\/\/mng.bz\/EQ2J\">http:\/\/mng.bz\/EQ2J<\/a> \u7684 Google SEO \u5165\u95e8\u6307\u5357\u4e2d\u6240\u8ff0\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0602.png\" alt=\"\" \/><\/p>\n<p>Figure 6.2 If you use static URL-based mapping, you need a different handler for every product in your product range. With a query string, you can use a single handler, and the query string contains the data. With routing, multiple URLs map to a single handler, and a dynamic parameter captures the difference in the URL.<br \/>\n\u56fe 6.2 \u5982\u679c\u4f7f\u7528\u57fa\u4e8e\u9759\u6001 URL \u7684\u6620\u5c04\uff0c\u5219\u4ea7\u54c1\u8303\u56f4\u4e2d\u7684\u6bcf\u4e2a\u4ea7\u54c1\u90fd\u9700\u8981\u4e0d\u540c\u7684\u5904\u7406\u7a0b\u5e8f\u3002\u4f7f\u7528\u67e5\u8be2\u5b57\u7b26\u4e32\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u5355\u4e2a\u5904\u7406\u7a0b\u5e8f\uff0c\u5e76\u4e14\u67e5\u8be2\u5b57\u7b26\u4e32\u5305\u542b\u6570\u636e\u3002\u4f7f\u7528\u8def\u7531\u65f6\uff0c\u591a\u4e2a URL \u6620\u5c04\u5230\u5355\u4e2a\u5904\u7406\u7a0b\u5e8f\uff0c\u5e76\u4e14\u52a8\u6001\u53c2\u6570\u6355\u83b7 URL \u4e2d\u7684\u5dee\u5f02\u3002<\/p>\n<p>As well as enabling dynamic URLs, routing fundamentally decouples the URLs in your application from the definition of your handlers.<\/p>\n<p>\u9664\u4e86\u542f\u7528\u52a8\u6001 URL \u4e4b\u5916\uff0c\u8def\u7531\u8fd8\u4ece\u6839\u672c\u4e0a\u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 URL \u4e0e\u5904\u7406\u7a0b\u5e8f\u7684\u5b9a\u4e49\u5206\u79bb\u3002<\/p>\n<blockquote>\n<p>File-system based routing<br \/>\n\u57fa\u4e8e\u6587\u4ef6\u7cfb\u7edf\u7684\u8def\u7531<br \/>\nIn one alternative to routing, the location of a handler on disk dictates the URL you use to invoke it. The downside of this approach is that if you want to change an exposed URL, you also need to change the location of the handler on disk.<br \/>\n\u5728\u8def\u7531\u7684\u53e6\u4e00\u79cd\u66ff\u4ee3\u65b9\u6cd5\u4e2d\uff0c\u5904\u7406\u7a0b\u5e8f\u5728\u78c1\u76d8\u4e0a\u7684\u4f4d\u7f6e\u51b3\u5b9a\u4e86\u7528\u4e8e\u8c03\u7528\u5b83\u7684 URL\u3002\u8fd9\u79cd\u65b9\u6cd5\u7684\u7f3a\u70b9\u662f\uff0c\u5982\u679c\u8981\u66f4\u6539\u516c\u5f00\u7684 URL\uff0c\u8fd8\u9700\u8981\u66f4\u6539\u5904\u7406\u7a0b\u5e8f\u5728\u78c1\u76d8\u4e0a\u7684\u4f4d\u7f6e\u3002<br \/>\nThis file-based approach may sound like a strange choice, but it has many advantages for some apps, primarily in terms of simplicity. As you\u2019ll see in part 2, Razor Pages is partially file-based but also uses routing to get the best of both worlds!<br \/>\n\u8fd9\u79cd\u57fa\u4e8e\u6587\u4ef6\u7684\u65b9\u6cd5\u542c\u8d77\u6765\u53ef\u80fd\u5f88\u5947\u602a\uff0c\u4f46\u5bf9\u4e8e\u67d0\u4e9b\u5e94\u7528\u7a0b\u5e8f\u6765\u8bf4\uff0c\u5b83\u6709\u5f88\u591a\u4f18\u70b9\uff0c\u4e3b\u8981\u662f\u5728\u7b80\u5355\u6027\u65b9\u9762\u3002\u6b63\u5982\u60a8\u5c06\u5728\u7b2c 2 \u90e8\u5206\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0cRazor Pages \u90e8\u5206\u57fa\u4e8e\u6587\u4ef6\uff0c\u4f46\u4e5f\u4f7f\u7528\u8def\u7531\u6765\u83b7\u5f97\u4e24\u5168\u5176\u7f8e\u7684\u6548\u679c\uff01<\/p>\n<\/blockquote>\n<p>With routing it\u2019s easy to modify your exposed URLs without changing any filenames or locations. You can also use routing to create friendlier URLs for users, which can improve discovery and \u201chackability.\u201d All of the following routes could point to the same handler:<\/p>\n<p>\u901a\u8fc7\u8def\u7531\uff0c\u53ef\u4ee5\u8f7b\u677e\u4fee\u6539\u516c\u5f00\u7684 URL\uff0c\u800c\u65e0\u9700\u66f4\u6539\u4efb\u4f55\u6587\u4ef6\u540d\u6216\u4f4d\u7f6e\u3002\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u4e3a\u7528\u6237\u521b\u5efa\u66f4\u53cb\u597d\u7684 URL\uff0c\u8fd9\u53ef\u4ee5\u63d0\u9ad8\u53d1\u73b0\u548c\u201c\u53ef\u9ed1\u5ba2\u653b\u51fb\u6027\u201d\u3002\u4ee5\u4e0b\u6240\u6709\u8def\u7531\u90fd\u53ef\u4ee5\u6307\u5411\u540c\u4e00\u4e2a\u5904\u7406\u7a0b\u5e8f\uff1a<\/p>\n<ul>\n<li>\/rates\/view\/1<\/li>\n<li>\/rates\/view\/USD<\/li>\n<li>\/rates\/current-exchange-rate\/USD<\/li>\n<li>\/current-exchange-rate-for-USD<\/li>\n<\/ul>\n<p>This level of customization isn\u2019t often necessary, but it\u2019s quite useful to have the capability to customize your app\u2019s URLs when you need it. In the next section we\u2019ll look at how routing works in practice in ASP.NET Core.<\/p>\n<p>\u8fd9\u79cd\u7ea7\u522b\u7684\u81ea\u5b9a\u4e49\u901a\u5e38\u4e0d\u662f\u5fc5\u9700\u7684\uff0c\u4f46\u80fd\u591f\u5728\u9700\u8981\u65f6\u81ea\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u7684 URL \u975e\u5e38\u6709\u7528\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3 ASP.NET Core \u4e2d\u7684\u8def\u7531\u5b9e\u9645\u5de5\u4f5c\u539f\u7406\u3002<\/p>\n<h2>6.2 Endpoint routing in ASP.NET Core<\/h2>\n<h2>6.2 ASP.NET Core \u4e2d\u7684\u7ec8\u7aef\u8282\u70b9\u8def\u7531<\/h2>\n<p>In this section I describe how endpoint routing works in ASP.NET Core, specifically with respect to minimal APIs and the middleware pipeline. In chapter 14 you\u2019ll learn how routing is used with Razor Pages and the ASP.NET Core MVC framework.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u7ec8\u7aef\u8282\u70b9\u8def\u7531\u5728 ASP.NET Core \u4e2d\u7684\u5de5\u4f5c\u539f\u7406\uff0c\u7279\u522b\u662f\u5173\u4e8e\u6700\u5c0f API \u548c\u4e2d\u95f4\u4ef6\u7ba1\u9053\u3002\u5728\u7b2c 14 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5c06\u8def\u7531\u4e0e Razor Pages \u548c ASP.NET Core MVC \u6846\u67b6\u4e00\u8d77\u4f7f\u7528\u3002<\/p>\n<p>Routing has been part of ASP.NET Core since its inception, but it has been through some big changes. In ASP.NET Core 2.0 and 2.1, routing was restricted to Razor Pages and the ASP.NET Core MVC framework. There was no dedicated routing middleware in the middleware pipeline; routing happened only within Razor Pages or MVC components.<\/p>\n<p>\u8def\u7531\u81ea\u6210\u7acb\u4ee5\u6765\u4e00\u76f4\u662f ASP.NET Core \u7684\u4e00\u90e8\u5206\uff0c\u4f46\u5b83\u7ecf\u5386\u4e86\u4e00\u4e9b\u91cd\u5927\u53d8\u5316\u3002\u5728 ASP.NET Core \u4e2d2.0 \u548c 2.1 \u4e2d\uff0c\u8def\u7531\u4ec5\u9650\u4e8e Razor Pages \u548c ASP.NET Core MVC \u6846\u67b6\u3002\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e2d\u6ca1\u6709\u4e13\u7528\u7684\u8def\u7531\u4e2d\u95f4\u4ef6;\u8def\u7531\u4ec5\u5728 Razor Pages \u6216 MVC \u7ec4\u4ef6\u4e2d\u53d1\u751f\u3002<\/p>\n<p>Unfortunately, restricting routing to the MVC and Razor Pages infrastructure made some things a bit messy. Some cross-cutting concerns, such as authorization, were restricted to the MVC infrastructure and were hard to use from other middleware in your application. That restriction caused inevitable duplication, which wasn\u2019t ideal.<\/p>\n<p>\u9057\u61be\u7684\u662f\uff0c\u5c06\u8def\u7531\u5230 MVC \u548c Razor Pages \u57fa\u7840\u7ed3\u6784\u4f1a\u4f7f\u67d0\u4e9b\u4e8b\u60c5\u53d8\u5f97\u6709\u70b9\u6df7\u4e71\u3002\u4e00\u4e9b\u6a2a\u5207\u5173\u6ce8\u70b9\uff08\u5982\u6388\u6743\uff09\u4ec5\u9650\u4e8e MVC \u57fa\u7840\u8bbe\u65bd\uff0c\u5e76\u4e14\u5f88\u96be\u4ece\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5176\u4ed6\u4e2d\u95f4\u4ef6\u4e2d\u4f7f\u7528\u3002\u8fd9\u79cd\u9650\u5236\u5bfc\u81f4\u4e86\u4e0d\u53ef\u907f\u514d\u7684\u91cd\u590d\uff0c\u8fd9\u5e76\u4e0d\u7406\u60f3\u3002<\/p>\n<p>ASP.NET Core 3.0 introduced a new routing system: endpoint routing. Endpoint routing makes the routing system a more fundamental feature of ASP.NET Core and no longer ties it to the MVC infrastructure. Now Razor Pages, MVC, and other middleware can all use the same routing system. .NET 7 continues to use the same endpoint routing system, which is integral to the minimal API functionality that was introduced in .NET 6.<\/p>\n<p>ASP.NET Core 3.0 \u5f15\u5165\u4e86\u4e00\u4e2a\u65b0\u7684\u8def\u7531\u7cfb\u7edf\uff1a\u7aef\u70b9\u8def\u7531\u3002\u7aef\u70b9\u8def\u7531\u4f7f\u8def\u7531\u7cfb\u7edf\u6210\u4e3a ASP.NET Core \u7684\u66f4\u57fa\u672c\u529f\u80fd\uff0c\u4e0d\u518d\u5c06\u5176\u7ed1\u5b9a\u5230 MVC \u57fa\u7840\u8bbe\u65bd\u3002\u73b0\u5728\uff0cRazor Pages\u3001MVC \u548c\u5176\u4ed6\u4e2d\u95f4\u4ef6\u90fd\u53ef\u4ee5\u4f7f\u7528\u76f8\u540c\u7684\u8def\u7531\u7cfb\u7edf\u3002.NET 7 \u7ee7\u7eed\u4f7f\u7528\u76f8\u540c\u7684\u7ec8\u7ed3\u70b9\u8def\u7531\u7cfb\u7edf\uff0c\u8fd9\u662f .NET 6 \u4e2d\u5f15\u5165\u7684\u6700\u5c0f API \u529f\u80fd\u4e0d\u53ef\u6216\u7f3a\u7684\u4e00\u90e8\u5206\u3002<\/p>\n<p>Endpoint routing is fundamental to all but the simplest ASP.NET Core apps. It\u2019s implemented with two pieces of middleware, which you\u2019ve already seen:<\/p>\n<p>\u7aef\u70b9\u8def\u7531\u662f\u9664\u6700\u7b80\u5355\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e4b\u5916\u7684\u6240\u6709\u5e94\u7528\u7a0b\u5e8f\u7684\u57fa\u7840\u3002\u5b83\u662f\u901a\u8fc7\u4e24\u4e2a\u4e2d\u95f4\u4ef6\u5b9e\u73b0\u7684\uff0c\u60a8\u5df2\u7ecf\u770b\u5230\u4e86\uff1a<\/p>\n<ul>\n<li>\n<p>EndpointRoutingMiddleware\u2014This middleware chooses which registered endpoints execute for a given request at runtime. To make it easier to distinguish between the two types of middleware, I\u2019ll be referring to this middleware as the RoutingMiddleware throughout this book.<br \/>\nEndpointRoutingMiddleware \u2014 \u6b64\u4e2d\u95f4\u4ef6\u9009\u62e9\u5728\u8fd0\u884c\u65f6\u4e3a\u7ed9\u5b9a\u8bf7\u6c42\u6267\u884c\u54ea\u4e9b\u5df2\u6ce8\u518c\u7684\u7ec8\u7aef\u8282\u70b9\u3002\u4e3a\u4e86\u66f4\u5bb9\u6613\u533a\u5206\u8fd9\u4e24\u79cd\u7c7b\u578b\u7684\u4e2d\u95f4\u4ef6\uff0c\u6211\u5728\u672c\u4e66\u4e2d\u5c06\u6b64\u4e2d\u95f4\u4ef6\u79f0\u4e3a RoutingMiddleware\u3002<\/p>\n<\/li>\n<li>\n<p>EndpointMiddleware\u2014This middleware is typically placed at the end of your middleware pipeline. The middleware executes the endpoint selected by the RoutingMiddleware at runtime.<br \/>\nEndpointMiddleware \u2014 \u6b64\u4e2d\u95f4\u4ef6\u901a\u5e38\u4f4d\u4e8e\u4e2d\u95f4\u4ef6\u7ba1\u9053\u7684\u672b\u5c3e\u3002\u4e2d\u95f4\u4ef6\u5728\u8fd0\u884c\u65f6\u6267\u884c RoutingMiddleware \u9009\u62e9\u7684\u7aef\u70b9\u3002<\/p>\n<\/li>\n<\/ul>\n<p>You register the endpoints in your application by calling Map* functions on an IEndpointRouteBuilder instance. In .NET 7 apps, this instance typically is a WebApplication instance but doesn\u2019t have to be, as you\u2019ll see in chapter 30.<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728 IEndpointRouteBuilder \u5b9e\u4f8b\u4e0a\u8c03\u7528 Map* \u51fd\u6570\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u7ec8\u7aef\u8282\u70b9\u3002\u5728 .NET 7 \u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u6b64\u5b9e\u4f8b\u901a\u5e38\u662f Web- Application \u5b9e\u4f8b\uff0c\u4f46\u5e76\u975e\u5fc5\u987b\u5982\u6b64\uff0c\u5982\u7b2c 30 \u7ae0\u6240\u793a\u3002<\/p>\n<p><em>Definition<\/em> An endpoint in ASP.NET Core is a handler that returns a response. Each endpoint is associated with a URL pattern. Depending on the type of application you\u2019re building, minimal API handlers, Razor Page handlers, or MVC controller action methods typically make up the bulk of the endpoints in an application. You can also use simple middleware as an endpoint or a health-check endpoint, for example.<br \/>\n<em>\u5b9a\u4e49<\/em> ASP.NET Core \u4e2d\u7684\u7ec8\u7aef\u8282\u70b9\u662f\u8fd4\u56de\u54cd\u5e94\u7684\u5904\u7406\u7a0b\u5e8f\u3002\u6bcf\u4e2a\u7aef\u70b9\u90fd\u4e0e\u4e00\u4e2a URL \u6a21\u5f0f\u76f8\u5173\u8054\u3002\u6839\u636e\u8981\u751f\u6210\u7684\u5e94\u7528\u7a0b\u5e8f\u7c7b\u578b\uff0c\u6700\u5c11\u7684 API \u5904\u7406\u7a0b\u5e8f\u3001Razor Page \u5904\u7406\u7a0b\u5e8f\u6216 MVC \u63a7\u5236\u5668\u4f5c\u65b9\u6cd5\u901a\u5e38\u6784\u6210\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5927\u90e8\u5206\u7ec8\u7ed3\u70b9\u3002\u4f8b\u5982\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528\u7b80\u5355\u7684\u4e2d\u95f4\u4ef6\u4f5c\u4e3a\u7ec8\u7aef\u8282\u70b9\uff0c\u6216\u8005\u60a8\u53ef\u4ee5\u4f7f\u7528\u8fd0\u884c\u72b6\u51b5\u68c0\u67e5\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>WebApplication implements IEndpointRouteBuilder, so you can register endpoints on it directly. Listing 6.1 shows how you\u2019d register several endpoints:<\/p>\n<p>WebApplication \u5b9e\u73b0 IEndpointRouteBuilder\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u76f4\u63a5\u5728\u5176\u4e0a\u6ce8\u518c\u7aef\u70b9\u3002\u6e05\u5355 6.1 \u5c55\u793a\u4e86\u5982\u4f55\u6ce8\u518c\u591a\u4e2a\u7aef\u70b9\uff1a<\/p>\n<ul>\n<li>\n<p>A minimal API handler using MapGet(), as you\u2019ve seen in previous chapters.<br \/>\n\u4f7f\u7528 MapGet\uff08\uff09 \u7684\u6700\u5c0f API \u5904\u7406\u7a0b\u5e8f\uff0c\u5982\u524d\u51e0\u7ae0\u6240\u793a\u3002<\/p>\n<\/li>\n<li>\n<p>A health-check endpoint using MapHealthChecks(). You can read more about health checks at <a href=\"http:\/\/mng.bz\/N2YD\">http:\/\/mng.bz\/N2YD<\/a>.<br \/>\n\u4f7f\u7528 MapHealthChecks\uff08\uff09 \u7684\u8fd0\u884c\u72b6\u51b5\u68c0\u67e5\u7ec8\u7aef\u8282\u70b9\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/N2YD\">http:\/\/mng.bz\/N2YD<\/a> \u4e0a\u9605\u8bfb\u6709\u5173\u8fd0\u884c\u72b6\u51b5\u68c0\u67e5\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<\/li>\n<li>\n<p>All Razor Pages endpoints in the application using MapRazorPages(). You\u2019ll learn more about routing with Razor Pages in chapter 14.<br \/>\n\u4f7f\u7528 MapRazorPages\uff08\uff09 \u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Razor Pages \u7ec8\u7ed3\u70b9\u3002\u60a8\u5c06\u5728\u7b2c 14 \u7ae0\u4e2d\u4e86\u89e3\u6709\u5173\u4f7f\u7528 Razor Pages \u8fdb\u884c\u8def\u7531\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<\/li>\n<\/ul>\n<p>Listing 6.1 Registering multiple endpoints with WebApplication<br \/>\n\u6e05\u5355 6.1 \u4f7f\u7528 \u6ce8\u518c\u591a\u4e2a\u7aef\u70b9Web\u5e94\u7528\u7a0b\u5e8f<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddHealthChecks();          \/\/  \u2776\nbuilder.Services.AddRazorPages();            \/\/  \u2776\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/test&quot;, () =&gt; &quot;Hello world!&quot;);   \/\/  \u2777\napp.MapHealthChecks(&quot;\/healthz&quot;);             \/\/  \u2778\napp.MapRazorPages();                         \/\/  \u2779\n\napp.Run();\n<\/pre>\n<p>\u2776 Adds the services required by the health-check middleware and Razor Pages<br \/>\n\u6dfb\u52a0\u8fd0\u884c\u72b6\u51b5\u68c0\u67e5\u4e2d\u95f4\u4ef6\u548c Razor Pages \u6240\u9700\u7684\u670d\u52a1<\/p>\n<p>\u2777 Registers a minimal API endpoint that returns \u201cHello World!\u201d at the route \/test<br \/>\n\u6ce8\u518c\u4e00\u4e2a\u6700\u5c0f\u7684 API \u7aef\u70b9\uff0c\u8be5\u7aef\u70b9\u5728\u8def\u7531 \/test \u4e2d\u8fd4\u56de \u201cHello World\uff01\u201d<\/p>\n<p>\u2778 Registers a health-check endpoint at the route \/healthz<br \/>\n\u5728\u8def\u7531 \/healthz \u4e2d\u6ce8\u518c\u4e00\u4e2a\u5065\u5eb7\u68c0\u67e5\u7aef\u70b9<\/p>\n<p>\u2779 Registers all the Razor Pages in your application as endpoints<br \/>\n\u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Razor Pages \u6ce8\u518c\u4e3a\u7aef\u70b9<\/p>\n<p>Each endpoint is associated with a route template that defines which URLs the endpoint should match. You can see two route templates, &quot;\/healthz&quot; and &quot;\/test&quot;, in listing 6.1.<\/p>\n<p>\u6bcf\u4e2a\u7ec8\u7aef\u8282\u70b9\u90fd\u4e0e\u4e00\u4e2a\u8def\u7531\u6a21\u677f\u76f8\u5173\u8054\uff0c\u8be5\u6a21\u677f\u5b9a\u4e49\u7ec8\u7aef\u8282\u70b9\u5e94\u5339\u914d\u7684 URL\u3002\u5728\u6e05\u5355 6.1 \u4e2d\u53ef\u4ee5\u770b\u5230\u4e24\u4e2a\u8def\u7531\u6a21\u677f\uff0c\u201c\/healthz\u201d \u548c \u201c\/test\u201d\u3002<\/p>\n<p><b>DEFINITION<\/b> A route template is a URL pattern that is used to match against request URLs, which are strings of fixed values, such as &quot;\/test&quot; in the previous listing. They can also contain placeholders for variables, as you\u2019ll see in section 6.3.<br \/>\n\u5b9a\u4e49\uff1a \u8def\u7531\u6a21\u677f\u662f\u4e00\u79cd URL \u6a21\u5f0f\uff0c\u7528\u4e8e\u5339\u914d\u8bf7\u6c42 URL\uff0c\u8bf7\u6c42 URL \u662f\u56fa\u5b9a\u503c\u7684\u5b57\u7b26\u4e32\uff0c\u5982\u4e0a\u4e00\u4e2a\u5217\u8868\u4e2d\u7684 \u201c\/test\u201d\u3002 \u5b83\u4eec\u8fd8\u53ef\u4ee5\u5305\u542b\u53d8\u91cf\u7684\u5360\u4f4d\u7b26\uff0c\u5982 6.3 \u8282\u6240\u793a\u3002<\/p>\n<p>The WebApplication stores the registered routes and endpoints in a dictionary that\u2019s shared by the RoutingMiddleware and the EndpointMiddleware.<\/p>\n<p>WebApplication \u5c06\u6ce8\u518c\u7684\u8def\u7531\u548c\u7ec8\u7aef\u8282\u70b9\u5b58\u50a8\u5728 RoutingMiddleware \u548c EndpointMiddleware \u5171\u4eab\u7684\u5b57\u5178\u4e2d\u3002<\/p>\n<p><b>TIP<\/b> By default, WebApplication automatically adds the RoutingMiddleware to the start of the middleware and EndpointMiddleware to the end of the middleware pipeline, though you can override the location in the pipeline by calling UseRouting() or UseEndpoints(). See section 4.2.3 for more details about automatically added middleware.<br \/>\n\u63d0\u793a\uff1a \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cWebApplication \u4f1a\u81ea\u52a8\u5c06 RoutingMiddleware \u6dfb\u52a0\u5230\u4e2d\u95f4\u4ef6\u7684\u5f00\u5934\uff0c\u5c06 EndpointMiddleware \u6dfb\u52a0\u5230\u4e2d\u95f4\u4ef6\u7ba1\u9053\u7684\u672b\u5c3e\uff0c\u4f46\u60a8\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528 UseRouting\uff08\uff09 \u6216 UseEndpoints\uff08\uff09 \u6765\u8986\u76d6\u7ba1\u9053\u4e2d\u7684\u4f4d\u7f6e\u3002\u6709\u5173\u81ea\u52a8\u6dfb\u52a0\u7684\u4e2d\u95f4\u4ef6\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u89c1 Section 4.2.3\u3002<\/p>\n<p>At runtime, the RoutingMiddleware compares an incoming request with the routes registered in the dictionary. If the RoutingMiddleware finds a matching endpoint, it makes a note of which endpoint was selected and attaches that to the request\u2019s HttpContext object. Then it calls the next middleware in the pipeline. When the request reaches the EndpointMiddleware, the middleware checks to see which endpoint was selected and executes the endpoint (and any associated endpoint filters), as shown in figure 6.3.<\/p>\n<p>\u5728\u8fd0\u884c\u65f6\uff0cRoutingMiddleware \u5c06\u4f20\u5165\u8bf7\u6c42\u4e0e\u5b57\u5178\u4e2d\u6ce8\u518c\u7684\u8def\u7531\u8fdb\u884c\u6bd4\u8f83\u3002\u5982\u679c RoutingMiddleware \u627e\u5230\u5339\u914d\u7684\u7aef\u70b9\uff0c\u5b83\u4f1a\u8bb0\u4e0b\u9009\u62e9\u4e86\u54ea\u4e2a\u7aef\u70b9\uff0c\u5e76\u5c06\u5176\u9644\u52a0\u5230\u8bf7\u6c42\u7684 HttpContext \u5bf9\u8c61\u3002\u7136\u540e\uff0c\u5b83\u4f1a\u8c03\u7528\u7ba1\u9053\u4e2d\u7684\u4e0b\u4e00\u4e2a\u4e2d\u95f4\u4ef6\u3002\u5f53\u8bf7\u6c42\u5230\u8fbe EndpointMiddleware \u65f6\uff0c\u4e2d\u95f4\u4ef6\u4f1a\u68c0\u67e5\u9009\u62e9\u54ea\u4e2a\u7aef\u70b9\u5e76\u6267\u884c\u8be5\u7aef\u70b9\uff08\u4ee5\u53ca\u4efb\u4f55\u5173\u8054\u7684\u7aef\u70b9\u8fc7\u6ee4\u5668\uff09\uff0c\u5982\u56fe 6.3 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0603.png\" alt=\"\" \/><\/p>\n<p>Figure 6.3 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 won\u2019t generate a response.<br \/>\n\u56fe 6.3 \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>If the request URL doesn\u2019t match a route template, the RoutingMiddleware doesn\u2019t select an endpoint, but the request still continues down the middleware pipeline. As no endpoint is selected, the EndpointMiddleware silently ignores the request and passes it to the next middleware in the pipeline. The EndpointMiddleware is typically the final middleware in the pipeline, so the \u201cnext\u201d middleware is normally the dummy middleware that always returns a 404 Not Found response, as you saw in chapter 4.<\/p>\n<p>\u5982\u679c\u8bf7\u6c42 URL \u4e0e\u8def\u7531\u6a21\u677f\u4e0d\u5339\u914d\uff0c\u5219 RoutingMiddleware \u4e0d\u4f1a\u9009\u62e9\u7ec8\u7aef\u8282\u70b9\uff0c\u4f46\u8bf7\u6c42\u4ecd\u4f1a\u7ee7\u7eed\u6cbf\u4e2d\u95f4\u4ef6\u7ba1\u9053\u5411\u4e0b\u79fb\u52a8\u3002\u7531\u4e8e\u672a\u9009\u62e9\u4efb\u4f55\u7ec8\u7aef\u8282\u70b9\uff0c\u56e0\u6b64 EndpointMiddleware \u4f1a\u9759\u9ed8\u5ffd\u7565\u8be5\u8bf7\u6c42\u5e76\u5c06\u5176\u4f20\u9012\u7ed9\u7ba1\u9053\u4e2d\u7684\u4e0b\u4e00\u4e2a\u4e2d\u95f4\u4ef6\u3002EndpointMiddleware \u901a\u5e38\u662f\u7ba1\u9053\u4e2d\u7684\u6700\u540e\u4e00\u4e2a\u4e2d\u95f4\u4ef6\uff0c\u56e0\u6b64 \u201cnext\u201d \u4e2d\u95f4\u4ef6\u901a\u5e38\u662f\u59cb\u7ec8\u8fd4\u56de 404 Not Found \u54cd\u5e94\u7684\u865a\u62df\u4e2d\u95f4\u4ef6\uff0c\u5982\u7b2c 4 \u7ae0\u6240\u793a\u3002<\/p>\n<p><b>TIP<\/b> If the request URL doesn\u2019t match a route template, no endpoint is selected or executed. The whole middleware pipeline is still executed, but typically a 404 response is returned when the request reaches the dummy 404 middleware.<br \/>\n\u63d0\u793a\uff1a \u5982\u679c\u8bf7\u6c42 URL \u4e0e\u8def\u7531\u6a21\u677f\u4e0d\u5339\u914d\uff0c\u5219\u4e0d\u4f1a\u9009\u62e9\u6216\u6267\u884c\u4efb\u4f55\u7ec8\u7aef\u8282\u70b9\u3002\u6574\u4e2a\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4ecd\u7136\u4f1a\u6267\u884c\uff0c\u4f46\u5f53\u8bf7\u6c42\u5230\u8fbe\u865a\u62df 404 \u4e2d\u95f4\u4ef6\u65f6\uff0c\u901a\u5e38\u4f1a\u8fd4\u56de 404 \u54cd\u5e94\u3002<\/p>\n<p>The advantage of having two separate pieces of middleware to handle this process may not be obvious at first blush. Figure 6.3 hinted at the main benefit: all middleware placed after the RoutingMiddleware can see which endpoint is going to be executed before it is.<\/p>\n<p>\u4f7f\u7528\u4e24\u4e2a\u5355\u72ec\u7684\u4e2d\u95f4\u4ef6\u6765\u5904\u7406\u6b64\u8fc7\u7a0b\u7684\u4f18\u52bf\u4e4d\u4e00\u770b\u53ef\u80fd\u5e76\u4e0d\u660e\u663e\u3002\u56fe 6.3 \u6697\u793a\u4e86\u4e3b\u8981\u7684\u597d\u5904\uff1a\u6240\u6709\u653e\u5728 RoutingMiddleware \u540e\u9762\u7684\u4e2d\u95f4\u4ef6\u90fd\u53ef\u4ee5\u5728\u6267\u884c\u4e4b\u524d\u770b\u5230\u54ea\u4e2a\u7aef\u70b9\u5c06\u88ab\u6267\u884c\u3002<\/p>\n<p><b>NOTE<\/b>  Only middleware placed after the RoutingMiddleware can detect which endpoint is going to be executed.<br \/>\n\u6ce8\u610f\uff1a \u53ea\u6709\u653e\u5728 RoutingMiddleware \u4e4b\u540e\u7684 middleware \u624d\u80fd\u68c0\u6d4b\u5230\u5c06\u8981\u6267\u884c\u7684\u7aef\u70b9\u3002<\/p>\n<p>Figure 6.4 shows a more realistic middleware pipeline in which middleware is placed both before the RoutingMiddleware and between the RoutingMiddleware and the EndpointMiddleware.<\/p>\n<p>\u56fe 6.4 \u663e\u793a\u4e86\u4e00\u4e2a\u66f4\u771f\u5b9e\u7684\u4e2d\u95f4\u4ef6\u7ba1\u9053\uff0c\u5176\u4e2d\u4e2d\u95f4\u4ef6\u653e\u7f6e\u5728 RoutingMiddleware \u4e4b\u524d\u4ee5\u53ca RoutingMiddleware \u548c EndpointMiddleware \u4e4b\u95f4\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0604.png\" alt=\"\" \/><\/p>\n<p>Figure 6.4 Middleware placed before the routing middleware doesn\u2019t know which endpoint the routing middleware will select. Middleware placed between the routing middleware and the endpoint middleware can see the selected endpoint.<br \/>\n\u56fe 6.4 \u653e\u7f6e\u5728\u8def\u7531\u4e2d\u95f4\u4ef6\u524d\u9762\u7684\u4e2d\u95f4\u4ef6\u4e0d\u77e5\u9053\u8def\u7531\u4e2d\u95f4\u4ef6\u5c06\u9009\u62e9\u54ea\u4e2a\u7aef\u70b9\u3002\u653e\u7f6e\u5728\u8def\u7531\u4e2d\u95f4\u4ef6\u548c\u7ec8\u7aef\u8282\u70b9\u4e2d\u95f4\u4ef6\u4e4b\u95f4\u7684\u4e2d\u95f4\u4ef6\u53ef\u4ee5\u770b\u5230\u6240\u9009\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>The StaticFileMiddleware in figure 6.4 is placed before the RoutingMiddleware, so it executes before an endpoint is selected. Conversely, the AuthorizationMiddleware is placed after the RoutingMiddleware, so it can tell which minimal API endpoint will be executed eventually. In addition, it can access certain metadata about the endpoint, such as its name and the permissions required to access it.<\/p>\n<p>\u56fe 6.4 \u4e2d\u7684 StaticFileMiddleware \u4f4d\u4e8e RoutingMiddleware \u4e4b\u524d\uff0c\u56e0\u6b64\u5b83\u5728\u9009\u62e9\u7aef\u70b9\u4e4b\u524d\u6267\u884c\u3002\u76f8\u53cd\uff0cAuthorizationMiddleware \u4f4d\u4e8e RoutingMiddleware \u4e4b\u540e\uff0c\u56e0\u6b64\u5b83\u53ef\u4ee5\u5224\u65ad\u6700\u7ec8\u5c06\u6267\u884c\u54ea\u4e2a\u6700\u5c0f\u7684 API \u7aef\u70b9\u3002\u6b64\u5916\uff0c\u5b83\u8fd8\u53ef\u4ee5\u8bbf\u95ee\u6709\u5173\u7ec8\u7aef\u8282\u70b9\u7684\u67d0\u4e9b\u5143\u6570\u636e\uff0c\u4f8b\u5982\u5176\u540d\u79f0\u548c\u8bbf\u95ee\u7ec8\u7aef\u8282\u70b9\u6240\u9700\u7684\u6743\u9650\u3002<\/p>\n<p><b>TIP<\/b> The AuthorizationMiddleware needs to know which endpoint will be executed, so it must be placed after the RoutingMiddleware and before the EndpointMiddleware in your middleware pipeline. I discuss authorization in more detail in chapter 24.<br \/>\n\u63d0\u793a\uff1a AuthorizationMiddleware \u9700\u8981\u77e5\u9053\u5c06\u6267\u884c\u54ea\u4e2a\u7aef\u70b9\uff0c\u56e0\u6b64\u5b83\u5fc5\u987b\u653e\u5728\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e2d\u7684 RoutingMiddleware \u4e4b\u540e\u548c EndpointMiddleware \u4e4b\u524d\u3002\u6211\u5728\u7b2c 24 \u7ae0\u4e2d\u66f4\u8be6\u7ec6\u5730\u8ba8\u8bba\u4e86\u6388\u6743\u3002<\/p>\n<p>It\u2019s important to remember the different roles of the two types of routing middleware when building your application. If you have a piece of middleware that needs to know which endpoint (if any) a given request will execute, you need to make sure to place it after the RoutingMiddleware and before the EndpointMiddleware.<\/p>\n<p>\u5728\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u8bf7\u52a1\u5fc5\u8bb0\u4f4f\u8fd9\u4e24\u79cd\u7c7b\u578b\u7684\u8def\u7531\u4e2d\u95f4\u4ef6\u7684\u4e0d\u540c\u89d2\u8272\u3002\u5982\u679c\u4f60\u6709\u4e00\u4e2a\u4e2d\u95f4\u4ef6\u9700\u8981\u77e5\u9053\u7ed9\u5b9a\u8bf7\u6c42\u5c06\u6267\u884c\u54ea\u4e2a\u7aef\u70b9\uff08\u5982\u679c\u6709\u7684\u8bdd\uff09\uff0c\u4f60\u9700\u8981\u786e\u4fdd\u5c06\u5176\u653e\u5728 RoutingMiddleware \u4e4b\u540e\u548c EndpointMiddleware \u4e4b\u524d\u3002<\/p>\n<p><b>TIP<\/b> If you want to place middleware before the RoutingMiddleware, such as the StaticFileMiddleware in figure 6.4, you need to override the automatic middleware added by WebApplication by calling UseRouting() at the appropriate point in your middleware pipeline. See listing 4.3 in chapter 4 for an example.<br \/>\n\u63d0\u793a\uff1a \u5982\u679c\u4f60\u60f3\u628a\u4e2d\u95f4\u4ef6\u653e\u5728 RoutingMiddleware \u4e4b\u524d\uff0c\u6bd4\u5982\u56fe 6.4 \u4e2d\u7684 StaticFileMiddleware\uff0c\u4f60\u9700\u8981\u5728\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e2d\u7684\u9002\u5f53\u4f4d\u7f6e\u8c03\u7528 UseRouting\uff08\uff09 \u6765\u8986\u76d6 WebApplication \u6dfb\u52a0\u7684\u81ea\u52a8\u4e2d\u95f4\u4ef6\u3002\u6709\u5173\u793a\u4f8b\uff0c\u8bf7\u53c2\u89c1\u7b2c 4 \u7ae0\u4e2d\u7684\u6e05\u5355 4.3\u3002<\/p>\n<p>I\u2019ve covered how the RoutingMiddleware and EndpointMiddleware interact to provide routing capabilities in ASP.NET Core, but we\u2019ve looked at only simple route templates so far. In the next section we\u2019ll look at some of the many features available with route templates.<\/p>\n<p>\u6211\u5df2\u7ecf\u4ecb\u7ecd\u4e86 RoutingMiddleware \u548c EndpointMiddleware \u5982\u4f55\u4ea4\u4e92\u4ee5\u5728 ASP.NET Core \u4e2d\u63d0\u4f9b\u8def\u7531\u529f\u80fd\uff0c\u4f46\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u53ea\u4e86\u89e3\u4e86\u7b80\u5355\u7684\u8def\u7531\u6a21\u677f\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3\u8def\u7531\u6a21\u677f\u63d0\u4f9b\u7684\u4f17\u591a\u529f\u80fd\u4e2d\u7684\u4e00\u4e9b\u3002<\/p>\n<h2>6.3 Exploring the route template syntax<\/h2>\n<h2>6.3 \u63a2\u7d22\u8def\u7531\u6a21\u677f\u8bed\u6cd5<\/h2>\n<p>So far in this book we\u2019ve looked at simple route templates consisting of fixed values, such as \/person and \/test, as well as using a basic route parameter such as \/fruit\/{id}. In this section we explore the full range of features available in route templates, such as default values, optional segments, and constraints.<\/p>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u5728\u672c\u4e66\u4e2d\uff0c\u6211\u4eec\u5df2\u7ecf\u4e86\u89e3\u4e86\u7531\u56fa\u5b9a\u503c\u7ec4\u6210\u7684\u7b80\u5355\u8def\u7531\u6a21\u677f\uff0c\u4f8b\u5982 \/person \u548c \/test\uff0c\u4ee5\u53ca\u4f7f\u7528\u57fa\u672c\u8def\u7531\u53c2\u6570\uff0c\u4f8b\u5982\/fruit\/{id} \u4e2d\u3002\u5728\u672c\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u63a2\u8ba8\u8def\u7531\u6a21\u677f\u4e2d\u63d0\u4f9b\u7684\u5168\u90e8\u529f\u80fd\uff0c\u4f8b\u5982\u9ed8\u8ba4\u503c\u3001\u53ef\u9009\u5206\u6bb5\u548c\u7ea6\u675f\u3002<\/p>\n<h3>6.3.1 Working with parameters and literal segments<\/h3>\n<h3>6.3.1 \u4f7f\u7528\u53c2\u6570\u548c\u6587\u672c\u6bb5<\/h3>\n<p>Route templates have a rich, flexible syntax. Figure 6.5, however, shows a simple example, similar to ones you\u2019ve already seen.<\/p>\n<p>\u8def\u7531\u6a21\u677f\u5177\u6709\u4e30\u5bcc\u3001\u7075\u6d3b\u7684\u8bed\u6cd5\u3002\u7136\u800c\uff0c\u56fe 6.5 \u663e\u793a\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u4f8b\u5b50\uff0c\u7c7b\u4f3c\u4e8e\u4f60\u5df2\u7ecf\u770b\u5230\u7684\u90a3\u4e9b\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0605.png\" alt=\"\" \/><\/p>\n<p>Figure 6.5 A simple route template showing a literal segment and two required route parameters<br \/>\n\u56fe 6.5 \u4e00\u4e2a\u7b80\u5355\u7684\u8def\u7531\u6a21\u677f\uff0c\u5176\u4e2d\u663e\u793a\u4e86\u4e00\u4e2a\u6587\u672c\u6bb5\u548c\u4e24\u4e2a\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570<\/p>\n<p>The routing middleware parses a route template by splitting it into segments. A segment is typically separated by the \/ character, but it can be any valid character.<\/p>\n<p>\u8def\u7531\u4e2d\u95f4\u4ef6\u901a\u8fc7\u5c06\u8def\u7531\u6a21\u677f\u62c6\u5206\u4e3a\u6bb5\u6765\u89e3\u6790\u8def\u7531\u6a21\u677f\u3002\u6bb5\u901a\u5e38\u7531\u5b57\u7b26\u5206\u9694\uff0c\u4f46\u5b83\u53ef\u4ee5\u662f\u4efb\u4f55\u6709\u6548\u5b57\u7b26\u3002\/<\/p>\n<p><b>DEFINITION<\/b> Segments that use a character other than \/ are called complex segments. I generally recommend that you avoid them and stick to using \/ as a separator. Complex segments have some peculiarities that make them hard to use, so be sure to check the documentation at <a href=\"http:\/\/mng.bz\/D4RE\">http:\/\/mng.bz\/D4RE<\/a> before you use them.<br \/>\n\u5b9a\u4e49\uff1a \u4f7f\u7528\u5176\u4ed6\u5b57\u7b26\u7684\u7ebf\u6bb5\u79f0\u4e3a\u590d\u6742\u7ebf\u6bb5\u3002\u6211\u901a\u5e38\u5efa\u8bae\u60a8\u907f\u514d\u4f7f\u7528\u5b83\u4eec\u5e76\u575a\u6301\u7528\u4f5c\u5206\u9694\u7b26\u3002\u590d\u6742\u533a\u6bb5\u5177\u6709\u4e00\u4e9b\u7279\u6027\uff0c\u4f7f\u5176\u96be\u4ee5\u4f7f\u7528\uff0c\u56e0\u6b64\u8bf7\u52a1\u5fc5\u5728\u4f7f\u7528 <a href=\"http:\/\/mng.bz\/D4RE\">http:\/\/mng.bz\/D4RE<\/a> \u4e4b\u524d\u67e5\u770b\u6587\u6863\u3002<\/p>\n<p>Each segment is either<br \/>\n\u6bcf\u4e2a\u6bb5\u662f<\/p>\n<p>\u2022   A literal value such as product in figure 6.5<br \/>\n\u5982\u56fe 6.5 \u6240\u793a\u7684 Literal \u503cproduct<\/p>\n<p>\u2022   A route parameter such as {category} and {name} in figure 6.5<br \/>\n\u8def\u7531\u53c2\u6570\uff0c\u5982\u56fe 6.5 \u4e2d\u7684 \u548c{category}{name}<\/p>\n<p>The request URL must match literal values exactly (ignoring case). If you need to match a particular URL exactly, you can use a template consisting only of literals.<\/p>\n<p>\u8bf7\u6c42 URL \u5fc5\u987b\u4e0e\u6587\u5b57\u503c\u5b8c\u5168\u5339\u914d\uff08\u5ffd\u7565\u5927\u5c0f\u5199\uff09\u3002\u5982\u679c\u9700\u8981\u7cbe\u786e\u5339\u914d\u7279\u5b9a URL\uff0c\u53ef\u4ee5\u4f7f\u7528\u4ec5\u5305\u542b\u6587\u672c\u7684\u6a21\u677f\u3002<\/p>\n<p><b>TIP<\/b>  Literal segments in ASP.NET Core aren\u2019t case-sensitive.<br \/>\n\u63d0\u793a\uff1a ASP.NET Core \u4e2d\u7684\u6587\u672c\u6bb5\u4e0d\u533a\u5206\u5927\u5c0f\u5199\u3002<\/p>\n<p>Imagine that you have a minimal API in your application defined using<br \/>\n\u5047\u8bbe\u60a8\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6709\u4e00\u4e2a\u4f7f\u7528<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\napp.MapGet(&quot;\/About\/Contact&quot;, () =&gt; {\/* *\/})\n<\/pre>\n<p>This route template, \u201c\/About\/Contact&quot;, consists only of literal values, so it matches only the exact URL (ignoring case). None of the following URLs would match this route template:<br \/>\n\u6b64\u8def\u7531\u6a21\u677f \u201c\/About\/Contact\u201d \u4ec5\u5305\u542b\u6587\u672c\u503c\uff0c\u56e0\u6b64\u5b83\u4ec5\u5339\u914d\u786e\u5207\u7684 URL\uff08\u5ffd\u7565\u5927\u5c0f\u5199\uff09\u3002\u4ee5\u4e0b URL \u90fd\u4e0d\u5339\u914d\u6b64\u8def\u7531\u6a21\u677f\uff1a<\/p>\n<p>\u2022   \/about<br \/>\n\u2022   \/about-us\/contact<br \/>\n\u2022   \/about\/contact\/email<br \/>\n\u2022   \/about\/contact-us<\/p>\n<p>Route parameters are sections of a URL that may vary but are still a match for the template. You define them by giving them a name and placing them in braces, such as {category} or {name}. When used in this way, the parameters are required, so the request URL must have a segment that they correspond to, but the value can vary.<\/p>\n<p>\u8def\u7531\u53c2\u6570\u662f URL \u7684\u90e8\u5206\uff0c\u8fd9\u4e9b\u90e8\u5206\u53ef\u80fd\u4f1a\u6709\u6240\u4e0d\u540c\uff0c\u4f46\u4ecd\u4e0e\u6a21\u677f\u5339\u914d\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u4e3a\u5b83\u4eec\u547d\u540d\u5e76\u5c06\u5b83\u4eec\u653e\u5728\u5927\u62ec\u53f7\u4e2d\u6765\u5b9a\u4e49\u5b83\u4eec\uff0c\u4f8b\u5982{\u7c7b\u522b}\u6216 {name} \u7684\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u4f7f\u7528\u65f6\uff0c\u53c2\u6570\u662f\u5fc5\u9700\u7684\uff0c\u56e0\u6b64\u8bf7\u6c42 URL \u5fc5\u987b\u5177\u6709\u5b83\u4eec\u5bf9\u5e94\u7684\u533a\u6bb5\uff0c\u4f46\u8be5\u503c\u53ef\u80fd\u4f1a\u6709\u6240\u4e0d\u540c\u3002<\/p>\n<p>The ability to use route parameters gives you great flexibility. The simple route template &quot;\/{category}\/{name}&quot; could be used to match all the product-page URLs in an e-commerce application:<br \/>\n\u4f7f\u7528\u8def\u7531\u53c2\u6570\u7684\u80fd\u529b\u4e3a\u60a8\u63d0\u4f9b\u4e86\u6781\u5927\u7684\u7075\u6d3b\u6027\u3002\u7b80\u5355\u7684\u8def\u7531\u6a21\u677f \u201c\/{category}\/{name}\u201d \u53ef\u7528\u4e8e\u5339\u914d\u7535\u5b50\u5546\u52a1\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709\u4ea7\u54c1\u9875\u9762 URL\uff1a<\/p>\n<p>\u2022   \/bags\/rucksack-a\u2014Where category=bags and name=rucksack-a<br \/>\n\u2022   \/shoes\/black-size9\u2014Where category=shoes and name=black-size9<\/p>\n<p>But note that this template would not map the following URLs:<br \/>\n\u4f46\u8bf7\u6ce8\u610f\uff0c\u6b64\u6a21\u677f\u4e0d\u4f1a\u6620\u5c04\u4ee5\u4e0b URL\uff1a<\/p>\n<p>\u2022   \/socks\/\u2014No name parameter specified<br \/>\n\u2022   \/trousers\/mens\/formal\u2014Extra URL segment, formal, not found in route template<\/p>\n<p>When a route template defines a route parameter and the route matches a URL, the value associated with the parameter is captured and stored in a dictionary of values associated with the request. These route values typically drive other behavior in the endpoint and can be injected into the handlers (as you saw briefly in chapter 5) in a process called model binding.<\/p>\n<p>\u5f53\u8def\u7531\u6a21\u677f\u5b9a\u4e49\u8def\u7531\u53c2\u6570\u5e76\u4e14\u8def\u7531\u4e0e URL \u5339\u914d\u65f6\uff0c\u5c06\u6355\u83b7\u4e0e\u8be5\u53c2\u6570\u5173\u8054\u7684\u503c\u5e76\u5c06\u5176\u5b58\u50a8\u5728\u4e0e\u8bf7\u6c42\u5173\u8054\u7684\u503c\u5b57\u5178\u4e2d\u3002\u8fd9\u4e9b\u8def\u7531\u503c\u901a\u5e38\u9a71\u52a8\u7aef\u70b9\u4e2d\u7684\u5176\u4ed6\u884c\u4e3a\uff0c\u5e76\u4e14\u53ef\u4ee5\u5728\u79f0\u4e3a\u6a21\u578b\u7ed1\u5b9a\u7684\u8fc7\u7a0b\u4e2d\u6ce8\u5165\u5904\u7406\u7a0b\u5e8f\uff08\u5982\u60a8\u5728\u7b2c 5 \u7ae0\u4e2d\u7b80\u8981\u770b\u5230\u7684\u90a3\u6837\uff09\u3002<\/p>\n<p><b>DEFINITION<\/b> Route values are the values extracted from a URL based on a given route template. Each route parameter in a template has an associated route value, and the values are stored as a string pair in a dictionary. They can be used during model binding, as you\u2019ll see in chapter 7.<br \/>\n\u5b9a\u4e49\uff1a \u8def\u7531\u503c \u662f\u6839\u636e\u7ed9\u5b9a\u8def\u7531\u6a21\u677f\u4ece URL \u4e2d\u63d0\u53d6\u7684\u503c\u3002\u6a21\u677f\u4e2d\u7684\u6bcf\u4e2a\u8def\u7531\u53c2\u6570\u90fd\u6709\u4e00\u4e2a\u5173\u8054\u7684\u8def\u7531\u503c\uff0c\u8fd9\u4e9b\u503c\u4f5c\u4e3a\u5b57\u7b26\u4e32\u5bf9\u5b58\u50a8\u5728\u5b57\u5178\u4e2d\u3002\u5b83\u4eec\u53ef\u4ee5\u5728\u6a21\u578b\u7ed1\u5b9a\u671f\u95f4\u4f7f\u7528\uff0c\u5982\u7b2c 7 \u7ae0\u6240\u793a\u3002<\/p>\n<p>Literal segments and route parameters are the two cornerstones of ASP.NET Core route templates. With these two concepts, it\u2019s possible to build all manner of URLs for your application. In the remainder of section 6.3 we\u2019ll look at additional features that let you have optional URL segments, provide default values when a segment isn\u2019t specified, and place additional constraints on the values that are valid for a given route parameter.<\/p>\n<p>\u6587\u5b57\u6bb5\u548c\u8def\u7531\u53c2\u6570\u662f ASP.NET Core \u8def\u7531\u6a21\u677f\u7684\u4e24\u4e2a\u57fa\u77f3\u3002\u6709\u4e86\u8fd9\u4e24\u4e2a\u6982\u5ff5\uff0c\u5c31\u53ef\u4ee5\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u5404\u79cd URL\u3002\u5728\u7b2c 6.3 \u8282\u7684\u5176\u4f59\u90e8\u5206\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u5176\u4ed6\u529f\u80fd\uff0c\u8fd9\u4e9b\u529f\u80fd\u5141\u8bb8\u60a8\u4f7f\u7528\u53ef\u9009\u7684 URL \u6bb5\uff0c\u5728\u672a\u6307\u5b9a\u6bb5\u65f6\u63d0\u4f9b\u9ed8\u8ba4\u503c\uff0c\u4ee5\u53ca\u5bf9\u7ed9\u5b9a\u8def\u7531\u53c2\u6570\u6709\u6548\u7684\u503c\u8bbe\u7f6e\u5176\u4ed6\u7ea6\u675f\u3002<\/p>\n<h3>6.3.2 Using optional and default values<\/h3>\n<h3>6.3.2 \u4f7f\u7528\u53ef\u9009\u503c\u548c\u9ed8\u8ba4\u503c<\/h3>\n<p>In section 6.3.1 you saw a simple route template with a literal segment and two required routing parameters. Figure 6.6 shows a more complex route that uses several additional features.<\/p>\n<p>\u5728 6.3.1 \u8282\u4e2d\uff0c\u60a8\u770b\u5230\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u8def\u7531\u6a21\u677f\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e2a\u6587\u5b57\u6bb5\u548c\u4e24\u4e2a\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570\u3002\u56fe6.6 \u663e\u793a\u4e86\u4e00\u4e2a\u66f4\u590d\u6742\u7684\u8def\u7531\uff0c\u5b83\u4f7f\u7528\u4e86\u51e0\u4e2a\u9644\u52a0\u529f\u80fd\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0606.png\" alt=\"\" \/><\/p>\n<p>Figure 6.6 A more complex route template showing literal segments, named route parameters, optional parameters, and default values.<br \/>\n\u56fe 6.6 \u4e00\u4e2a\u66f4\u590d\u6742\u7684\u8def\u7531\u6a21\u677f\uff0c\u663e\u793a\u6587\u672c\u6bb5\u3001\u547d\u540d\u8def\u7531\u53c2\u6570\u3001\u53ef\u9009\u53c2\u6570\u548c\u9ed8\u8ba4\u503c\u3002<\/p>\n<p>The literal product segment and the required {category} parameter are the same as those in in figure 6.6. The {name} parameter looks similar, but it has a default value specified for it by =all. If the URL doesn\u2019t contain a segment corresponding to the {name} parameter, the router will use the all value instead.<\/p>\n<p>\u6587\u672c product segment \u548c\u6240\u9700\u7684 {category}\u53c2\u6570\u4e0e\u56fe 6.6 \u4e2d\u7684\u76f8\u540c\u3002\u8fd9{name} \u53c2\u6570\u770b\u8d77\u6765\u7c7b\u4f3c\uff0c\u4f46\u5b83\u7684\u9ed8\u8ba4\u503c\u7531 =all \u6307\u5b9a\u3002\u5982\u679c URL \u4e0d\u5305\u542b\u4e0e {name} \u53c2\u6570\u5bf9\u5e94\u7684\u6bb5\uff0c\u5219\u8def\u7531\u5668\u5c06\u6539\u7528 all \u503c\u3002<\/p>\n<p>The final segment of figure 6.6, {id?}, defines an optional route parameter called id. This segment of the URL is optional. If this segment is present, the router captures the value for the {id} parameter; if the segment isn\u2019t there, the router doesn\u2019t create a route value for id.<\/p>\n<p>\u56fe 6.6 \u7684\u6700\u540e\u4e00\u6bb5 {id\uff1f} \u5b9a\u4e49\u4e86\u4e00\u4e2a\u540d\u4e3a id \u7684\u53ef\u9009\u8def\u7531\u53c2\u6570\u3002URL \u7684\u6b64\u6bb5\u662f\u53ef\u9009\u7684\u3002\u5982\u679c\u5b58\u5728\u6b64\u6bb5\uff0c\u5219\u8def\u7531\u5668\u5c06\u6355\u83b7 {id} \u53c2\u6570\u7684\u503c;\u5982\u679c segment \u4e0d\u5b58\u5728\uff0c\u5219\u8def\u7531\u5668\u4e0d\u4f1a\u4e3a id \u521b\u5efa route \u503c\u3002<\/p>\n<p>You can specify any number of route parameters in your templates, and these values will be available to you for model binding. The complex route template shown in figure 6.6 allows you to match a greater variety of URLs by making {name} and {id} optional and by providing a default for {name}. Table 6.1 shows some of the URLs that this template would match and the corresponding route values that the router would set.<\/p>\n<p>\u60a8\u53ef\u4ee5\u5728\u6a21\u677f\u4e2d\u6307\u5b9a\u4efb\u610f\u6570\u91cf\u7684\u8def\u7531\u53c2\u6570\uff0c\u8fd9\u4e9b\u503c\u5c06\u53ef\u7528\u4e8e\u6a21\u578b\u7ed1\u5b9a\u3002\u56fe\u4e2d\u6240\u793a\u7684\u590d\u6742\u8def\u7531\u6a21\u677f6.6 \u5141\u8bb8\u60a8\u901a\u8fc7\u4ee5\u4e0b\u65b9\u5f0f\u5339\u914d\u66f4\u591a\u79cd\u7c7b\u7684 URL{\u59d3\u540d}\u548c {id} \u53ef\u9009\uff0c\u5e76\u901a\u8fc7\u4e3a{\u59d3\u540d}\u3002\u8868 6.1 \u663e\u793a\u4e86\u6b64\u6a21\u677f\u5c06\u5339\u914d\u7684\u4e00\u4e9b URL \u4ee5\u53ca\u8def\u7531\u5668\u5c06\u8bbe\u7f6e\u7684\u76f8\u5e94\u8def\u7531\u503c\u3002<\/p>\n<p>Table 6.1 URLs that would match the template of figure 6.7 and their corresponding route values<br \/>\n\u8868 6.1 \u4e0e\u56fe 6.7 \u4e2d\u7684\u6a21\u677f\u5339\u914d\u7684 URL \u53ca\u5176\u76f8\u5e94\u7684\u8def\u7531\u503c<\/p>\n<table>\n<thead>\n<tr>\n<th>URL<\/th>\n<th>Route values<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>\/product\/shoes\/formal\/3<\/td>\n<td>category=shoes, name=formal, id=3<\/td>\n<\/tr>\n<tr>\n<td>\/product\/shoes\/formal<\/td>\n<td>category=shoes, name=formal<\/td>\n<\/tr>\n<tr>\n<td>\/product\/shoes<\/td>\n<td>category=shoes, name=all<\/td>\n<\/tr>\n<tr>\n<td>\/product\/bags\/satchels<\/td>\n<td>category=bags, name=satchels<\/td>\n<\/tr>\n<tr>\n<td>\/product\/phones<\/td>\n<td>category=phones, name=all<\/td>\n<\/tr>\n<tr>\n<td>\/product\/computers\/laptops\/ABC-123<\/td>\n<td>category=computers, name=laptops,id=ABC-123<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>NOTE<\/b>  that there\u2019s no way to specify a value for the optional {id} parameter without also specifying the {category} and {name} parameters. You can put an optional parameter (that doesn\u2019t have a default) only at the end of a route template.<br \/>\n\u6ce8\u610f\uff1a \u65e0\u6cd5\u4e3a\u53ef\u9009\u7684{id} \u53c2\u6570\uff0c\u4f46\u672a\u6307\u5b9a {category}\u548c {name} \u53c2\u6570\u3002\u60a8\u53ef\u4ee5\u653e\u7f6e\u4e00\u4e2a\u53ef\u9009\u53c2\u6570\uff08\u6ca1\u6709\u9ed8\u8ba4\u503c\uff09\u4ec5\u5728\u8def\u7531\u6a21\u677f\u7684\u672b\u5c3e\u3002<\/p>\n<p>Using default values allows you to have multiple ways to call the same URL, which may be desirable in some cases. Given the route template in figure 6.6, the following two URLs are equivalent:<\/p>\n<p>\u4f7f\u7528\u9ed8\u8ba4\u503c\u5141\u8bb8\u60a8\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u8c03\u7528\u540c\u4e00 URL\uff0c\u8fd9\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\u53ef\u80fd\u662f\u53ef\u53d6\u7684\u3002\u7ed9\u5b9a\u56fe 6.6 \u4e2d\u7684\u8def\u7531\u6a21\u677f\uff0c\u4ee5\u4e0b\u4e24\u4e2a URL \u662f\u7b49\u6548\u7684\uff1a<\/p>\n<p>\u2022   \/product\/shoes<br \/>\n\u2022   \/product\/shoes\/all<\/p>\n<p>Both URLs will execute the same endpoint handler, with the same route values of category=shoes and name=all. Using default values allows you to use shorter, more memorable URLs in your application for common URLs but still gives you the flexibility to match a variety of routes in a single template.<\/p>\n<p>\u8fd9\u4e24\u4e2a URL \u5c06\u6267\u884c\u76f8\u540c\u7684\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\uff0c\u5177\u6709\u76f8\u540c\u7684\u8def\u7531\u503c category=shoes \u548c name=all\u3002\u4f7f\u7528\u9ed8\u8ba4\u503c\u5141\u8bb8\u60a8\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4e3a\u5e38\u89c1 URL \u4f7f\u7528\u66f4\u77ed\u3001\u66f4\u6613\u8bb0\u7684 URL\uff0c\u4f46\u60a8\u4ecd\u7136\u53ef\u4ee5\u7075\u6d3b\u5730\u5728\u5355\u4e2a\u6a21\u677f\u4e2d\u5339\u914d\u5404\u79cd\u8def\u7531\u3002<\/p>\n<h3>6.3.3 Adding additional constraints to route parameters<\/h3>\n<h3>6.3.3 \u5411\u8def\u7531\u53c2\u6570\u6dfb\u52a0\u5176\u4ed6\u7ea6\u675f<\/h3>\n<p>By defining whether a route parameter is required or optional and whether it has a default value, you can match a broad range of URLs with terse template syntax. Unfortunately, in some cases this approach ends up being a little too broad. Routing only matches URL segments to route parameters; it doesn\u2019t know anything about the data you\u2019re expecting those route parameters to contain. If you consider a template similar to the one in figure 6.6, &quot;\/{category}\/{name=all}\/{id?}&quot;, all of the following URLs would match:<\/p>\n<p>\u901a\u8fc7\u5b9a\u4e49\u8def\u7531\u53c2\u6570\u662f\u5fc5\u9700\u7684\u8fd8\u662f\u53ef\u9009\u7684\uff0c\u4ee5\u53ca\u5b83\u662f\u5426\u5177\u6709\u9ed8\u8ba4\u503c\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u7b80\u6d01\u7684\u6a21\u677f\u8bed\u6cd5\u5339\u914d\u5404\u79cd URL\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u8fd9\u79cd\u65b9\u6cd5\u6700\u7ec8\u6709\u70b9\u8fc7\u4e8e\u5bbd\u6cdb\u3002\u8def\u7531\u4ec5\u5c06 URL \u6bb5\u4e0e\u8def\u7531\u53c2\u6570\u5339\u914d;\u5b83\u4e0d\u77e5\u9053\u60a8\u671f\u671b\u8fd9\u4e9b\u8def\u7531\u53c2\u6570\u5305\u542b\u7684\u6570\u636e\u3002\u5982\u679c\u60a8\u8003\u8651\u7c7b\u4f3c\u4e8e\u56fe 6.6 \u4e2d\u7684\u6a21\u677f\uff0c\u201c\/{category}\/{name=all}\/{id\uff1f}\u201d\uff0c\u5219\u4ee5\u4e0b\u6240\u6709 URL \u90fd\u5c06\u5339\u914d\uff1a<\/p>\n<p>\u2022   \/shoes\/sneakers\/test<br \/>\n\u2022   \/shoes\/sneakers\/123<br \/>\n\u2022   \/Account\/ChangePassword<br \/>\n\u2022   \/ShoppingCart\/Checkout\/Start<br \/>\n\u2022   \/1\/2\/3<\/p>\n<p>These URLs are perfectly valid given the template\u2019s syntax, but some might cause problems for your application. These URLs have two or three segments, so the router happily assigns route values and matches the template when you might not want it to! These are the route values assigned:<\/p>\n<p>\u6839\u636e\u6a21\u677f\u7684\u8bed\u6cd5\uff0c\u8fd9\u4e9b URL \u662f\u5b8c\u5168\u6709\u6548\u7684\uff0c\u4f46\u6709\u4e9b URL \u53ef\u80fd\u4f1a\u7ed9\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5e26\u6765\u95ee\u9898\u3002\u8fd9\u4e9b URL \u6709\u4e24\u5230\u4e09\u4e2a\u6bb5\uff0c\u56e0\u6b64\u8def\u7531\u5668\u4f1a\u5f88\u9ad8\u5174\u5730\u5206\u914d\u8def\u7531\u503c\uff0c\u5e76\u5728\u60a8\u53ef\u80fd\u4e0d\u5e0c\u671b\u7684\u65f6\u5019\u5339\u914d\u6a21\u677f\uff01\u4ee5\u4e0b\u662f\u5206\u914d\u7684\u8def\u7531\u503c\uff1a<\/p>\n<p>\u2022   \/shoes\/sneakers\/test has route values category=shoes, name=sneakers, and id=test.<\/p>\n<p>\u2022   \/shoes\/sneakers\/123 has route values category=shoes, name=sneakers, and id=123.<\/p>\n<p>\u2022   \/Account\/ChangePassword has route values category=Account, and name=ChangePassword.<\/p>\n<p>\u2022   \/Cart\/Checkout\/Start has route values category=Cart, name=Checkout, and id=Start.<br \/>\n\u2022   \/1\/2\/3 has route values category=1, name=2, and id=3.<\/p>\n<p>Typically, the router passes route values to handlers through model binding, which you saw briefly in chapter 5 (and which chapter 7 discusses in detail). A minimal API endpoint defined as<\/p>\n<p>\u901a\u5e38\uff0c\u8def\u7531\u5668\u901a\u8fc7\u6a21\u578b\u7ed1\u5b9a\u5c06 route \u503c\u4f20\u9012\u7ed9\u5904\u7406\u7a0b\u5e8f\uff0c\u60a8\u5728\u7b2c 5 \u7ae0\u4e2d\u7b80\u8981\u770b\u5230\u8fc7\uff08\u7b2c 7 \u7ae0\u8be6\u7ec6\u8ba8\u8bba\u4e86\uff09\u3002\u5b9a\u4e49\u4e3a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\napp.MapGet(&quot;\/fruit\/{id}&quot;, (int id) =&gt; &quot;Hello world!&quot;);\n<\/pre>\n<p>would obtain the id argument from the id route value. If the id route parameter ends up assigned a noninteger value from the URL, you\u2019ll get an exception when it\u2019s bound to the integer id parameter.<\/p>\n<p>\u5c06\u4ece route \u503c\u4e2d\u83b7\u53d6id\u53c2\u6570\u3002\u5982\u679c route \u53c2\u6570\u6700\u7ec8\u4ece URL \u5206\u914d\u4e86\u4e00\u4e2a\u975e\u6574\u6570\u503c\uff0c\u5219\u5f53\u5b83\u7ed1\u5b9a\u5230 integer \u53c2\u6570\u65f6\uff0c\u60a8\u5c06\u6536\u5230\u5f02\u5e38\u3002<\/p>\n<p>To avoid this problem, it\u2019s possible to add more constraints to a route template that must be satisfied for a URL to be considered a match. You can define constraints in a route template for a given route parameter by using : (colon). {id:int}, for example, would add the IntRouteConstraint to the id parameter. For a given URL to be considered a match, the value assigned to the id route value must be convertible to an integer.<\/p>\n<p>\u4e3a\u907f\u514d\u6b64\u95ee\u9898\uff0c\u53ef\u4ee5\u5411\u8def\u7531\u6a21\u677f\u6dfb\u52a0\u66f4\u591a\u7ea6\u675f\uff0c\u5fc5\u987b\u6ee1\u8db3\u8fd9\u4e9b\u7ea6\u675f\u624d\u80fd\u5c06 URL \u89c6\u4e3a\u5339\u914d\u9879\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 \uff1a\uff08\u5192\u53f7\uff09\u5728\u8def\u7531\u6a21\u677f\u4e2d\u4e3a\u7ed9\u5b9a\u8def\u7531\u53c2\u6570\u5b9a\u4e49\u7ea6\u675f\u3002{id\uff1aint}\uff0c\u5219\u4f1a\u6dfb\u52a0IntRouteConstraint \u8bbe\u7f6e\u4e3a id \u53c2\u6570\u3002\u8981\u4f7f\u7ed9\u5b9a URL \u88ab\u89c6\u4e3a\u5339\u914d\u9879\uff0c\u5206\u914d\u7ed9 id \u8def\u7531\u503c\u7684\u503c\u5fc5\u987b\u53ef\u8f6c\u6362\u4e3a\u6574\u6570\u3002<\/p>\n<p>You can apply a large number of route constraints to route templates to ensure that route values are convertible to appropriate types. You can also check more advanced constraints, such as that an integer value has a particular minimum value, that a string value has a maximum length, and that a value matches a given regular expression. Table 6.2 describes some of the available constraints. You can find a more complete list online in Microsoft\u2019s documentation at <a href=\"http:\/\/mng.bz\/BmRJ\">http:\/\/mng.bz\/BmRJ<\/a>.<\/p>\n<p>\u60a8\u53ef\u4ee5\u5c06\u5927\u91cf\u8def\u7531\u7ea6\u675f\u5e94\u7528\u4e8e\u8def\u7531\u6a21\u677f\uff0c\u4ee5\u786e\u4fdd\u8def\u7531\u503c\u53ef\u8f6c\u6362\u4e3a\u9002\u5f53\u7684\u7c7b\u578b\u3002\u60a8\u8fd8\u53ef\u4ee5\u68c0\u67e5\u66f4\u9ad8\u7ea7\u7684\u7ea6\u675f\uff0c\u4f8b\u5982\u6574\u6570\u503c\u662f\u5426\u5177\u6709\u7279\u5b9a\u7684\u6700\u5c0f\u503c\u3001\u5b57\u7b26\u4e32\u503c\u662f\u5426\u5177\u6709\u6700\u5927\u957f\u5ea6\u6216\u503c\u662f\u5426\u4e0e\u7ed9\u5b9a\u7684\u6b63\u5219\u8868\u8fbe\u5f0f\u5339\u914d\u3002\u8868 6.2 \u63cf\u8ff0\u4e86\u4e00\u4e9b\u53ef\u7528\u7684\u7ea6\u675f\u3002\u60a8\u53ef\u4ee5\u627e\u5230\u66f4\u5b8c\u6574\u7684\u5728\u7ebf\u5217\u8868\uff0c\u8bf7\u53c2\u9605 Microsoft \u7684\u6587\u6863\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/BmRJ\">http:\/\/mng.bz\/BmRJ<\/a>\u3002<\/p>\n<p>Table 6.2 A few route constraints and their behavior when applied<br \/>\nConstraint<br \/>\n\u8868 6.2 \u4e00\u4e9b\u8def\u7531\u7ea6\u675f\u53ca\u5176\u5e94\u7528\u65f6\u7684\u884c\u4e3a<\/p>\n<table>\n<thead>\n<tr>\n<th>Constraint<\/th>\n<th>Example<\/th>\n<th>Description<\/th>\n<th>Match examples<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>int<\/td>\n<td>{qty:int}<\/td>\n<td>Matches any integer<\/td>\n<td>123, -123, 0<\/td>\n<\/tr>\n<tr>\n<td>Guid<\/td>\n<td>{id:guid}<\/td>\n<td>Matches any Guid<\/td>\n<td>d071b70c-a812-4b54-87d2-7769528e2814<\/td>\n<\/tr>\n<tr>\n<td>decimal<\/td>\n<td>{cost:decimal}<\/td>\n<td>Matches any decimal value<\/td>\n<td>29.99, 52,-1.01<\/td>\n<\/tr>\n<tr>\n<td>min(value)<\/td>\n<td>{age:min(18)}<\/td>\n<td>Matches integer values of 18 or greater<\/td>\n<td>18, 20<\/td>\n<\/tr>\n<tr>\n<td>length(value)<\/td>\n<td>{name:length(6)}<\/td>\n<td>Matches string values with a length of 6<\/td>\n<td>Andrew,123456<\/td>\n<\/tr>\n<tr>\n<td>optional int<\/td>\n<td>{qty:int?}<\/td>\n<td>Optionally matches any integer<\/td>\n<td>123, -123,0,null<\/td>\n<\/tr>\n<tr>\n<td>optional int max(value)<\/td>\n<td>{qty:int:max(10)?}<\/td>\n<td>Optionally matches any integer of 10 or less<\/td>\n<td>3, -123, 0,null<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>TIP<\/b>  As you can see from table 6.2, you can also combine multiple constraints by separating the constraints with colons.<br \/>\n\u63d0\u793a\uff1a \u4ece\u8868 6.2 \u4e2d\u53ef\u4ee5\u770b\u51fa\uff0c\u60a8\u8fd8\u53ef\u4ee5\u901a\u8fc7\u7528\u5192\u53f7\u5206\u9694\u7ea6\u675f\u6765\u7ec4\u5408\u591a\u4e2a\u7ea6\u675f\u3002<\/p>\n<p>Using constraints allows you to narrow down the URLs that a given route template will match. When the routing middleware matches a URL to a route template, it interrogates the constraints to check that they\u2019re all valid. If they aren\u2019t valid, the route template isn\u2019t considered a match, and the endpoint won\u2019t be executed.<\/p>\n<p>\u4f7f\u7528 constraints \u53ef\u4ee5\u7f29\u5c0f\u7ed9\u5b9a\u8def\u7531\u6a21\u677f\u5c06\u5339\u914d\u7684 URL \u7684\u8303\u56f4\u3002\u5f53\u8def\u7531\u4e2d\u95f4\u4ef6\u5c06 URL \u4e0e\u8def\u7531\u6a21\u677f\u5339\u914d\u65f6\uff0c\u5b83\u4f1a\u8be2\u95ee\u7ea6\u675f\u4ee5\u68c0\u67e5\u5b83\u4eec\u662f\u5426\u90fd\u6709\u6548\u3002\u5982\u679c\u5b83\u4eec\u65e0\u6548\uff0c\u5219\u8def\u7531\u6a21\u677f\u4e0d\u88ab\u89c6\u4e3a\u5339\u914d\u9879\uff0c\u5e76\u4e14\u4e0d\u4f1a\u6267\u884c\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p><b>WARNING<\/b> Don\u2019t use route constraints to validate general input, such as to check that an email address is valid. Doing so will result in 404 \u201cPage not found\u201d errors, which will be confusing for the user. You should also be aware that all these built-in constraints assume invariant culture, which may prove to be problematic if your application uses URLs localized for other languages.<br \/>\n\u8b66\u544a\uff1a \u4e0d\u8981\u4f7f\u7528\u8def\u7531\u7ea6\u675f\u6765\u9a8c\u8bc1\u5e38\u89c4\u8f93\u5165\uff0c\u4f8b\u5982\u68c0\u67e5\u7535\u5b50\u90ae\u4ef6\u5730\u5740\u662f\u5426\u6709\u6548\u3002\u8fd9\u6837\u505a\u5c06\u5bfc\u81f4 404 \u201cPage not found\u201d \u9519\u8bef\uff0c\u8fd9\u5c06\u4f7f\u7528\u6237\u611f\u5230\u56f0\u60d1\u3002\u60a8\u8fd8\u5e94\u8be5\u77e5\u9053\uff0c\u6240\u6709\u8fd9\u4e9b\u5185\u7f6e\u7ea6\u675f\u90fd\u5047\u5b9a\u56fa\u5b9a\u533a\u57df\u6027\uff0c\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u9488\u5bf9\u5176\u4ed6\u8bed\u8a00\u672c\u5730\u5316\u7684 URL\uff0c\u8fd9\u53ef\u80fd\u4f1a\u5e26\u6765\u95ee\u9898\u3002<\/p>\n<p>Constraints are best used sparingly, but they can be useful when you have strict requirements on the URLs used by the application, as they can allow you to work around some otherwise-tricky combinations. You can even create custom constraints, as described in the documentation at <a href=\"http:\/\/mng.bz\/d14Q\">http:\/\/mng.bz\/d14Q<\/a>.<\/p>\n<p>\u7ea6\u675f\u6700\u597d\u8c28\u614e\u4f7f\u7528\uff0c\u4f46\u5f53\u60a8\u5bf9\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528\u7684 URL \u6709\u4e25\u683c\u8981\u6c42\u65f6\uff0c\u5b83\u4eec\u53ef\u80fd\u5f88\u6709\u7528\uff0c\u56e0\u4e3a\u5b83\u4eec\u53ef\u4ee5\u8ba9\u60a8\u89e3\u51b3\u4e00\u4e9b\u5176\u4ed6\u68d8\u624b\u7684\u7ec4\u5408\u3002\u60a8\u751a\u81f3\u53ef\u4ee5\u521b\u5efa\u81ea\u5b9a\u4e49\u7ea6\u675f\uff0c\u5982 <a href=\"http:\/\/mng.bz\/d14Q\">http:\/\/mng.bz\/d14Q<\/a> \u4e2d\u7684\u6587\u6863\u4e2d\u6240\u8ff0\u3002<\/p>\n<blockquote>\n<p>Constraints and overlapping routes<br \/>\n\u7ea6\u675f\u548c\u91cd\u53e0\u8def\u7531<br \/>\nIf you have a well-designed set of URLs for your application, you\u2019ll probably find that you don\u2019t need to use route constraints. Route constraints are most useful when you have overlapping route templates.<br \/>\n\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6709\u4e00\u7ec4\u8bbe\u8ba1\u826f\u597d\u7684 URL\uff0c\u60a8\u53ef\u80fd\u4f1a\u53d1\u73b0\u4e0d\u9700\u8981\u4f7f\u7528\u8def\u7531\u7ea6\u675f\u3002\u5f53\u60a8\u6709\u91cd\u53e0\u7684\u8def\u7531\u6a21\u677f\u65f6\uff0c\u8def\u7531\u7ea6\u675f\u6700\u6709\u7528\u3002<br \/>\nSuppose that you have an endpoint with the route template &quot;\/{number}\/{name}&quot; and another with the template &quot;\/{product}\/{id}&quot;. When a request with the URL \/shoes\/123 arrives, which template is chosen? Both match, so the routing middleware panics and throws an exception\u2014not ideal.<br \/>\n\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u8def\u7531\u6a21\u677f\u4e3a \u201c\/{number}\/{name}\u201d \u7684\u7ec8\u7aef\u8282\u70b9\uff0c\u53e6\u4e00\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u6a21\u677f\u4e3a \u201c\/{product}\/{id}\u201d\u3002\u5f53 URL \u4e3a \/shoes\/123 \u7684\u8bf7\u6c42\u5230\u8fbe\u65f6\uff0c\u9009\u62e9\u54ea\u4e2a\u6a21\u677f\uff1f\u4e24\u8005\u90fd\u5339\u914d\uff0c\u56e0\u6b64\u8def\u7531\u4e2d\u95f4\u4ef6\u4f1a panic \u5e76\u5f15\u53d1\u5f02\u5e38 \u2014 \u8fd9\u5e76\u4e0d\u7406\u60f3\u3002<br \/>\nUsing constraints can fix this problem. If you update the first template to &quot;\/{number:int}\/{name}&quot;, the integer constraint means that the URL is no longer a match, and the routing middleware can choose correctly. Note, however, that the URL \/123\/shoes still matches both route templates, so you\u2019re not out of the woods.<br \/>\n\u4f7f\u7528\u7ea6\u675f\u53ef\u4ee5\u89e3\u51b3\u6b64\u95ee\u9898\u3002\u5982\u679c\u5c06\u7b2c\u4e00\u4e2a\u6a21\u677f\u66f4\u65b0\u4e3a \u201c\/{number\uff1aint}\/{name}\u201d\uff0c\u5219\u6574\u6570\u7ea6\u675f\u8868\u793a URL \u4e0d\u518d\u5339\u914d\uff0c\u8def\u7531\u4e2d\u95f4\u4ef6\u53ef\u4ee5\u6b63\u786e\u9009\u62e9\u3002\u4f46\u8bf7\u6ce8\u610f\uff0cURL \/123\/shoes \u4ecd\u7136\u4e0e\u4e24\u4e2a\u8def\u7531\u6a21\u677f\u5339\u914d\uff0c\u56e0\u6b64\u60a8\u4e0d\u4f1a\u9677\u5165\u56f0\u5883\u3002<br \/>\nGenerally, you should avoid overlapping route templates like these, as they\u2019re often confusing and more trouble than they\u2019re worth. If your route templates are well defined so that each URL maps to a single template, ASP.NET Core routing will work without any difficulties. Sticking to the built-in conventions as far as possible is the best way to stay on the happy path!<br \/>\n\u901a\u5e38\uff0c\u60a8\u5e94\u8be5\u907f\u514d\u50cf\u8fd9\u6837\u7684\u91cd\u53e0\u8def\u7531\u6a21\u677f\uff0c\u56e0\u4e3a\u5b83\u4eec\u901a\u5e38\u4f1a\u4ee4\u4eba\u56f0\u60d1\u5e76\u4e14\u6bd4\u5b83\u4eec\u7684\u4ef7\u503c\u66f4\u9ebb\u70e6\u3002\u5982\u679c\u60a8\u7684\u8def\u7531\u6a21\u677f\u5b9a\u4e49\u660e\u786e\uff0c\u4ee5\u4fbf\u6bcf\u4e2a URL \u90fd\u6620\u5c04\u5230\u5355\u4e2a\u6a21\u677f\uff0c\u5219 ASP.NET Core \u8def\u7531\u5c06\u6beb\u65e0\u56f0\u96be\u5730\u5de5\u4f5c\u3002\u5c3d\u53ef\u80fd\u575a\u6301\u5185\u5728\u7684\u7ea6\u5b9a\u4fd7\u6210\u662f\u4fdd\u6301\u5feb\u4e50\u9053\u8def\u7684\u6700\u4f73\u65b9\u5f0f\uff01<\/p>\n<\/blockquote>\n<p>We\u2019re coming to the end of our look at route templates, but before we move on, there\u2019s one more type of parameter to think about: the catch-all parameter.<\/p>\n<p>\u6211\u4eec\u5373\u5c06\u7ed3\u675f\u5bf9\u8def\u7531\u6a21\u677f\u7684\u4ecb\u7ecd\uff0c\u4f46\u5728\u6211\u4eec\u7ee7\u7eed\u4e4b\u524d\uff0c\u8fd8\u6709\u4e00\u79cd\u7c7b\u578b\u7684\u53c2\u6570\u9700\u8981\u8003\u8651\uff1acatch-all \u53c2\u6570\u3002<\/p>\n<h3>6.3.4 Matching arbitrary URLs with the catch-all parameter<\/h3>\n<h3>6.3.4 \u4f7f\u7528 catch-all \u53c2\u6570\u5339\u914d\u4efb\u610f URL<\/h3>\n<p>You\u2019ve seen how route templates take URL segments and attempt to match them to parameters or literal strings. These segments normally split around the slash character, \/, so the route parameters themselves won\u2019t contain a slash. What do you do if you need them to contain a slash or don\u2019t know how many segments you\u2019re going to have?<\/p>\n<p>\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u8def\u7531\u6a21\u677f\u5982\u4f55\u83b7\u53d6 URL \u6bb5\u5e76\u5c1d\u8bd5\u5c06\u5b83\u4eec\u4e0e\u53c2\u6570\u6216\u6587\u672c\u5b57\u7b26\u4e32\u5339\u914d\u3002\u8fd9\u4e9b\u6bb5\u901a\u5e38\u56f4\u7ed5\u659c\u6760\u5b57\u7b26 \/ \u8fdb\u884c\u5206\u5272\uff0c\u56e0\u6b64\uff0c\u8def\u7531\u53c2\u6570\u672c\u8eab\u4e0d\u4f1a\u5305\u542b\u659c\u6760\u3002\u5982\u679c\u60a8\u9700\u8981\u5b83\u4eec\u5305\u542b\u659c\u6760\u6216\u4e0d\u77e5\u9053\u60a8\u5c06\u62e5\u6709\u591a\u5c11\u4e2a\u6bb5\uff0c\u8be5\u600e\u4e48\u529e\uff1f<\/p>\n<p>Imagine that you\u2019re building a currency-converter application that shows the exchange rate from one currency to one or more other currencies. You\u2019re told that the URLs for this page should contain all the currencies as separate segments. Here are some examples:<\/p>\n<p>\u5047\u8bbe\u60a8\u6b63\u5728\u6784\u5efa\u4e00\u4e2a\u8d27\u5e01\u8f6c\u6362\u5668\u5e94\u7528\u7a0b\u5e8f\uff0c\u8be5\u5e94\u7528\u7a0b\u5e8f\u663e\u793a\u4ece\u4e00\u79cd\u8d27\u5e01\u5230\u4e00\u79cd\u6216\u591a\u79cd\u5176\u4ed6\u8d27\u5e01\u7684\u6c47\u7387\u3002\u60a8\u88ab\u544a\u77e5\u6b64\u9875\u9762\u7684 URL \u5e94\u5305\u542b\u6240\u6709\u8d27\u5e01\u4f5c\u4e3a\u5355\u72ec\u7684\u6bb5\u3002\u4ee5\u4e0b\u662f\u4e00\u4e9b\u793a\u4f8b\uff1a<\/p>\n<p>\u2022   \/USD\/convert\/GBP\u2014Show USD with exchange rate to GBP.<\/p>\n<p>\u2022   \/USD\/convert\/GBP\/EUR\u2014Show USD with exchange rates to GBP and EUR.<\/p>\n<p>\u2022   \/USD\/convert\/GBP\/EUR\/CAD\u2014Show USD with exchange rates for GBP, EUR, and CAD.<\/p>\n<p>If you want to support showing any number of currencies, as these URLs do, you need a way to capture everything after the convert segment. You could achieve this goal by using a catch-all parameter in the route template, as shown in figure 6.7.<\/p>\n<p>\u5982\u679c\u8981\u50cf\u8fd9\u4e9b URL \u4e00\u6837\u652f\u6301\u663e\u793a\u4efb\u610f\u6570\u91cf\u7684\u8d27\u5e01\uff0c\u5219\u9700\u8981\u4e00\u79cd\u65b9\u6cd5\u6765\u6355\u83b7 convert \u533a\u6bb5\u4e4b\u540e\u7684\u6240\u6709\u5185\u5bb9\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u8def\u7531\u6a21\u677f\u4e2d\u4f7f\u7528 catch-all \u53c2\u6570\u6765\u5b9e\u73b0\u6b64\u76ee\u6807\uff0c\u5982\u56fe 6.7 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0607.png\" alt=\"\" \/><\/p>\n<p>Figure 6.7 You can use catch-all parameters to match the remainder of a URL. Catch-all parameters may include the \/ character or may be an empty string.<br \/>\n\u56fe 6.7 \u60a8\u53ef\u4ee5\u4f7f\u7528 catch-all \u53c2\u6570\u6765\u5339\u914d URL \u7684\u5176\u4f59\u90e8\u5206\u3002Catch-all \u53c2\u6570\u53ef\u4ee5\u5305\u542b \/ \u5b57\u7b26\uff0c\u4e5f\u53ef\u4ee5\u662f\u7a7a\u5b57\u7b26\u4e32\u3002<\/p>\n<p>You can declare catch-all parameters by using either one or two asterisks inside the parameter definition, as in {*others} and {**others}. These parameters match the remaining unmatched portion of a URL, including any slashes or other characters that aren\u2019t part of earlier parameters. They can also match an empty string. For the USD\/convert\/GBP\/EUR URL, the value of the route value others would be the single string &quot;GBP\/EUR&quot;.<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u53c2\u6570\u5b9a\u4e49\u4e2d\u4f7f\u7528\u4e00\u4e2a\u6216\u4e24\u4e2a\u661f\u53f7\u6765\u58f0\u660e catch-all \u53c2\u6570\uff0c\u5982 \u548c \u3002\u8fd9\u4e9b\u53c2\u6570\u4e0e URL \u7684\u5269\u4f59\u4e0d\u5339\u914d\u90e8\u5206\u5339\u914d\uff0c\u5305\u62ec\u4efb\u4f55\u659c\u6760\u6216\u4e0d\u5c5e\u4e8e\u65e9\u671f\u53c2\u6570\u7684\u5176\u4ed6\u5b57\u7b26\u3002\u5b83\u4eec\u8fd8\u53ef\u4ee5\u5339\u914d\u7a7a\u5b57\u7b26\u4e32\u3002\u5bf9\u4e8e URL\uff0croute \u503c\u7684\u503c\u5c06\u662f\u5355\u4e2a\u5b57\u7b26\u4e32 \u3002{*others}{**others}USD\/convert\/GBP\/EURothers&quot;GBP\/EUR&quot;<\/p>\n<p><b>TIP<\/b>  Catch-all parameters are greedy and will capture the whole unmatched portion of a URL. Where possible, to avoid confusion, avoid defining route templates with catch-all parameters that overlap other route templates.<br \/>\n\u63d0\u793a\uff1a Catch-all \u53c2\u6570\u662f\u8d2a\u5a6a\u7684\uff0c\u5c06\u6355\u83b7 URL \u7684\u6574\u4e2a\u4e0d\u5339\u914d\u90e8\u5206\u3002\u4e3a\u907f\u514d\u6df7\u6dc6\uff0c\u8bf7\u907f\u514d\u4f7f\u7528\u4e0e\u5176\u4ed6\u8def\u7531\u6a21\u677f\u91cd\u53e0\u7684 catch-all \u53c2\u6570\u5b9a\u4e49\u8def\u7531\u6a21\u677f\u3002<\/p>\n<p>The one- and two-asterisk versions of the catch-all parameter behave identically when routing an incoming request to an endpoint. The difference occurs only when you\u2019re generating URLs (which we\u2019ll cover in the next section): the one-asterisk version URL encodes forward slashes, and the two-asterisk version doesn\u2019t. Typically, the round-trip behavior of the two-asterisk version is what you want.<\/p>\n<p>\u5728\u5c06\u4f20\u5165\u8bf7\u6c42\u8def\u7531\u5230\u7ec8\u7aef\u8282\u70b9\u65f6\uff0ccatch-all \u53c2\u6570\u7684\u4e00\u4e2a\u661f\u53f7\u548c\u4e24\u4e2a\u661f\u53f7\u7248\u672c\u7684\u884c\u4e3a\u76f8\u540c\u3002\u4ec5\u5728\u751f\u6210 URL \u65f6\uff08\u6211\u4eec\u5c06\u5728\u4e0b\u4e00\u8282\u4e2d\u4ecb\u7ecd\uff09\u624d\u4f1a\u51fa\u73b0\u5dee\u5f02\uff1a\u4e00\u4e2a\u661f\u53f7\u7248\u672c\u7684 URL \u5bf9\u6b63\u659c\u6760\u8fdb\u884c\u7f16\u7801\uff0c\u800c\u4e24\u4e2a\u661f\u53f7\u7684 URL \u5219\u4e0d\u7f16\u7801\u3002\u901a\u5e38\uff0c\u4e24\u4e2a\u661f\u53f7\u7248\u672c\u7684\u5f80\u8fd4\u884c\u4e3a\u662f\u60a8\u60f3\u8981\u7684\u3002<\/p>\n<p><b>NOTE<\/b>  For examples and a comparison between the one and two-asterisk catch-all versions, see the documentation at <a href=\"http:\/\/mng.bz\/rWyX\">http:\/\/mng.bz\/rWyX<\/a>.<br \/>\n\u6ce8\u610f\uff1a \u6709\u5173\u793a\u4f8b\u4ee5\u53ca 1 \u4e2a\u661f\u53f7\u548c 2 \u4e2a\u661f\u53f7 catch-all \u7248\u672c\u4e4b\u95f4\u7684\u6bd4\u8f83\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/rWyX\">http:\/\/mng.bz\/rWyX<\/a> \u4e2d\u7684\u6587\u6863\u3002<\/p>\n<p>You read that last paragraph correctly: mapping URLs to endpoints is only half of the responsibilities of the routing system in ASP.NET Core. It\u2019s also used to generate URLs so that you can reference your endpoints easily from other parts of your application.<\/p>\n<p>\u60a8\u6b63\u786e\u5730\u9605\u8bfb\u4e86\u6700\u540e\u4e00\u6bb5\uff1a\u5c06 URL \u6620\u5c04\u5230\u7ec8\u7aef\u8282\u70b9\u53ea\u662f ASP.NET Core \u4e2d\u8def\u7531\u7cfb\u7edf\u804c\u8d23\u7684\u4e00\u534a\u3002\u5b83\u8fd8\u7528\u4e8e\u751f\u6210 URL\uff0c\u4ee5\u4fbf\u60a8\u53ef\u4ee5\u8f7b\u677e\u5730\u4ece\u5e94\u7528\u7a0b\u5e8f\u7684\u5176\u4ed6\u90e8\u5206\u5f15\u7528\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<h2>6.4 Generating URLs from route parameters<\/h2>\n<h2>6.4 \u4ece\u8def\u7531\u53c2\u6570\u751f\u6210 URL<\/h2>\n<p>In this section we\u2019ll look at the other half of routing: generating URLs. You\u2019ll learn how to generate URLs as a string you can use in your code and how to send redirect URLs automatically as a response from your endpoints.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3\u8def\u7531\u7684\u53e6\u4e00\u534a\uff1a\u751f\u6210 URL\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5c06 URL \u751f\u6210\u4e3a\u53ef\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528\u7684\u5b57\u7b26\u4e32\uff0c\u4ee5\u53ca\u5982\u4f55\u81ea\u52a8\u53d1\u9001\u91cd\u5b9a\u5411 URL \u4f5c\u4e3a\u6765\u81ea\u7ec8\u7aef\u8282\u70b9\u7684\u54cd\u5e94\u3002<\/p>\n<p>One of the benefits and byproducts of using the routing infrastructure in ASP.NET Core is that your URLs can be somewhat fluid. You can change route templates however you like in your application\u2014by renaming \/cart to \/basket, for example\u2014and won\u2019t get any compilation errors.<\/p>\n<p>\u5728 ASP.NET Core \u4e2d\u4f7f\u7528\u8def\u7531\u57fa\u7840\u8bbe\u65bd\u7684\u597d\u5904\u548c\u526f\u4ea7\u54c1\u4e4b\u4e00\u662f\u60a8\u7684 URL \u53ef\u80fd\u6709\u4e9b\u4e0d\u7a33\u5b9a\u3002\u60a8\u53ef\u4ee5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6839\u636e\u9700\u8981\u66f4\u6539\u8def\u7531\u6a21\u677f - \u901a\u8fc7\u5c06 \/cart \u91cd\u547d\u540d\u4e3a\/basket \u7684 URL\uff0c\u5e76\u4e14\u4e0d\u4f1a\u6536\u5230\u4efb\u4f55\u7f16\u8bd1\u9519\u8bef\u3002<\/p>\n<p>Endpoints aren\u2019t isolated, of course; inevitably, you\u2019ll want to include a link to one endpoint in another. Trying to manage these links within your app manually would be a recipe for heartache, broken links, and 404 errors. If your URLs were hardcoded, you\u2019d have to remember to do a find-and-replace operation with every rename!<\/p>\n<p>\u5f53\u7136\uff0c\u7ec8\u7aef\u8282\u70b9\u4e0d\u662f\u5b64\u7acb\u7684;\u4e0d\u53ef\u907f\u514d\u5730\uff0c\u60a8\u9700\u8981\u5728\u53e6\u4e00\u4e2a\u7ec8\u7aef\u8282\u70b9\u4e2d\u5305\u542b\u6307\u5411\u4e00\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u94fe\u63a5\u3002\u5c1d\u8bd5\u5728\u4f60\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u624b\u52a8\u7ba1\u7406\u8fd9\u4e9b\u94fe\u63a5\u5c06\u5bfc\u81f4\u5fc3\u75db\u3001\u94fe\u63a5\u65ad\u5f00\u548c 404 \u9519\u8bef\u3002\u5982\u679c\u60a8\u7684 URL \u662f\u786c\u7f16\u7801\u7684\uff0c\u5219\u5fc5\u987b\u8bb0\u4f4f\u5728\u6bcf\u6b21\u91cd\u547d\u540d\u65f6\u6267\u884c\u67e5\u627e\u548c\u66ff\u6362\u4f5c\uff01<\/p>\n<p>Luckily, you can use the routing infrastructure to generate appropriate URLs dynamically at runtime instead, freeing you from the burden. Conceptually, this process is almost the exact reverse of the process of mapping a URL to an endpoint, as shown in figure 6.8. In the routing case, the routing middleware takes a URL, matches it to a route template, and splits it into route values. In the URL generation case, the generator takes in the route values and combines them with a route template to build a URL.<\/p>\n<p>\u5e78\u8fd0\u7684\u662f\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u57fa\u7840\u8bbe\u65bd\u5728\u8fd0\u884c\u65f6\u52a8\u6001\u751f\u6210\u9002\u5f53\u7684 URL\uff0c\u4ece\u800c\u51cf\u8f7b\u60a8\u7684\u8d1f\u62c5\u3002\u4ece\u6982\u5ff5\u4e0a\u8bb2\uff0c\u6b64\u8fc7\u7a0b\u51e0\u4e4e\u4e0e\u5c06 URL \u6620\u5c04\u5230\u7aef\u70b9\u7684\u8fc7\u7a0b\u5b8c\u5168\u76f8\u53cd\uff0c\u5982\u56fe 6.8 \u6240\u793a\u3002\u5728\u8def\u7531\u60c5\u51b5\u4e0b\uff0c\u8def\u7531\u4e2d\u95f4\u4ef6\u91c7\u7528 URL\uff0c\u5c06\u5176\u4e0e\u8def\u7531\u6a21\u677f\u5339\u914d\uff0c\u5e76\u5c06\u5176\u62c6\u5206\u4e3a\u8def\u7531\u503c\u3002\u5728 URL\u751f\u6210\u7684\u60c5\u51b5\u4e0b\uff0c\u751f\u6210\u5668\u4f1a\u63a5\u6536\u8def\u7531\u503c\u5e76\u5c06\u5b83\u4eec\u4e0e\u8def\u7531\u6a21\u677f\u7ec4\u5408\u5728\u4e00\u8d77\u4ee5\u6784\u5efa URL\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0608.png\" alt=\"\" \/><\/p>\n<p>Figure 6.8 A comparison between routing and URL generation. Routing takes in a URL and generates route values, but URL generation uses route values to generate a URL.<br \/>\n\u56fe 6.8 \u8def\u7531\u548c URL \u751f\u6210\u4e4b\u95f4\u7684\u6bd4\u8f83\u3002\u8def\u7531\u63a5\u6536 URL \u5e76\u751f\u6210route \u503c\uff0c\u4f46 URL \u751f\u6210\u4f7f\u7528\u8def\u7531\u503c\u6765\u751f\u6210 URL\u3002<\/p>\n<p>You can use the LinkGenerator class to generate URLs for your minimal APIs. You can use it in any part of your application, so you can use it in middleware and any other services too. LinkGenerator has various methods for generating URLs, such as GetPathByPage and GetPathByAction, which are used specifically for routing to Razor Pages and MVC actions, so we\u2019ll look at those in chapter 14. We\u2019re interested in the methods related to named routes.<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528 LinkGenerator \u7c7b\u4e3a\u60a8\u7684\u6700\u5c0f API \u751f\u6210 URL\u3002\u60a8\u53ef\u4ee5\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u4efb\u4f55\u90e8\u5206\u4f7f\u7528\u5b83\uff0c\u56e0\u6b64\u60a8\u4e5f\u53ef\u4ee5\u5728\u4e2d\u95f4\u4ef6\u548c\u4efb\u4f55\u5176\u4ed6\u670d\u52a1\u4e2d\u4f7f\u7528\u5b83\u3002LinkGenerator \u5177\u6709\u5404\u79cd\u751f\u6210 URL \u7684\u65b9\u6cd5\uff0c\u4f8b\u5982 GetPathByPage \u548c GetPathByAction\uff0c\u5b83\u4eec\u4e13\u95e8\u7528\u4e8e\u8def\u7531\u5230 Razor Pages \u548c MVC\u4f5c\uff0c\u56e0\u6b64\u6211\u4eec\u5c06\u5728\u7b2c 14 \u7ae0\u4e2d\u4ecb\u7ecd\u8fd9\u4e9b\u65b9\u6cd5\u3002\u6211\u4eec\u5bf9\u4e0e\u547d\u540d\u8def\u7531\u76f8\u5173\u7684\u65b9\u6cd5\u611f\u5174\u8da3\u3002<\/p>\n<h3>6.4.1 Generating URLs for a minimal API endpoint with LinkGenerator<\/h3>\n<h3>6.4.1 \u4f7f\u7528 LinkGenerator \u4e3a\u6700\u5c0f API \u7aef\u70b9\u751f\u6210 URL<\/h3>\n<p>You\u2019ll need to generate URLs in various places in your application, and one common location is your minimal API endpoints. The following listing shows how you could generate a link to one endpoint from another by annotating the target endpoint with a name and using the LinkGenerator class.<\/p>\n<p>\u60a8\u9700\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u4e0d\u540c\u4f4d\u7f6e\u751f\u6210 URL\uff0c\u4e00\u4e2a\u5e38\u89c1\u4f4d\u7f6e\u662f\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u901a\u8fc7\u4f7f\u7528\u540d\u79f0\u6ce8\u91ca\u76ee\u6807\u7ec8\u7aef\u8282\u70b9\u5e76\u4f7f\u7528 LinkGenerator \u7c7b\u6765\u751f\u6210\u4ece\u53e6\u4e00\u4e2a\u7ec8\u7aef\u8282\u70b9\u5230\u53e6\u4e00\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u94fe\u63a5 \u3002<\/p>\n<p>Listing 6.2 Generating a URL LinkGenerator and a named endpoint<br \/>\n\u6e05\u5355 6.2 \u751f\u6210\u4e00\u4e2a URL LinkGenerator \u548c\u4e00\u4e2a\u547d\u540d\u7684\u7aef\u70b9<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\napp.MapGet(&quot;\/product\/{name}&quot;, (string name) =&gt; $&quot;The product is {name}&quot;)        \/\/ \u2776\n    .WithName(&quot;product&quot;);                                                       \/\/ \u2777\n\napp.MapGet(&quot;\/links&quot;, (LinkGenerator links) =&gt;                                   \/\/ \u2778\n{\n    string link = links.GetPathByName(&quot;product&quot;,                                \/\/ \u2779\n        new { name = &quot;big-widget&quot; });                                           \/\/ \u2779\n    return $&quot;View the product at {link}&quot;;                                       \/\/ \u277a\n});\n<\/pre>\n<p>\u2776 The endpoint echoes the name it receives in the route template.<br \/>\n\u7aef\u70b9\u56de\u663e\u5b83\u5728\u8def\u7531\u6a21\u677f\u4e2d\u63a5\u6536\u7684\u540d\u79f0\u3002<\/p>\n<p>\u2777 Gives the endpoint a name by adding metadata to it<br \/>\n\u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u5143\u6570\u636e\u6765\u4e3a\u7ec8\u7aef\u8282\u70b9\u547d\u540d<\/p>\n<p>\u2778 References the LinkGenerator class in the endpoint handler<br \/>\n\u5728\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u5f15\u7528 LinkGenerator \u7c7b<\/p>\n<p>\u2779 Creates a link using the route name \u201cproduct\u201d and provides a value for the route parameter<br \/>\n\u4f7f\u7528\u8def\u7531\u540d\u79f0 \u201cproduct\u201d \u521b\u5efa\u94fe\u63a5\uff0c\u5e76\u4e3a\u8def\u7531\u53c2\u6570\u63d0\u4f9b\u503c<\/p>\n<p>\u277a Returns the value \u201cView the product at \/product\/big-widget\u201d<br \/>\n\u8fd4\u56de\u503c \u201cView the product at \/product\/big-widget\u201d<\/p>\n<p>The WithName() method adds metadata to your endpoints so that they can be referenced by other parts of your application. In this case, we\u2019re adding a name to the endpoint so we can refer to it later. You\u2019ll learn more about metadata in chapter 11.<\/p>\n<p>WithName\uff08\uff09 \u65b9\u6cd5\u5c06\u5143\u6570\u636e\u6dfb\u52a0\u5230\u7aef\u70b9\uff0c\u4ee5\u4fbf\u5e94\u7528\u7a0b\u5e8f\u7684\u5176\u4ed6\u90e8\u5206\u53ef\u4ee5\u5f15\u7528\u5b83\u4eec\u3002\u5728\u672c\u4f8b\u4e2d\uff0c\u6211\u4eec\u5c06\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u4e00\u4e2a\u540d\u79f0\uff0c\u4ee5\u4fbf\u7a0d\u540e\u53ef\u4ee5\u5f15\u7528\u5b83\u3002\u60a8\u5c06\u5728\u7b2c 11 \u7ae0\u4e2d\u4e86\u89e3\u6709\u5173\u5143\u6570\u636e\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p><strong>Note<\/strong> Endpoint names are case-sensitive (unlike the route templates themselves) and must be globally unique. Duplicate names cause exceptions at runtime.<br \/>\n<strong>\u6ce8\u610f\uff1a<\/strong> \u7ec8\u7aef\u8282\u70b9\u540d\u79f0\u533a\u5206\u5927\u5c0f\u5199\uff08\u4e0e\u8def\u7531\u6a21\u677f\u672c\u8eab\u4e0d\u540c\uff09\uff0c\u5e76\u4e14\u5fc5\u987b\u5168\u5c40\u552f\u4e00\u3002\u91cd\u590d\u7684\u540d\u79f0\u4f1a\u5bfc\u81f4\u8fd0\u884c\u65f6\u51fa\u73b0\u5f02\u5e38<\/p>\n<p>The LinkGenerator is a service available anywhere in ASP.NET Core. You can access it from your endpoints by including it as a parameter in the handler.<\/p>\n<p>LinkGenerator \u662f\u4e00\u9879\u5728 ASP.NET Core \u4e2d\u7684\u4efb\u4f55\u4f4d\u7f6e\u90fd\u53ef\u7528\u7684\u670d\u52a1\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06\u5176\u4f5c\u4e3a\u53c2\u6570\u5305\u542b\u5728\u5904\u7406\u7a0b\u5e8f\u4e2d\uff0c\u4ece\u7ec8\u7aef\u8282\u70b9\u8bbf\u95ee\u5b83\u3002<\/p>\n<p><b>NOTE<\/b>  You can reference the LinkGenerator in your handler because it\u2019s registered with the dependency injection container automatically. You\u2019ll learn about dependency injection in chapters 8 and 9.<br \/>\n\u6ce8\u610f\uff1a \u60a8\u53ef\u4ee5\u5728\u5904\u7406\u7a0b\u5e8f\u4e2d\u5f15\u7528 LinkGenerator\uff0c\u56e0\u4e3a\u5b83\u4f1a\u81ea\u52a8\u6ce8\u518c\u5230\u4f9d\u8d56\u9879\u6ce8\u5165\u5bb9\u5668\u4e2d\u3002\u60a8\u5c06\u5728\u7b2c 8 \u7ae0\u548c\u7b2c 9 \u7ae0\u4e2d\u4e86\u89e3\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165\u3002<\/p>\n<p>The GetPathByName() method takes the name of a route and, optionally, route data. The route data is packaged as key-value pairs into a single C# anonymous object. If you need to pass more than one route value, you can add more properties to the anonymous object. Then the helper will generate a path based on the referenced endpoint\u2019s route template.<\/p>\n<p>GetPathByName\uff08\uff09 \u65b9\u6cd5\u91c7\u7528\u8def\u7531\u7684\u540d\u79f0\uff0c\u4e5f\u53ef\u4ee5\u9009\u62e9\u83b7\u53d6\u8def\u7531\u6570\u636e\u3002\u8def\u7531\u6570\u636e\u4f5c\u4e3a\u952e\u503c\u5bf9\u6253\u5305\u5230\u5355\u4e2a C# \u533f\u540d\u5bf9\u8c61\u4e2d\u3002\u5982\u679c\u9700\u8981\u4f20\u9012\u591a\u4e2a\u8def\u7531\u503c\uff0c\u53ef\u4ee5\u6dfb\u52a0\u66f4\u591aproperties \u6dfb\u52a0\u5230\u533f\u540d\u5bf9\u8c61\u3002\u7136\u540e\uff0c\u5e2e\u52a9\u7a0b\u5e8f\u5c06\u6839\u636e\u5f15\u7528\u7684\u7aef\u70b9\u7684\u8def\u7531\u6a21\u677f\u751f\u6210\u8def\u5f84\u3002<\/p>\n<p>Listing 6.2 shows how to generate a path. But you can also generate a complete URL by using the GetUriByName() method and providing values for the host and scheme, as in this example:<\/p>\n<p>\u6e05\u5355 6.2 \u5c55\u793a\u4e86\u5982\u4f55\u751f\u6210\u8def\u5f84\u3002\u4f46\u662f\uff0c\u60a8\u4e5f\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 GetUriByName\uff08\uff09 \u65b9\u6cd5\u5e76\u4e3a host \u548c scheme \u63d0\u4f9b\u503c\u6765\u751f\u6210\u5b8c\u6574\u7684 URL\uff0c\u5982\u4e0b\u4f8b\u6240\u793a\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nlinks.GetUriByName(&quot;product&quot;, new { Name = &quot;super-fancy-widget&quot;},\n    &quot;https&quot;, new HostString(&quot;localhost&quot;));\n<\/pre>\n<p>Also, some methods available on LinkGenerator take an HttpContext. These methods are often easier to use in an endpoint handler, as they extract ambient values such as the scheme and hostname from the incoming request and reuse them for URL generation.<\/p>\n<p>\u6b64\u5916\uff0cLinkGenerator \u4e0a\u53ef\u7528\u7684\u4e00\u4e9b\u65b9\u6cd5\u91c7\u7528 HttpContext\u3002\u8fd9\u4e9b\u65b9\u6cd5\u901a\u5e38\u66f4\u5bb9\u6613\u5728\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u4f7f\u7528\uff0c\u56e0\u4e3a\u5b83\u4eec\u4ece\u4f20\u5165\u8bf7\u6c42\u4e2d\u63d0\u53d6\u73af\u5883\u503c\uff08\u5982 scheme \u548c hostname\uff09\uff0c\u5e76\u5c06\u5b83\u4eec\u91cd\u65b0\u7528\u4e8e URL \u751f\u6210\u3002<\/p>\n<p><b>WARNING<\/b> Be careful when using the GetUriByName method. It\u2019s possible to expose vulnerabilities in your app if you use unvalidated host values. For more information on host filtering and why it\u2019s important, see this post: <a href=\"http:\/\/mng.bz\/V1d5\">http:\/\/mng.bz\/V1d5<\/a>.<br \/>\n\u8b66\u544a\uff1a \u4f7f\u7528 GetUriByName \u65b9\u6cd5\u65f6\u8981\u5c0f\u5fc3\u3002\u5982\u679c\u60a8\u4f7f\u7528\u672a\u7ecf\u9a8c\u8bc1\u7684 host \u503c\uff0c\u5219\u53ef\u80fd\u4f1a\u66b4\u9732\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6f0f\u6d1e\u3002\u6709\u5173\u4e3b\u673a\u7b5b\u9009\u53ca\u5176\u91cd\u8981\u6027\u7684\u66f4\u591a\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u6b64\u535a\u6587\uff1a<a href=\"http:\/\/mng.bz\/V1d5\">http:\/\/mng.bz\/V1d5<\/a>\u3002<\/p>\n<p>In listing 6.2, as well as providing the route name, I passed in an anonymous object to GetPathByName:<br \/>\n\u5728\u6e05\u5355 6.2 \u4e2d\uff0c\u9664\u4e86\u63d0\u4f9b\u8def\u7531\u540d\u79f0\u5916\uff0c\u6211\u8fd8\u5411 GetPathByName \u4f20\u5165\u4e86\u4e00\u4e2a\u533f\u540d\u5bf9\u8c61\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstring link = links.GetPathByName(&quot;product&quot;, new { name = &quot;big-widget&quot;});\n<\/pre>\n<p>This object provides additional route values when generating the URL, in this case setting the name parameter to &quot;big-widget&quot;:<\/p>\n<p>\u6b64\u5bf9\u8c61\u5728\u751f\u6210 URL \u65f6\u63d0\u4f9b\u989d\u5916\u7684\u8def\u7531\u503c\uff0c\u5728\u672c\u4f8b\u4e2d\uff0c\u5c06 name \u53c2\u6570\u8bbe\u7f6e\u4e3a \u201cbig- widget\u201d\u3002<\/p>\n<p>If a selected route explicitly includes the defined route value in its definition, such as in the &quot;\/product\/{name}&quot; route template, the route value will be used in the URL path, resulting in \/product\/big-widget. If a route doesn\u2019t contain the route value explicitly, as in the &quot;\/product&quot; template, the route value is appended to the query string as additional data. as in \/product?name=big-widget.<\/p>\n<p>\u5982\u679c\u6240\u9009\u8def\u7531\u5728\u5176\u5b9a\u4e49\u4e2d\u663e\u5f0f\u5305\u542b\u5b9a\u4e49\u7684\u8def\u7531\u503c\uff0c\u4f8b\u5982\u5728\u201c\/product\/{name}\u201d\u8def\u7531\u6a21\u677f\u4e2d\uff0c\u5219\u8def\u7531\u503c\u5c06\u5728 URL \u8def\u5f84\u4e2d\u4f7f\u7528\uff0c\u4ece\u800c\u751f\u6210 \/product\/big-widget\u3002\u5982\u679c\u8def\u7531\u6ca1\u6709\u663e\u5f0f\u5305\u542b\u8def\u7531\u503c\uff08\u5982\u201c\/product\u201d\u6a21\u677f\u4e2d\u6240\u793a\uff09\uff0c\u5219\u8def\u7531\u503c\u5c06\u4f5c\u4e3a\u9644\u52a0\u6570\u636e\u9644\u52a0\u5230\u67e5\u8be2\u5b57\u7b26\u4e32\u4e2d\u3002\u5982 \/product\uff1fname=big-widget \u4e2d\u6240\u793a\u3002<\/p>\n<h3>6.4.2 Generating URLs with IResults<\/h3>\n<h3>6.4.2 \u4f7f\u7528 IResults \u751f\u6210 URL<\/h3>\n<p>Generating URLs that link to other endpoints is common when you\u2019re creating a REST API, for example. But you don\u2019t always need to display URLs. Sometimes, you want to redirect a user to a URL automatically. In that situation you can use Results.RedirectToRoute() to handle the URL generation instead.<\/p>\n<p>\u4f8b\u5982\uff0c\u5728\u521b\u5efa REST API \u65f6\uff0c\u751f\u6210\u94fe\u63a5\u5230\u5176\u4ed6\u7ec8\u7aef\u8282\u70b9\u7684 URL \u5f88\u5e38\u89c1\u3002\u4f46\u60a8\u5e76\u4e0d\u603b\u662f\u9700\u8981\u663e\u793a URL\u3002\u6709\u65f6\uff0c\u60a8\u5e0c\u671b\u81ea\u52a8\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 URL\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 Results.RedirectToRoute\uff08\uff09 \u6765\u5904\u7406 URL \u751f\u6210\u3002<\/p>\n<p><b>NOTE<\/b>  Redirects are more common with server-rendered applications such as Razor Pages, but they\u2019re perfectly valid for API applications too.<br \/>\n\u6ce8\u610f\uff1a \u91cd\u5b9a\u5411\u5728\u670d\u52a1\u5668\u5448\u73b0\u7684\u5e94\u7528\u7a0b\u5e8f\uff08\u5982 Razor Pages\uff09\u4e2d\u66f4\u4e3a\u5e38\u89c1\uff0c\u4f46\u5b83\u4eec\u5bf9 API \u5e94\u7528\u7a0b\u5e8f\u4e5f\u5b8c\u5168\u6709\u6548\u3002<\/p>\n<p>Listing 6.3 shows how you can return a response from an endpoint that automatically redirects a user to a different named endpoint. The RedirectToRoute() method takes the name of the endpoint and any required route parameters, and generates a URL in a similar way to LinkGenerator. The minimal API framework automatically sends the generated URL as the response, so you never see the URL in your code. Then the user\u2019s browser reads the URL from the response and automatically redirects to the new page.<\/p>\n<p>\u6e05\u5355 6.3 \u5c55\u793a\u4e86\u5982\u4f55\u4ece\u7aef\u70b9\u8fd4\u56de\u54cd\u5e94\uff0c\u8be5\u54cd\u5e94\u4f1a\u81ea\u52a8\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230\u4e0d\u540c\u7684\u547d\u540d\u7aef\u70b9\u3002RedirectToRoute\uff08\uff09 \u65b9\u6cd5\u91c7\u7528\u7aef\u70b9\u7684\u540d\u79f0\u548c\u4efb\u4f55\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570\uff0c\u5e76\u4ee5\u4e0e LinkGenerator \u7c7b\u4f3c\u7684\u65b9\u5f0f\u751f\u6210 URL\u3002\u6700\u5c0f API \u6846\u67b6\u4f1a\u81ea\u52a8\u5c06\u751f\u6210\u7684 URL \u4f5c\u4e3a\u54cd\u5e94\u53d1\u9001\uff0c\u56e0\u6b64\u60a8\u6c38\u8fdc\u4e0d\u4f1a\u5728\u4ee3\u7801\u4e2d\u770b\u5230 URL\u3002\u7136\u540e\uff0c\u7528\u6237\u7684\u6d4f\u89c8\u5668\u8bfb\u53d6 URL\u5e76\u81ea\u52a8\u91cd\u5b9a\u5411\u5230\u65b0\u9875\u9762\u3002<\/p>\n<p>Listing 6.3 Generating a redirect URL using Results.RedirectToRoute()<br \/>\n\u6e05\u5355 6.3 \u4f7f\u7528 Result \u751f\u6210\u91cd\u5b9a\u5411 URLs.RedirectToRoute()<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\napp.MapGet(&quot;\/test&quot;, () =&gt; &quot;Hello world!&quot;)\n    .WithName(&quot;hello&quot;);                        \/\/ \u2776\n\napp.MapGet(&quot;\/redirect-me&quot;,\n    () =&gt; Results.RedirectToRoute(&quot;hello&quot;))    \/\/ \u2777\n<\/pre>\n<p>\u2776 Annotates the route with the name \u201chello\u201d<br \/>\n\u4f7f\u7528\u540d\u79f0 \u201chello\u201d \u6ce8\u91ca\u8def\u7531<\/p>\n<p>\u2777 Generates a response that sends a redirect to the \u201chello\u201d endpoint<br \/>\n\u751f\u6210\u4e00\u4e2a\u54cd\u5e94\uff0c\u5c06\u91cd\u5b9a\u5411\u53d1\u9001\u5230 \u201chello\u201d \u7aef\u70b9<\/p>\n<p>By default, RedirectToRoute() generates a 302 Found response and includes the generated URL in the Location response header. You can control the status code used by setting the optional parameters preserveMethod and permanent as follows:<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cRedirectToRoute\uff08\uff09 \u4f1a\u751f\u6210 302 Found \u54cd\u5e94\uff0c\u5e76\u5c06\u751f\u6210\u7684 URL \u5305\u542b\u5728 Location \u54cd\u5e94\u6807\u5934\u4e2d\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8bbe\u7f6e\u53ef\u9009\u53c2\u6570 preserveMethod \u548c permanent \u6765\u63a7\u5236\u4f7f\u7528\u7684\u72b6\u6001\u7801\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\u2022   permanent=false, preserveMethod=false\u2014302 Found<\/p>\n<p>\u2022   permanent=true, preserveMethod=false\u2014301 Moved Permanently<\/p>\n<p>\u2022   permanent=false, preserveMethod=true\u2014307 Temporary Redirect<\/p>\n<p>\u2022   permanent=true, preserveMethod=true\u2014308 Permanent Redirect<\/p>\n<p><b>NOTE<\/b>  Each of the redirect status codes has a slightly different semantic meaning, though in practice, many sites simply use 302. Be careful with the permanent move status codes; they\u2019ll cause browsers to never call the original URL, always favoring the redirect location. For a good explanation of these codes (and the useful 303 See Other status code), see the Mozilla documentation at <a href=\"http:\/\/mng.bz\/x4GB\">http:\/\/mng.bz\/x4GB<\/a>.<br \/>\n\u6ce8\u610f: \u6bcf\u4e2a\u91cd\u5b9a\u5411\u72b6\u6001\u4ee3\u7801\u7684\u8bed\u4e49\u542b\u4e49\u7565\u6709\u4e0d\u540c\uff0c\u4f46\u5b9e\u9645\u4e0a\uff0c\u8bb8\u591a\u7ad9\u70b9\u53ea\u9700\u4f7f\u7528 302\u3002\u5c0f\u5fc3\u6c38\u4e45\u79fb\u52a8\u72b6\u6001\u4ee3\u7801;\u5b83\u4eec\u5c06\u5bfc\u81f4\u6d4f\u89c8\u5668\u4ece\u4e0d\u8c03\u7528\u539f\u59cb URL\uff0c\u59cb\u7ec8\u4f18\u5148\u4f7f\u7528\u91cd\u5b9a\u5411\u4f4d\u7f6e\u3002\u6709\u5173\u8fd9\u4e9b\u4ee3\u7801\uff08\u4ee5\u53ca\u6709\u7528\u7684 303 See Other \u72b6\u6001\u4ee3\u7801\uff09\u7684\u826f\u597d\u89e3\u91ca\uff0c\u8bf7\u53c2\u9605 Mozilla \u6587\u6863 <a href=\"http:\/\/mng.bz\/x4GB\">http:\/\/mng.bz\/x4GB<\/a>\u3002<\/p>\n<p>As well as redirecting to a specific endpoint, you can redirect to an arbitrary URL by using the Results.Redirect() method. This method works in the same way as RedirectToRoute() but takes a URL instead of a route name and can be useful for redirecting to external URLs.<\/p>\n<p>\u9664\u4e86\u91cd\u5b9a\u5411\u5230\u7279\u5b9a\u7aef\u70b9\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 Results.Redirect\uff08\uff09 \u65b9\u6cd5\u91cd\u5b9a\u5411\u5230\u4efb\u610f URL\u3002\u6b64\u65b9\u6cd5\u7684\u5de5\u4f5c\u65b9\u5f0f\u4e0e RedirectToRoute\uff08\uff09 \u76f8\u540c\uff0c\u4f46\u91c7\u7528 URL \u800c\u4e0d\u662f\u8def\u7531\u540d\u79f0\uff0c\u53ef\u7528\u4e8e\u91cd\u5b9a\u5411\u5230\u5916\u90e8 URL\u3002<\/p>\n<p>Whether you\u2019re generating URLs by using LinkGenerator or RedirectToRoute(), you need to be careful in these route generation methods. Make sure to provide the correct endpoint name and any necessary route parameters. If you get something wrong\u2014if you have a typo in your endpoint name or forget to include a required route parameter, for example\u2014the URL generated will be null. Sometimes it\u2019s worth checking the generated URL for null explicitly to make sure that there are no problems.<\/p>\n<p>\u65e0\u8bba\u60a8\u662f\u4f7f\u7528 LinkGenerator \u8fd8\u662f RedirectToRoute\uff08\uff09 \u751f\u6210 URL\uff0c\u90fd\u9700\u8981\u5c0f\u5fc3\u4f7f\u7528\u8fd9\u4e9b\u8def\u7531\u751f\u6210\u65b9\u6cd5\u3002\u786e\u4fdd\u63d0\u4f9b\u6b63\u786e\u7684\u7ec8\u7aef\u8282\u70b9\u540d\u79f0\u548c\u4efb\u4f55\u5fc5\u8981\u7684\u8def\u7531\u53c2\u6570\u3002\u5982\u679c\u51fa\u73b0\u9519\u8bef\uff08\u4f8b\u5982\uff0c\u5982\u679c\u7ec8\u7aef\u8282\u70b9\u540d\u79f0\u4e2d\u6709\u62fc\u5199\u9519\u8bef\u6216\u5fd8\u8bb0\u5305\u542b\u5fc5\u9700\u7684\u8def\u7531\u53c2\u6570\uff09\uff0c\u751f\u6210\u7684 URL \u5c06\u4e3a null\u3002\u6709\u65f6\uff0c\u503c\u5f97\u663e\u5f0f\u68c0\u67e5\u751f\u6210\u7684 URL \u662f\u5426\u4e3a null\uff0c\u4ee5\u786e\u4fdd\u6ca1\u6709\u95ee\u9898\u3002<\/p>\n<h3>6.4.3 Controlling your generated URLs with RouteOptions<\/h3>\n<h3>6.4.3 \u4f7f\u7528 RouteOptions \u63a7\u5236\u751f\u6210\u7684 URL<\/h3>\n<p>Your endpoint routes are the public surface of your APIs, so you may well have opinions on how they should look. By default, LinkGenerator does its best to generate routes the same way you define them; if you define an endpoint with the route template \/MyRoute, LinkGenerator generates the path \/MyRoute. But what if that path isn\u2019t what you want? What if you\u2019d rather have LinkGenerator produce prettier paths, such as \/myroute or \/myroute\/? In this section you\u2019ll learn how to configure URL generation both globally and on a case-by-case basis.<\/p>\n<p>\u60a8\u7684\u7ec8\u7aef\u8282\u70b9\u8def\u7531\u662f API \u7684\u516c\u5171\u8868\u9762\uff0c\u56e0\u6b64\u60a8\u53ef\u80fd\u5bf9\u5b83\u4eec\u7684\u5916\u89c2\u6709\u81ea\u5df1\u7684\u60f3\u6cd5\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cLinkGenerator \u4f1a\u5c3d\u6700\u5927\u52aa\u529b\u4ee5\u4e0e\u5b9a\u4e49\u8def\u7531\u76f8\u540c\u7684\u65b9\u5f0f\u751f\u6210\u8def\u7531;\u5982\u679c\u60a8\u5b9a\u4e49\u4e86\u4e00\u4e2a\u7aef\u70b9\u4f7f\u7528\u8def\u7531\u6a21\u677f \/MyRoute\uff0cLinkGenerator \u4f1a\u751f\u6210\u8def\u5f84 \/MyRoute\u3002\u4f46\u662f\uff0c\u5982\u679c\u8fd9\u6761\u8def\u4e0d\u662f\u60a8\u60f3\u8981\u7684\u5462\uff1f\u5982\u679c\u60a8\u5e0c\u671b LinkGenerator \u751f\u6210\u66f4\u6f02\u4eae\u7684\u8def\u5f84\uff0c\u4f8b\u5982 \/myroute \u6216 \/myroute\/\uff0c\u8be5\u600e\u4e48\u529e\uff1f\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5168\u5c40\u548c\u9010\u4e2a\u914d\u7f6e URL \u751f\u6210\u3002<\/p>\n<p><b>NOTE<\/b>  Whether to add a trailing slash to your URLs is largely a question of taste, but the choice has some implications in terms of both usability and search results. I typically choose to add trailing slashes for Razor Pages applications but not for APIs. For details, see <a href=\"http:\/\/mng.bz\/Ao1W\">http:\/\/mng.bz\/Ao1W<\/a>.<br \/>\n\u6ce8\u610f: \u662f\u5426\u5411 URL \u6dfb\u52a0\u5c3e\u90e8\u659c\u6760\u5728\u5f88\u5927\u7a0b\u5ea6\u4e0a\u662f\u4e00\u4e2a\u54c1\u5473\u95ee\u9898\uff0c\u4f46\u8fd9\u79cd\u9009\u62e9\u5728\u53ef\u7528\u6027\u548c\u641c\u7d22\u7ed3\u679c\u65b9\u9762\u90fd\u6709\u4e00\u4e9b\u5f71\u54cd\u3002\u6211\u901a\u5e38\u9009\u62e9\u4e3a Razor Pages \u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u5c3e\u90e8\u659c\u6760\uff0c\u4f46\u4e0d\u4e3a API \u6dfb\u52a0\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/Ao1W\">http:\/\/mng.bz\/Ao1W<\/a>\u3002<\/p>\n<p>When ASP.NET Core matches an incoming URL against your route templates by using routing, it uses a case-insensitive comparison, as you saw in chapter 5. So if you have a route template \/MyRoute, requests to \/myroute, \/MYROUTE, and even \/myROUTE match. But when generating URLs, LinkGenerator needs to choose a single version to use. By default, it uses the same casing that you defined in your route templates. So if you write<\/p>\n<p>\u5f53 ASP.NET Core \u4f7f\u7528\u8def\u7531\u5c06\u4f20\u5165 URL \u4e0e\u8def\u7531\u6a21\u677f\u5339\u914d\u65f6\uff0c\u5b83\u4f1a\u4f7f\u7528\u4e0d\u533a\u5206\u5927\u5c0f\u5199\u7684\u6bd4\u8f83\uff0c\u5982\u7b2c 5 \u7ae0\u6240\u793a\u3002\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u6709\u8def\u7531\u6a21\u677f \/MyRoute\uff0c\u5219\u5bf9 \/myroute\u3001\/MYROUTE \u751a\u81f3 \/myROUTE \u7684\u8bf7\u6c42\u90fd\u4f1a\u5339\u914d\u3002\u4f46\u662f\u5728\u751f\u6210 URL \u65f6\uff0cLinkGenerator \u9700\u8981\u9009\u62e9\u4e00\u4e2a\u7248\u672c\u6765\u4f7f\u7528\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5b83\u4f7f\u7528\u60a8\u5728\u8def\u7531\u6a21\u677f\u4e2d\u5b9a\u4e49\u7684\u76f8\u540c\u5927\u5c0f\u5199\u3002\u6240\u4ee5\u5982\u679c\u4f60\u5199<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\napp.MapGet(&quot;\/MyRoute&quot;, () =&gt; &quot;Hello world!&quot;).WithName(&quot;route1&quot;);\n<\/pre>\n<p>LinkGenerator.GetPathByName(&quot;route1&quot;) returns\/MyRoute.<br \/>\nLinkGenerator.GetPathByName\uff08\u201croute1\u201d\uff09 \u8fd4\u56de\/MyRoute\u3002<\/p>\n<p>Although that\u2019s a good default, you\u2019d probably prefer that all the links generated by your app be consistent. I like all my links to be lowercase, regardless of whether I accidentally failed to make my route template lowercase.<\/p>\n<p>\u5c3d\u7ba1\u8fd9\u662f\u4e00\u4e2a\u5f88\u597d\u7684\u9ed8\u8ba4\u503c\uff0c\u4f46\u4f60\u53ef\u80fd\u5e0c\u671b\u4f60\u7684\u5e94\u7528\u7a0b\u5e8f\u751f\u6210\u7684\u6240\u6709\u94fe\u63a5\u90fd\u662f\u4e00\u81f4\u7684\u3002\u6211\u559c\u6b22\u6211\u7684\u6240\u6709\u94fe\u63a5\u90fd\u662f\u5c0f\u5199\u7684\uff0c\u65e0\u8bba\u6211\u662f\u5426\u610f\u5916\u5730\u672a\u80fd\u5c06\u6211\u7684\u8def\u7531\u6a21\u677f\u8bbe\u7f6e\u4e3a\u5c0f\u5199\u3002<\/p>\n<p>You can control the route generation rules by using RouteOptions. You configure the RouteOptions for your app using the Configure<T> extension method on WebApplicationBuilder.Services, which updates the RouteOptions instance for the app using the configuration system.<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528 RouteOptions \u63a7\u5236\u8def\u7531\u751f\u6210\u89c4\u5219\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 WebApplicationBuilder.Services \u4e0a\u7684 Configure<T> \u6269\u5c55\u65b9\u6cd5\u4e3a\u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e RouteOptions\uff0c\u8be5\u65b9\u6cd5\u4f7f\u7528\u914d\u7f6e\u7cfb\u7edf\u66f4\u65b0\u5e94\u7528\u7a0b\u5e8f\u7684 RouteOptions \u5b9e\u4f8b\u3002<\/p>\n<p>Note You\u2019ll learn all about the configuration system and the Configure<T> method in chapter 10.<br \/>\n\u6ce8\u610f \u60a8\u5c06\u5728\u7b2c 10 \u7ae0\u4e2d\u4e86\u89e3\u6709\u5173\u914d\u7f6e\u7cfb\u7edf\u548cConfigure<T>\u65b9\u6cd5\u7684\u6240\u6709\u4fe1\u606f\u3002<\/p>\n<p>RouteOptions contains several configuration options, as shown in listing 6.4. These settings control whether the URLs your app generates are forced to be lowercase, whether the query string should also be lowercase, and whether a trailing slash (\/) should be appended to the final URLs. In the listing, I set the URL to be lowercased, for the trailing slash to be added, and for the query string to remain unchanged.<\/p>\n<p>RouteOptions \u5305\u542b\u51e0\u4e2a\u914d\u7f6e\u9009\u9879\uff0c\u5982\u6e05\u5355 6.4 \u6240\u793a\u3002\u8fd9\u4e9b\u8bbe\u7f6e\u63a7\u5236\u662f\u5426\u5f3a\u5236\u5e94\u7528\u7a0b\u5e8f\u751f\u6210\u7684 URL \u4e3a\u5c0f\u5199\uff0c\u67e5\u8be2\u5b57\u7b26\u4e32\u662f\u5426\u4e5f\u5e94\u4e3a\u5c0f\u5199\uff0c\u4ee5\u53ca\u662f\u5426\u5e94\u5c06\u5c3e\u90e8\u659c\u6760 \uff08\/\uff09 \u9644\u52a0\u5230\u6700\u7ec8 URL\u3002\u5728\u6e05\u5355\u4e2d\uff0c\u6211\u5c06 URL \u8bbe\u7f6e\u4e3a\u5c0f\u5199\uff0c\u4ee5\u4fbf\u6dfb\u52a0\u5c3e\u90e8\u659c\u6760\uff0c\u5e76\u4f7f\u67e5\u8be2\u5b57\u7b26\u4e32\u4fdd\u6301\u4e0d\u53d8\u3002<\/p>\n<p>Note In listing 6.4 the whole path is lowercased, including any route parameter segments such as {name}. Only the query string retains its original casing.<br \/>\n\u6ce8\u610f \u5728\u6e05\u5355 6.4 \u4e2d\uff0c\u6574\u4e2a\u8def\u5f84\u90fd\u662f\u5c0f\u5199\u7684\uff0c\u5305\u62ec\u4efb\u4f55\u8def\u7531\u53c2\u6570\u6bb5\uff0c\u6bd4\u5982 {name}\u3002\u53ea\u6709\u67e5\u8be2\u5b57\u7b26\u4e32\u4fdd\u7559\u5176\u539f\u59cb\u5927\u5c0f\u5199\u3002<\/p>\n<p>Listing 6.4 Configuring link generation using RouteOptions<br \/>\n\u6e05\u5355 6.4 \u4f7f\u7528\u8def\u7531\u9009\u9879<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.Configure&lt;RouteOptions&gt;(o =&gt;                                        \/\/ \u2776\n{                                                                                    \/\/ \u2776\n    o.LowercaseUrls = true;                                                          \/\/ \u2776\n    o.AppendTrailingSlash = true;                                                    \/\/ \u2776\n    o.LowercaseQueryStrings = false;                                                 \/\/ \u2777\n});\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/HealthCheck&quot;, () =&gt; Results.Ok()).WithName(&quot;healthcheck&quot;);\napp.MapGet(&quot;\/{name}&quot;, (string name) =&gt; name).WithName(&quot;product&quot;);\n\napp.MapGet(&quot;\/&quot;, (LinkGenerator links) =&gt;\nnew&#x5B;]\n{\n    links.GetPathByName(&quot;healthcheck&quot;),                                              \/\/ \u2778\n    links.GetPathByName(&quot;product&quot;,                                                   \/\/ \u2779\n        new { Name = &quot;Big-Widget&quot;, Q = &quot;Test&quot;})                                      \/\/ \u2779\n});\n\napp.Run();\n<\/pre>\n<p>\u2776 Configures the RouteOptions used for link generation<br \/>\n\u914d\u7f6e\u7528\u4e8e\u94fe\u63a5\u751f\u6210\u7684 RouteOptions<\/p>\n<p>\u2777 All the settings default to false.<br \/>\n\u6240\u6709\u8bbe\u7f6e\u90fd\u9ed8\u8ba4\u4e3a false\u3002<\/p>\n<p>\u2778 Returns \/healthcheck\/<br \/>\n\u8fd4\u56de \/healthcheck\/<\/p>\n<p>\u2779 Returns \/big-widget\/?Q=Test<br \/>\n\u8fd4\u56de \/big-widget\/?Q=Test<\/p>\n<p>Whatever default options you choose, you should try to use them throughout your whole app, but in some cases that may not be possible. You might have a legacy API that you need to emulate, for example, and can\u2019t use lowercase URLs. In these cases, you can override the defaults by passing an optional LinkOptions parameter to LinkGenerator methods. The values you set in LinkOptions override the default values set in RouteOptions. Generating a link for the app in listing 6.4 by using<\/p>\n<p>\u65e0\u8bba\u60a8\u9009\u62e9\u4ec0\u4e48\u9ed8\u8ba4\u9009\u9879\uff0c\u60a8\u90fd\u5e94\u8be5\u5c1d\u8bd5\u5728\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u4eec\uff0c\u4f46\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u8fd9\u53ef\u80fd\u65e0\u6cd5\u5b9e\u73b0\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u80fd\u6709\u4e00\u4e2a\u9700\u8981\u6a21\u62df\u7684\u65e7 API\uff0c\u5e76\u4e14\u4e0d\u80fd\u4f7f\u7528\u5c0f\u5199 URL\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06\u53ef\u9009\u7684 LinkOptions \u53c2\u6570\u4f20\u9012\u7ed9 LinkGenerator \u65b9\u6cd5\u6765\u8986\u76d6\u9ed8\u8ba4\u503c\u3002\u5728 LinkOptions \u4e2d\u8bbe\u7f6e\u7684\u503c\u5c06\u8986\u76d6\u5728 RouteOptions \u4e2d\u8bbe\u7f6e\u7684\u9ed8\u8ba4\u503c\u3002<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nlinks.GetPathByName(&quot;healthcheck&quot;,\noptions: new LinkOptions\n{\n    LowercaseUrls = false,\n    AppendTrailingSlash = false,\n});\n<\/pre>\n<p>would return the value \/HealthCheck. Without the LinkOptions parameter, GetPathByName would return \/healthcheck\/.<\/p>\n<p>\u5c06\u8fd4\u56de\u503c \/HealthCheck\u3002\u5982\u679c\u6ca1\u6709LinkOptions \u53c2\u6570\uff0cGetPathByName \u5c06\u8fd4\u56de<br \/>\n\/healthcheck\/ \u4e2d\u3002<\/p>\n<p>Congratulations\u2014you\u2019ve made it all the way through this detailed discussion of routing! Routing is one of those topics that people often get stuck on when they come to building an application, which can be frustrating. We\u2019ll revisit routing when we look at Razor Pages in chapter 14 and web API controllers in chapter 20, but rest assured that this chapter has covered all the tricky details!<\/p>\n<p>\u606d\u559c \u2014 \u60a8\u5df2\u7ecf\u5b8c\u6210\u4e86\u8fd9\u4e2a\u5173\u4e8e\u8def\u7531\u7684\u8be6\u7ec6\u8ba8\u8bba\uff01\u8def\u7531\u662f\u4eba\u4eec\u5728\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u65f6\u7ecf\u5e38\u9677\u5165\u56f0\u5883\u7684\u8bdd\u9898\u4e4b\u4e00\uff0c\u8fd9\u53ef\u80fd\u4f1a\u4ee4\u4eba\u6cae\u4e27\u3002\u5f53\u6211\u4eec\u5728\u7b2c 14 \u7ae0\u4e2d\u67e5\u770b Razor \u9875\u9762\uff0c\u5728\u7b2c 20 \u7ae0\u4e2d\u67e5\u770b Web API \u63a7\u5236\u5668\u65f6\uff0c\u6211\u4eec\u5c06\u91cd\u65b0\u8ba8\u8bba\u8def\u7531\uff0c\u4f46\u8bf7\u653e\u5fc3\uff0c\u672c\u7ae0\u5df2\u7ecf\u6db5\u76d6\u4e86\u6240\u6709\u68d8\u624b\u7684\u7ec6\u8282\uff01<\/p>\n<p>In chapter 7 we\u2019ll dive into model binding. You\u2019ll see how the route values generated during routing are bound to your endpoint handler parameters and, perhaps more important, how to validate the values you\u2019re provided.<\/p>\n<p>\u5728\u7b2c 7 \u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u6df1\u5165\u63a2\u8ba8\u6a21\u578b\u7ed1\u5b9a\u3002\u60a8\u5c06\u770b\u5230\u8def\u7531\u671f\u95f4\u751f\u6210\u7684\u8def\u7531\u503c\u5982\u4f55\u7ed1\u5b9a\u5230\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u53c2\u6570\uff0c\u4e5f\u8bb8\u66f4\u91cd\u8981\u7684\u662f\uff0c\u5982\u4f55\u9a8c\u8bc1\u60a8\u63d0\u4f9b\u7684\u503c\u3002<\/p>\n<h2>6.5 Summary<\/h2>\n<h2>6.5 \u603b\u7ed3<\/h2>\n<p>Routing is the process of mapping an incoming request URL to an endpoint that executes to generate a response. Routing provides flexibility to your API implementations, enabling you to map multiple URLs to a single endpoint, for example.<br \/>\n\u8def\u7531\u662f\u5c06\u4f20\u5165\u8bf7\u6c42 URL \u6620\u5c04\u5230\u7ec8\u7aef\u8282\u70b9\u7684\u8fc7\u7a0b\uff0c\u8be5\u7ec8\u7aef\u8282\u70b9\u6267\u884c\u4ee5\u751f\u6210\u54cd\u5e94\u3002\u8def\u7531\u4e3a\u60a8\u7684 API \u5b9e\u65bd\u63d0\u4f9b\u4e86\u7075\u6d3b\u6027\uff0c\u4f8b\u5982\uff0c\u4f7f\u60a8\u80fd\u591f\u5c06\u591a\u4e2a URL \u6620\u5c04\u5230\u5355\u4e2a\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>ASP.NET Core uses two pieces of middleware for routing. The EndpointRoutingMiddleware and the EndpointMiddleware. WebApplication adds both pieces of middleware to your pipeline by default, so typically, you don\u2019t add them to your application manually.<br \/>\nASP.NET Core \u4f7f\u7528\u4e24\u4e2a\u4e2d\u95f4\u4ef6\u8fdb\u884c\u8def\u7531\u3002EndpointRoutingMiddleware \u548c EndpointMiddleware.\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cWebApplication \u4f1a\u5c06\u8fd9\u4e24\u4e2a\u4e2d\u95f4\u4ef6\u6dfb\u52a0\u5230\u60a8\u7684\u7ba1\u9053\u4e2d\uff0c\u56e0\u6b64\u901a\u5e38\u4e0d\u4f1a\u624b\u52a8\u5c06\u5b83\u4eec\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u3002<\/p>\n<p>The EndpointRoutingMiddleware selects which endpoint should be executed by using routing to match the request URL. The EndpointMiddleware executes the endpoint. Having two separate middleware components means that middleware placed between them can react based on the endpoint that will execute when it reaches the end of the pipeline.<br \/>\nEndpointRoutingMiddleware \u901a\u8fc7\u4f7f\u7528\u8def\u7531\u6765\u5339\u914d\u8bf7\u6c42 URL\uff0c\u4ece\u800c\u9009\u62e9\u5e94\u8be5\u6267\u884c\u7684\u7aef\u70b9\u3002EndpointMiddleware \u6267\u884c endpoint\u3002\u62e5\u6709\u4e24\u4e2a\u5355\u72ec\u7684\u4e2d\u95f4\u4ef6\u7ec4\u4ef6\u610f\u5473\u7740\u653e\u7f6e\u5728\u5b83\u4eec\u4e4b\u95f4\u7684\u4e2d\u95f4\u4ef6\u53ef\u4ee5\u6839\u636e\u5230\u8fbe\u7ba1\u9053\u672b\u5c3e\u65f6\u5c06\u6267\u884c\u7684\u7aef\u70b9\u505a\u51fa\u53cd\u5e94\u3002<\/p>\n<p>Route templates define the structure of known URLs in your application. They\u2019re strings with placeholders for variables that can contain optional values and map to endpoint handlers. You should think about your routes carefully, as they\u2019re the public surface of your application.<br \/>\n\u8def\u7531\u6a21\u677f\u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5df2\u77e5 URL \u7684\u7ed3\u6784\u3002\u5b83\u4eec\u662f\u5e26\u6709\u53d8\u91cf\u5360\u4f4d\u7b26\u7684\u5b57\u7b26\u4e32\uff0c\u8fd9\u4e9b\u53d8\u91cf\u53ef\u4ee5\u5305\u542b\u53ef\u9009\u503c\u5e76\u6620\u5c04\u5230\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u3002\u60a8\u5e94\u8be5\u4ed4\u7ec6\u8003\u8651\u8def\u7531\uff0c\u56e0\u4e3a\u5b83\u4eec\u662f\u5e94\u7528\u7a0b\u5e8f\u7684\u516c\u5171\u8868\u9762\u3002<\/p>\n<p>Route parameters are variable values extracted from a request\u2019s URL. You can use route parameters to map multiple URLs to the same endpoint and to extract the variable value from the URL automatically.<br \/>\n\u8def\u7531\u53c2\u6570\u662f\u4ece\u8bf7\u6c42\u7684 URL \u4e2d\u63d0\u53d6\u7684\u53d8\u91cf\u503c\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u53c2\u6570\u5c06\u591a\u4e2a URL \u6620\u5c04\u5230\u540c\u4e00\u7ec8\u7aef\u8282\u70b9\uff0c\u5e76\u81ea\u52a8\u4ece URL \u4e2d\u63d0\u53d6\u53d8\u91cf\u503c\u3002<\/p>\n<p>Route parameters can be optional and can use default values when a value is missing. You should use optional and default parameters sparingly, as they can make your APIs harder to understand, but they can be useful in some cases. Optional parameters must be the last segment of a route.<br \/>\n\u8def\u7531\u53c2\u6570\u53ef\u4ee5\u662f\u53ef\u9009\u7684\uff0c\u5e76\u4e14\u5728\u7f3a\u5c11\u503c\u65f6\u53ef\u4ee5\u4f7f\u7528\u9ed8\u8ba4\u503c\u3002\u60a8\u5e94\u8be5\u8c28\u614e\u4f7f\u7528\u53ef\u9009\u53c2\u6570\u548c\u9ed8\u8ba4\u53c2\u6570\uff0c\u56e0\u4e3a\u5b83\u4eec\u4f1a\u4f7f\u60a8\u7684 API \u66f4\u96be\u7406\u89e3\uff0c\u4f46\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\u5b83\u4eec\u53ef\u80fd\u5f88\u6709\u7528\u3002\u53ef\u9009\u53c2\u6570\u5fc5\u987b\u662f\u8def\u7531\u7684\u6700\u540e\u4e00\u6bb5\u3002<\/p>\n<p>Route parameters can have constraints that restrict the possible values allowed. If a route parameter doesn\u2019t match its constraints, the route isn\u2019t considered to be a match. This approach can help you disambiguate between two similar routes, but you shouldn\u2019t use constraints for validation.<br \/>\n\u8def\u7531\u53c2\u6570\u53ef\u4ee5\u5177\u6709\u9650\u5236\u5141\u8bb8\u7684\u53ef\u80fd\u503c\u7684\u7ea6\u675f\u3002\u5982\u679c\u8def\u7531\u53c2\u6570\u4e0e\u5176\u7ea6\u675f\u6761\u4ef6\u4e0d\u5339\u914d\uff0c\u5219\u4e0d\u4f1a\u5c06\u8be5\u8def\u7531\u89c6\u4e3a\u5339\u914d\u9879\u3002\u6b64\u65b9\u6cd5\u53ef\u4ee5\u5e2e\u52a9\u60a8\u6d88\u9664\u4e24\u4e2a\u76f8\u4f3c\u8def\u7531\u4e4b\u95f4\u7684\u6b67\u4e49\uff0c\u4f46\u60a8\u4e0d\u5e94\u4f7f\u7528 constraints \u8fdb\u884c\u9a8c\u8bc1\u3002<\/p>\n<p>Use a catch-all parameter to capture the remainder of a URL into a route value. Unlike standard route parameters, catch-all parameters can include slashes (\/) in the captured values.<br \/>\n\u4f7f\u7528 catch-all \u53c2\u6570\u5c06 URL \u7684\u5176\u4f59\u90e8\u5206\u6355\u83b7\u5230\u8def\u7531\u503c\u4e2d\u3002\u4e0e\u6807\u51c6\u8def\u7531\u53c2\u6570\u4e0d\u540c\uff0ccatch-all \u53c2\u6570\u53ef\u4ee5\u5728\u6355\u83b7\u7684\u503c\u4e2d\u5305\u542b\u659c\u6760 \uff08\/\uff09\u3002<\/p>\n<p>You can use the routing infrastructure to generate URLs for your application. This approach ensures that all your links remain correct if you change your endpoint\u2019s route templates.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u8def\u7531\u57fa\u7840\u8bbe\u65bd\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u751f\u6210 URL\u3002\u6b64\u65b9\u6cd5\u53ef\u786e\u4fdd\u5728\u66f4\u6539\u7ec8\u7aef\u8282\u70b9\u7684\u8def\u7531\u6a21\u677f\u65f6\u6240\u6709\u94fe\u63a5\u90fd\u4fdd\u6301\u6b63\u786e\u3002<\/p>\n<p>The LinkGenerator can be used to generate URLs from minimal API endpoints. Provide the name of the endpoint to link to and any required route values to generate an appropriate URL.<br \/>\nLinkGenerator \u53ef\u7528\u4e8e\u4ece\u6700\u5c0f\u7684 API \u7aef\u70b9\u751f\u6210 URL\u3002\u63d0\u4f9b\u8981\u94fe\u63a5\u5230\u7684\u7aef\u70b9\u7684\u540d\u79f0\u4ee5\u53ca\u751f\u6210\u76f8\u5e94 URL \u6240\u9700\u7684\u4efb\u4f55\u8def\u7531\u503c\u3002<\/p>\n<p>You can use the RedirectToRoute method to generate URLs while also generating a redirect response. This approach is useful when you don\u2019t need to reference the URL in code.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 RedirectToRoute \u65b9\u6cd5\u751f\u6210 URL\uff0c\u540c\u65f6\u751f\u6210\u91cd\u5b9a\u5411\u54cd\u5e94\u3002\u5f53\u60a8\u4e0d\u9700\u8981\u5728\u4ee3\u7801\u4e2d\u5f15\u7528 URL \u65f6\uff0c\u6b64\u65b9\u6cd5\u975e\u5e38\u6709\u7528\u3002<\/p>\n<p>By default, URLs are generated using the same casing as the route template and any supplied route parameters. Instead, you can force lowercase URLs, lowercase query strings, and trailing slashes by customizing RouteOptions, calling <code>builder.Services.Configure&lt;RouteOptions&gt;()<\/code>.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cURL \u662f\u4f7f\u7528\u4e0e\u8def\u7531\u6a21\u677f\u76f8\u540c\u7684\u5927\u5c0f\u5199\u548c\u63d0\u4f9b\u7684\u4efb\u4f55\u8def\u7531\u53c2\u6570\u751f\u6210\u7684\u3002\u76f8\u53cd\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u81ea\u5b9a\u4e49 RouteOptions\u3001\u8c03\u7528 <code>builder.Services.Configure&lt;RouteOptions&gt;()<\/code>\u6765\u5f3a\u5236\u4f7f\u7528\u5c0f\u5199 URL\u3001\u5c0f\u5199\u67e5\u8be2\u5b57\u7b26\u4e32\u548c\u5c3e\u90e8\u659c\u6760\u3002<\/p>\n<p>You can change the settings for a single URL generation by passing a LinkOptions object to the LinkGenerator methods. These methods can be useful when you need to differ from the defaults for a single endpoint, such as when you\u2019re trying to match an existing legacy route.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06 LinkOptions \u5bf9\u8c61\u4f20\u9012\u7ed9 LinkGenerator \u65b9\u6cd5\u6765\u66f4\u6539\u5355\u4e2a URL \u751f\u6210\u7684\u8bbe\u7f6e\u3002\u5f53\u60a8\u9700\u8981\u4e0e\u5355\u4e2a\u7ec8\u7aef\u8282\u70b9\u7684\u9ed8\u8ba4\u503c\u4e0d\u540c\u65f6\uff0c\u4f8b\u5982\u5f53\u60a8\u5c1d\u8bd5\u5339\u914d\u73b0\u6709\u7684\u65e7\u8def\u7531\u65f6\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u53ef\u80fd\u5f88\u6709\u7528\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>6 Mapping URLs to endpoints using routing 6 \u4f7f\u7528\u8def\u7531\u5c06 URL \u6620\u5c04\u5230\u7aef\u70b9 This chapter covers \u672c\u7ae0\u6db5\u76d6 Mapping URLs to endpoint handlers \u5c06 URL \u6620\u5c04\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f Using constraints and default values to match URLs \u4f7f\u7528\u7ea6\u675f\u548c\u9ed8\u8ba4\u503c\u6765\u5339\u914d URL Generating URLs from route parameters \u4ece\u8def\u7531\u53c2\u6570\u751f\u6210 URL In chapter 5 you learned how to define minimal APIs, how to return responses, and [&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-580","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\/580","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=580"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/580\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=580"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=580"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=580"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}