Endpoint 路由
在 ASP.NET Core 2.2 中, 新增了一种路由, 叫做 Endpoint(终结点) 路由. 本文将以往的路由系统称为传统路由.
本文通过源码的方式介绍传统路由和 Endpoint 路由部分核心功能和实现方法, 具体功能上的差异见官方文档.
在升级到 ASP.NET Core 2.2 后, 会自动启用 Endpoint 路由. 如果要恢复以往的实现逻辑, 需要加入以下代码:
- services.AddMvc(options => options.EnableEndpointRouting = false)
- .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
本文分析的源代码基于 ASP.NET Core 2.2.3 版本的源代码 https://github.com/aspnet/AspNetCore/tree/v2.2.3 .
Endpoint 作用
Endpoint 路由与传统路由的区别在于, 传统路由 Url 与 Action 对应关系的处理是在 UseMvc 中做的. 我们无法根据 Url 获取对应的 Action 然后进行处理.
Endpoint 就是将 Url 与 Action 的映射关系从 Mvc 中拆离, 作为独立使用的中间件.
由此带来的好处是我们可以在其他的中间件中使用 Controller 和 Action 上的一些信息, 例如 Attruibute.
框架也提供了 LinkGenerator 类来直接根据 Endpoint 生成链接, 不再需要 HttpContext 的信息.
另外也提升了一些 RPS(Requests per Second).
不过目前 Endpoint 依然是在 UseMvc 中调用, 更多开放的使用方式会在 ASP.NET Core 3.0 中实现.
启用 Endpoint 路由
源代码见 GitHub. 也可以获取源代码到本地看.
在 MvcApplicationBuilderExtensions.cs 文件 72 行的 UseMvc 方法中我们可以看到以下代码:
- var options = App.ApplicationServices.GetRequiredService<IOptions<MvcOptions>>();
- if (options.Value.EnableEndpointRouting)
- {
- ...
- }
- else
- {
- ...
- }
if 之中是 Endpoint 路由的逻辑, else 是传统路由的逻辑.
而 MvcOptions 的构造方法如下所示, EnableEndpointRouting 是通过 CompatibilitySwitch 来控制默认值的, 这就是 CompatibilityVersion.Version_2_2 启用 Endpoint 路由的原因.
- public MvcOptions()
- {
- // ...
- _enableEndpointRouting = new CompatibilitySwitch<bool>(nameof(EnableEndpointRouting));
- // ...
- }
Endpoint 路由实现原理
在 MvcApplicationBuilderExtensions.cs 文件的 92-123 行的代码是将所有的 Controller 中的 Action 转换成 Endpoint.
在 129 行的 UseEndpointRouting 中, 添加了一个 EndpointRoutingMiddleware 的中间件, 这个中间件就是从所有的 Endpoint 中找到当前路由对应的 Endpoint, 然后放到 Feature 集合中.
在 132 行的 UseEndpoint 中, 添加了一个 EndpointMiddleware 中间件, 这个中间件是将 EndpointRoutingMiddleware 中找到的 Endpoint 取出, 根据其中的 MetaData 信息, 找到对应的 Controller 和 Action, 并调用.
在 UseMvc 方法里, UseEndpointRouting 和 UseEndpoint 是连续的两个中间件, 而 UseEndpoint 是请求的结束, 这意味着我们自定义的中间件无法取得 Endpoint 信息.
但是通过手动调用 UseEndpoint, 我们还是可以拿到 Endpoint 路由信息的.
使用示例
下面展示一个使用示例.
定义一个 LogAttribute 类, 并包含一个 Message 属性, 在 Action 上声明使用.
定义一个 EndpointTestMiddleware 中间件, 输出 LogAttribute 的 Message 属性.
手动调用 UseEndpointRouting, 然后调用我们定义的 EndpointTestMiddleware 中间件.
- // Startup.cs
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- App.UseEndpointRouting();
- App.UseMiddleware<EndpointTestMiddleware>();
- App.UseMvc(routes =>
- {
- routes.MapRoute(
- name: "default",
- template: "{controller=Home}/{action=Index}/{id?}");
- });
- }
- // EndpointTestMiddleware.cs
- public class EndpointTestMiddleware
- {
- private RequestDelegate _next;
- public EndpointTestMiddleware(RequestDelegate next)
- {
- _next = next;
- }
- public async Task Invoke(HttpContext httpContext)
- {
- var endpoint = httpContext.Features.Get<IEndpointFeature>()?.Endpoint;
- if (endpoint == null)
- {
- await _next(httpContext);
- return;
- }
- var attruibutes = endpoint.Metadata.OfType<LogAttribute>();
- foreach (var attribute in attruibutes)
- {
- Debug.WriteLine("------------------------------------------------------------------------");
- Debug.WriteLine(attribute.Message);
- Debug.WriteLine("------------------------------------------------------------------------");
- }
- await _next(httpContext);
- }
- }
- // LogAttribute.cs
- [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- public sealed class LogAttribute : Attribute
- {
- public LogAttribute(string message)
- {
- Message = message;
- }
- public string Message { get; set; }
- }
- // HomeController.cs
- public class HomeController : Controller
- {
- [Log("Index")]
- public IActionResult Index()
- {
- return View();
- }
- [Log("Privacy")]
- public IActionResult Privacy()
- {
- return View();
- }
- }
这样的话, 我们可以在我们自己的中间件中拿到 Endpoint 信息, 然后找到 Controller 上的 LogAttribute, 然后输出 Message.
总结
Endpoint 是 ASP.NET Core 2.2 中一种新的路由机制, 它解决了传统路由难以扩展的问题, 解决了传统路由与 MVC 过于耦合的问题, 并提升了一定的 RPS.
本文介绍了 Endpoint 路由, 简单分析了 Endpoint 的实现原理, 并给出了一个使用的示例.
参考链接:
[]
[]
[]
来源: http://www.bubuko.com/infodetail-3003111.html