1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > ASP.NET Core 基础(九)——路由Routing

ASP.NET Core 基础(九)——路由Routing

时间:2019-03-17 07:03:00

相关推荐

ASP.NET Core 基础(九)——路由Routing

此文是在官方文档的基础上做的个人笔记,一些简单的内容就没用再列出来了,参考官方文档:/zh-cn/aspnet/core/fundamentals/routing?view=aspnetcore-5.0

定义:路由是负责匹配传入的http请求,然后进行发送到应用的可执行终结点(代码处理单元)。

本文只介绍较低级别的路由信息,有关MVC中的路由和Razor中的路由参考:

/zh-cn/aspnet/core/mvc/controllers/routing?view=aspnetcore-5.0

/zh-cn/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-5.0

1. 路由基础知识

路由是一对由UseRoutingUseEndPoints注册的中间件:

UseRouting向管道添加路由配置。此中间件会查看应用中定义的终结点集合,并根据请求选择最佳配置。UseEndpoints向管道添加终结点执行。它会执行与所选终结点关联的委托。

app.UseRouting();//...app.UseEndpoints(endpoints =>{endpoints.MapGet("/", async context =>{await context.Response.WriteAsync("Hello World!");});});

上述代码MapGet表示当是get请求时且请求的是根URL时就执行委托。如果请求的方法不是GET或者请求的不是根URL则无路由配置就返回404.

1.1 终结点

MapGet方法用于定义终结点。终结点可以通过匹配URL和HTTP方法来选择运行委托执行请求。类似MapGet的方法还有:

MapMapDeleteMapGetMapPutMapPostMapHealthChecksMapRazorPages:针对的RazorPageMapController:针对控制器MapHub<THub>:针对SingalRMapGrpcService<TService>:针对gRpc

1.2 路由模板

下面代码的/hello/{name:alpha}就是一个路由模板,这个模板匹配类似/hello/jim格式的路由。其中alpha叫路由约束,表示name属性应为字母,所以不会匹配/hello/123.

endpoints.MapGet("/hello/{name:alpha}",async c=>{var name=c.Request.RouteValues["name"];})

常见路由模板介绍:

1.3路由约束

路由约束不能等同于输入验证,因为不符合约束的路由直接返回404.

1.3.1 自定义路由约束

很少情况下需要这么做,当模型绑定也不能实现你的要求时,可以实现IRouteConstraint接口来创建自定义路由约束

1.4 路由参数转换

使用场景:/subscription-management/get-all这种的url可以匹配到SubScriptionManagementControll.GetAll方法上。

首先,定义路由

routes.MapControllerRoute(name: "default",template: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

然后写一个类实现IOutboundParameterTransformer接口:

public class SlugifyParameterTransformer : IOutboundParameterTransformer{public string TransformOutbound(object value){if (value == null) {return null; }return Regex.Replace(value.ToString(), "([a-z])([A-Z])","$1-$2",RegexOptions.CultureInvariant,TimeSpan.FromMilliseconds(100)).ToLowerInvariant();}}

最后,在startup里进行配置:

public void ConfigureServices(IServiceCollection services){services.AddControllers();services.AddRouting(options =>{options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);});}

当使用Url.Action("GetAll","SubscriptionMangement")生成路径时,也会生成/subscription-management/get-all

1.5 其它中间件与Routing、EndPoint的关系

//因为还没有调用app.UseRouting,所以这里始终为nullapp.Use(next => context =>{Console.WriteLine($"1. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");return next(context);});app.UseRouting();//如果你请求的url匹配到了某个EndPoint,则这里不会返回nullapp.Use(next => context =>{var point=context.GetEndpoint();//可以获取终结点后面跟的元数据进行处理var cls=point?.Metadata.GetMetadata<Class>();Console.WriteLine($"2. Endpoint: {point?.DisplayName ?? "(null)"}");return next(context);});app.UseEndpoints(endpoints =>{// 匹配根Url,如果匹配到了,则整个管道到此结束,不会执行后续代码endpoints.MapGet("/", context =>{Console.WriteLine($"3. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");return pletedTask;}).WithDisplayName("Hello").WithMetadata(new Class());//带上元数据Class对象});//如果你请求的url没有匹配到任何EndPoint,才会执行到这里//所以这里要么不输出,要么输出nullapp.Use(next => context =>{Console.WriteLine($"4. Endpoint: {context.GetEndpoint()?.DisplayName ?? "(null)"}");return next(context);});

中间件可以在UseRouting前执行,以修改路由操作的数据,如:UseRewriter,UseHttpMethodOverride,UsePathBase.中间件可以在UseRoutingUseEndPoints之间运行,以便在执行终结点前处理路由元数据:如:UseAuthorizationUseCors

1.6 路由与主机(Host)的匹配

使用场景:请求过来的某个url,请求头里的Host字段应符合某个域名规则。如:

请求的根路径的主机只能是adventure-。请求健康检查的主机的端口号只能是8080.

public void Configure(IApplicationBuilder app){app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapGet("/", context => context.Response.WriteAsync("Hi Contoso!")).RequireHost("");endpoints.MapGet("/", context => context.Response.WriteAsync("AdventureWorks!")).RequireHost("adventure-");endpoints.MapHealthChecks("/healthz").RequireHost("*:8080");});}

请求到某个controller上的主机只能是adventure-。但是请求到Privacy这个action上的主机是个例外,只能是:8080

[Host("", "adventure-")]public class ProductController : Controller{public IActionResult Index(){return ControllerContext.MyDisplayRouteInfo();}[Host(":8080")]public IActionResult Privacy(){return ControllerContext.MyDisplayRouteInfo();}}

1.7路由的性能

当应用出现性能问题的时候,如果开发人员排除了代码逻辑问题,一般都会认为是路由问题。但最常见的根本原因是性能不佳的自定义中间件。以下演示如何测试一个中间件执行耗时:

public void Configure(IApplicationBuilder app, ILogger<Startup> logger){app.Use(next => async context =>{var sw = Stopwatch.StartNew();await next(context);sw.Stop();logger.LogInformation("Time 1: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);});app.UseRouting();app.Use(next => async context =>{var sw = Stopwatch.StartNew();await next(context);sw.Stop();logger.LogInformation("Time 2: {ElapsedMilliseconds}ms", sw.ElapsedMilliseconds);});}

用Time1减去Time2就测出了UseRouting中间件的性能。上述代码也可以优化如下:

public sealed class MyStopwatch : IDisposable{ILogger<Startup> _logger;string _message;Stopwatch _sw;public MyStopwatch(ILogger<Startup> logger, string message){_logger = logger;_message = message;_sw = Stopwatch.StartNew();}private bool disposed = false;public void Dispose(){if (!disposed){_logger.LogInformation("{Message }: {ElapsedMilliseconds}ms",_message, _sw.ElapsedMilliseconds);disposed = true;}}}public void Configure(IApplicationBuilder app, ILogger<Startup> logger){int count = 0;app.Use(next => async context =>{using (new MyStopwatch(logger, $"Time {++count}")){await next(context);}});app.UseRouting();app.Use(next => async context =>{using (new MyStopwatch(logger, $"Time {++count}")){await next(context);}});}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。