|
|
前言
; o* f/ F/ t; u; d T, z" ^5 p/ w0 I, z0 o, N. W
用户请求接口路由,应用返回处理结果。应用中如何匹配请求的数据呢?为何能如此精确的找到对应的处理方法?今天就谈谈这个路由。路由负责匹配传入的HTTP请求,将这些请求发送到可以执行的终结点。终结点在应用中进行定义并且在应用启动的时候进行配置,也就是在中间件中进行处理。
" X4 E3 X7 @% T5 c6 b路由基础知识
8 G1 {+ G. }+ r% u9 ^% o+ U% ~* ^3 v" ^- c5 [ c) G! D/ \
1 [: }# A( R3 r c( a
在项目新建的时候都会自动生成路由相关代码。在Startup.Configure中的中间件管道注册的。主要涉及到的则是UseRouting和UseEndpoints中间件。
4 C/ I6 O* ^6 \- W$ E/ w% H8 h UseRouting向中间件添加路由匹配。此中间件还会查看应用中定义的终结点集。也就是把应用中的路由统统注册到中间件管道,方便请求的时候进行匹配。
0 r- k8 `- X* i2 f$ z% X UseEndpoints向中间件添加终结点执行。会运行相关联的委托。简单将就是路由匹配之后的处理事件运行。- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); }
复制代码 例如上面的代码就是HTPP GET 请求并且Url是/的时候需要执行的委托、如果这里的请求不是Get请求或者不是"/",那么没有路由匹配,则会返回404。同时指定匹配模式的还有MapDelete、MapMethods、MapPost、MapPut、Map等。 J! o5 _$ p6 z0 e7 x3 ]# S
终结点
) H s6 e" Z# T( h0 I6 ~1 z/ g' z2 i- ?6 R0 s
; o+ Q- s2 r5 R6 @: e* h
上面讲的MapGet或者未用到MapPost等就是用于定义终结点的。它们都包含有两个参数,一个是用于Url匹配的,另外一个就是需要执行的委托。这里在不一样的应用中都采用了不同的终结点定义方法0 w4 g& p# g$ w6 T
$ G" R2 d! }) ~; J7 W' E4 d d
- 用于 Razor Pages 的 MapRazorPages
" Y8 d+ ~( j7 z/ k - 用于控制器的 MapControllers 5 J) d% H+ D6 [: c4 L0 a2 j0 k8 S
- 用于 SignalR 的 MapHub
w m9 A% ?9 N4 \9 |/ a) X4 d - 用于 gRPC 的 MapGrpcService
* o; m+ R. ]6 Y& ~! H 那么我们如果需要使用到了授权模块将如何处理呢,终结点也有相对应的处理方式。下面就展示将授权中间件和路由一起使用,MapHealthChecks添加运行状况检查终结点。后面跟着的RequireAuthorization则是将授权策略添加到端点。- app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapHealthChecks("/healthz").RequireAuthorization(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); });
复制代码 而且我们看中间的使用顺序,UseAuthentication、UseAuthorization是穿插在UseRouting和UseEndpoints中间的,如此写法则是为了授权策略能在UseRouting中查找终结点,但是能在UseEndpoints发送到终结点执行之前应用所选择的授权策略& Q j* E0 ]7 t; }; n: P
终结点元数据
9 i; x) z. l. ]5 P7 y& q: [% w# P5 O7 X! U% [# y1 M, d0 k
# H2 Q+ f7 r: K7 S. A4 C
上面的示例展示了运行状况检查终结点附加了授权策略。添加的授权策略是额外数据,也就是终结点元数据。
7 p, O3 B9 s* L' _4 I5 t
( s) A! s9 Q7 E9 f& _8 v- 可以通过路由感知中间件来处理元数据。 ( Q! ^( F( ?+ w
- 元数据可以是任意的 .NET 类型。
0 G% f. e9 Z3 y5 a9 V$ u" j0 {3 o 上面提到元数据可以是人意的.NET类型,那么具体到底是什么呢?元数据如何使用呢?- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.Use(next => context => { var endpoint = context.GetEndpoint(); if (endpoint?.Metadata.GetMetadata()?.NeedsAudit ==true) { Console.WriteLine("开始处理事务逻辑"); Console.WriteLine($"ACCESS TO SENSITIVE DATA AT: {DateTime.UtcNow}"); } return next(context); }); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello world!"); }); // Using metadata to configure the audit policy. endpoints.MapGet("/sensitive", async context => { await context.Response.WriteAsync($"sensitive data{DateTime.UtcNow}"); }) .WithMetadata(new AuditPolicyAttribute(needsAudit: true)); }); } } public class AuditPolicyAttribute : Attribute { public AuditPolicyAttribute(bool needsAudit) { NeedsAudit = needsAudit; } public bool NeedsAudit { get; } }
复制代码 看上面的示例中,在终结点绑定"/sensitive"的时候会附加元数据WithMetadata。当访问“/”的时候会输出"Hello world!"。但是在app.Use中并不会执行输出"处理事务逻辑",因为并没有匹配的元数据。但是当执行"/sensitive"的时候就会输出Console.WriteLine("开始处理事务逻辑");。因为在终结点定义的时候添加了元数据。元数据可以是人意.NET类型。上面的元数据也是我们自定义Class。
5 z+ X+ U: ^+ r8 a& B* N- j' F# _比较终端中间件和路由% u, s- x3 f" w* y$ l) n9 _
6 O% t l7 L: }% G
5 T& g# N' }* P& n$ m" Z, ]7 X6 p 上面我们使用app.Use来检测匹配元数据,如果匹配成功我们就执行对应的操作。我们称之为终端中间件,为什么是终端中间件呢,因为这里会停止搜索执行匹配和操作、最后返回。. x, H: Y$ M' b/ K3 X& {4 E* S1 V1 P
那么相比较下终端中间件和路由有什么区别呢?. ]# t3 V2 _1 }7 } V+ b
这两种方法都允许终止处理管道:终端中间件允许在管道中的任意位置放置中间件:
) ?/ j# z, H. z$ }# F- ` . w. C* q1 A" x
- 中间件通过返回而不是调用 next 来终止管道。 8 r3 T" ~! T' y# q: P" O
- 终结点始终是终端。* m# \- T( W1 V6 }7 p# B7 N
终端中间件允许在管道中的任意位置放置中间件:
. z4 D1 H8 g" Q8 y# y - ~' d( \& Q( n2 `( g$ A) t
- 终结点在 UseEndpoints 位置执行。
9 L- Y9 w- H# p# D7 C 终端中间件允许任意代码确定中间件匹配的时间:; ~' q8 t4 k1 o4 z1 c
; ]% [( Y; e. v% W- R; P
- 自定义路由匹配代码可能比较复杂,且难以正确编写。 ; r' _$ { \# E; @3 D! W
- 路由为典型应用提供了简单的解决方案。
5 q; x) b/ F J0 j - 大多数应用不需要自定义路由匹配代码。
) g' ? N! l, n3 p8 t( J$ t- E 带有中间件的终结点接口,如 UseAuthorization 和 UseCors。8 H) [5 e) I" X& Z& x' @9 D5 `
5 {" M) c- n2 q) A! K B
- 通过 UseAuthorization 或 UseCors 使用终端中间件需要与授权系统进行手动交互
% U4 H4 y+ Y: G3 d% Z 设置传统路由
5 j! T! x8 v0 u2 b! ^
$ M1 N8 O. X6 J. Z" p$ t y- Y" t$ L. B: r8 ~$ I! Y; v
上面我们知道了通过UseRouting向中间件添加路由匹配,然后通过UseEndpoints定义终结点去执行匹配委托。那么在MVC模式中如何设置呢?我们看看传统路由的设置方法。- app.UseEndpoints(endpoints => { app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); });
复制代码 上面我们设置传统路由的时候采用的是endpoints.MapControllerRoute();,其中附带有两个参数,一个是名称default,第二个则是路由模板。我们看路由模板{controller=Home}/{action=Index}/{id?},那么在匹配Url路径的时候,例如执行路径 WeatherForecast/Index/5。那么则会匹配控制器为WeatherForecast,其中方法是Index并且参数是int类型的一个处理方法。
% }0 W+ C2 r7 e! [/ }REST Api 的属性路由
9 j" o3 {, n4 Z+ J1 G
. e1 R' h, b! y1 ]/ r0 i4 U, [- C, V% X' W6 b2 R3 U
上面讲的是传统路由设置,那么对于Api项目的路由设置是如何的呢?REST Api 应使用属性路由将应用功能建模为一组资源。我们看下示例代码- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
复制代码 在上面的代码中使用MapControllers调用。映射属性路由。我们看在使用的时候属性路由的使用方式。
; v ]' g! O+ Q8 i- u! zRoute[]
; }3 u6 w+ Q% m2 n' Z- A: ~1 _ 下面的示例中我们采用的是Route[]的方式,它既可单独作用域控制器也可单独作用域action。也可同时使用。- [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [Route("Index")] public string Index(int? id) { return "Test"; } }
复制代码- [ApiController] [Route("[controller]/[action]")] public class WeatherForecastController : ControllerBase { public string Index(int? id) { return "Test"; } }
复制代码- [ApiController] public class WeatherForecastController : ControllerBase { [Route("[controller]/Index")] public string Index(int? id) { return "Test"; } }
复制代码 Http[Verb]
E" j: j$ `6 C7 P. _+ y* ~9 l& K 采用Http[Verb]的方式那就仅仅能作用在action上了。比如下面的就直接在Index上方写[HttpGet- ("[controller]/Index")],其他就是HttpPost、HttpDelete等等操作 [ApiController] public class WeatherForecastController : ControllerBase { [HttpGet("[controller]/Index")] public string Index(int? id) { return "Test"; } }
复制代码 Route[]和Http[Verb]混合使用) [4 J: d1 l, ~3 g* _
( Y$ Z9 T- C+ [ a) ? h/ o; a( w
有时在实际运用中也可以采取两种方式混合使用的,例如下面的示例在控制器采用Route[],在action采用Http[Verb]。因为一般定义Api的时候我们不仅要标注action名称,我们还需要知道action的请求方式。- [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet("Index")] public string Index(int? id) { return "Test"; } }
复制代码 总结# A/ {* G8 U8 X) J0 z
( o$ u! W) D3 l& a& [# h到此这篇关于.Net Core路由处理的文章就介绍到这了,更多相关.Net Core路由处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!+ Z: _% Z+ n5 F0 o4 p
/ K$ ?: }; L/ q( {来源:http://www.jb51.net/article/209414.htm4 A. F) h7 i# I1 l
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
|