.Net Core 路由處理

小世界的野孩子發表於2021-04-09
  使用者請求介面路由,應用返回處理結果。應用中如何匹配請求的資料呢?為何能如此精確的找到對應的處理方法?今天就談談這個路由。路由負責匹配傳入的HTTP請求,將這些請求傳送到可以執行的終結點。終結點在應用中進行定義並且在應用啟動的時候進行配置,也就是在中介軟體中進行處理。

路由基礎知識

  在專案新建的時候都會自動生成路由相關程式碼。在Startup.Configure中的中介軟體管道註冊的。主要涉及到的則是UseRoutingUseEndpoints中介軟體。
    UseRouting向中介軟體新增路由匹配。此中介軟體還會檢視應用中定義的終結點集。也就是把應用中的路由統統註冊到中介軟體管道,方便請求的時候進行匹配。
    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等。

終結點

  上面講的MapGet或者未用到MapPost等就是用於定義終結點的。它們都包含有兩個引數,一個是用於Url匹配的,另外一個就是需要執行的委託。這裡在不一樣的應用中都採用了不同的終結點定義方法
    • 用於 Razor Pages 的 MapRazorPages
    • 用於控制器的 MapControllers
    • 用於 SignalR 的 MapHub
    • 用於 gRPC 的 MapGrpcService
  那麼我們如果需要使用到了授權模組將如何處理呢,終結點也有相對應的處理方式。下面就展示將授權中介軟體和路由一起使用,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是穿插在UseRoutingUseEndpoints中間的,如此寫法則是為了授權策略能在UseRouting中查詢終結點,但是能在UseEndpoints傳送到終結點執行之前應用所選擇的授權策略

終結點後設資料

  上面的示例展示了執行狀況檢查終結點附加了授權策略。新增的授權策略是額外資料,也就是終結點後設資料。
    • 可以通過路由感知中介軟體來處理後設資料。
    • 後設資料可以是任意的 .NET 型別。
  上面提到後設資料可以是人意的.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<AuditPolicyAttribute>()?.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

比較終端中介軟體和路由

  上面我們使用app.Use來檢測匹配後設資料,如果匹配成功我們就執行對應的操作。我們稱之為終端中介軟體,為什麼是終端中介軟體呢,因為這裡會停止搜尋執行匹配和操作、最後返回。
  那麼相比較下終端中介軟體和路由有什麼區別呢?
    • 這兩種方法都允許終止處理管道:終端中介軟體允許在管道中的任意位置放置中介軟體:
      • 中介軟體通過返回而不是呼叫 next 來終止管道。
      • 終結點始終是終端。
    • 終端中介軟體允許在管道中的任意位置放置中介軟體:

      • 終結點在 UseEndpoints 位置執行。
    • 終端中介軟體允許任意程式碼確定中介軟體匹配的時間:
      • 自定義路由匹配程式碼可能比較複雜,且難以正確編寫。
      • 路由為典型應用提供了簡單的解決方案。 
      • 大多數應用不需要自定義路由匹配程式碼。
    • 帶有中介軟體的終結點介面,如 UseAuthorization 和 UseCors。
      • 通過 UseAuthorization 或 UseCors 使用終端中介軟體需要與授權系統進行手動互動

設定傳統路由

  上面我們知道了通過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型別的一個處理方法。

REST Api 的屬性路由

  上面講的是傳統路由設定,那麼對於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呼叫。對映屬性路由。我們看在使用的時候屬性路由的使用方式。
    1. Route[]
          下面的示例中我們採用的是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";
            }
        }
      1. Http[Verb]
            採用Http[Verb]的方式那就僅僅能作用在action上了。比如下面的就直接在Index上方寫[HttpGet("[controller]/Index")],其他就是HttpPost、HttpDelete等等操作
         [ApiController]
          public class WeatherForecastController : ControllerBase
          {
              [HttpGet("[controller]/Index")]
              public string Index(int? id)
              {
                  return "Test";
              }
          }
        1. Route[]和Http[Verb]混合使用
              有時在實際運用中也可以採取兩種方式混合使用的,例如下面的示例在控制器採用Route[],在action採用Http[Verb]。因為一般定義Api的時候我們不僅要標註action名稱,我們還需要知道action的請求方式。
          [ApiController]
            [Route("[controller]")]
            public class WeatherForecastController : ControllerBase
            {
        
                [HttpGet("Index")]
                public string Index(int? id)
                {
                    return "Test";
                }
            }

         

         

        相關文章