.Net Core 路由處理

  用戶請求接口路由,應用返回處理結果。應用中如何匹配請求的數據呢?爲什麼能如此精確的找到對應的處理方法?今天就談談這個路由。路由負責匹配傳入的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 來終止管道。
      • 終結點始終是終端。
    • 終端中間件容許在管道中的任意位置放置中間件:javascript

      • 終結點在 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";
        }
    }
相關文章
相關標籤/搜索