天天看點

.NET 雲原生架構師訓練營(子產品二 基礎鞏固 路由與終結點)--學習筆記2.3.3 Web API -- 路由與終結點

2.3.3 Web API -- 路由與終結點

  • 路由模闆
  • 約定路由
  • 特性路由
  • 路由沖突
  • 終結點

ASP.NET Core 中的路由:

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/routing?view=aspnetcore-5.0

UseRouting 添加路由中間件到管道,路由中間件用來比對 url 和具體的 endpoint,然後執行 endpoint

UseEndpoints 添加或者注冊 endpoint 到程式中,使得路由中間件可以發現它們

  • MapRazorPages for Razor Pages 添加所有 Razor Pages 終結點
  • MapControllers for controllers 添加所有 controller 終結點
  • MapHub for SignalR 添加 SignalR 終結點
  • MapGrpcService for gRPC 添加 gRPC 終結點

路由模闆由 token 和其他特定字元組成。比如“/”,特定字元進行路由比對的時候必須全部比對

/hello/{name:alpha}

{name:alpha} 是一段 token,一段 token 包括一個參數名,可以跟着一個限制(alpha)或者一個預設值(mingson),比如 {name=mingson} ,或者直接 {name}

app.UseEndpoints(endpoints =>
{
    //endpoints.MapControllers();
    endpoints.MapGet("/hello/{name:alpha}", async context =>
    {
        var name = context.Request.RouteValues["name"];
        await context.Response.WriteAsync($"Hello {name}!");
    });
});           

路由模闆中的參數被存儲在 HttpRequest.RouteValues 中

大小寫不敏感

url 中如果有符合,在模闆中用{}代替

catch-all 路由模闆

  • 在 token 前用 或者 加在參數名前,比如 blog/{slug}
  • blog/ 後面的字元串會當成 slug 的路由參數值,包括 "/",比如浏覽器輸入 blog/my/path 會比對成 foo/my%2Fpath,如果想要得到 blog/my/path 則使用兩個 ,foo/{path}
  • 字元串.也是可選的,比如 files/{filename}.{ext?},如果要輸入 /files/myFile 也能比對到這個路由
//app.Run(async context =>
//{
//    await context.Response.WriteAsync("my middleware 2");
//});

app.UseEndpoints(endpoints =>
{
    //endpoints.MapControllers();

    // 将終結點綁定到路由上
    endpoints.MapGet("/hello", async context =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
});           

啟動程式,通路:

https://localhost:5001/hello

輸出如下:

my middleware 1Hello World!           

擷取路由模闆參數

endpoints.MapGet("/blog/{*title}", async context =>
{
    var title = context.Request.RouteValues["title"];
    await context.Response.WriteAsync($"blog title: {title}");
});           
https://localhost:5001/blog/my-title
my middleware 1blog title: my-title           

constraint 限制

.NET 雲原生架構師訓練營(子產品二 基礎鞏固 路由與終結點)--學習筆記2.3.3 Web API -- 路由與終結點
.NET 雲原生架構師訓練營(子產品二 基礎鞏固 路由與終結點)--學習筆記2.3.3 Web API -- 路由與終結點
[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }           
app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}",
        context => 
        {
            return context.Response.WriteAsync("inline-constraint match");
        });
});           

預設

endpoints.MapDefaultControllerRoute();           

自定義

endpoints.MapControllerRoute("default","{controller=Home}/{action=Index}/{id?}");           
// 約定路由
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

// 約定路由也可以同時定義多個
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapControllerRoute(
        name: "blog",
        pattern: "blog/{*article}",
        defaults: new {controller = "blog", action = "Article"});
});           

controller

[Route("[controller]")]           

http method

[HttpGet("option")]

[HttpGet]
[Route("option")]

[HttpGet]
[Route("option/{id:int}")]           

[HttpGet]
//[Route("option")]
public IActionResult GetOption()
{
    return Ok(_myOption);
}           

如果路由相同,啟動程式會報錯:

AmbiguousMatchException: The request matched multiple endpoints. Matches:
HelloApi.Controllers.ConfigController.GetOption (HelloApi)
HelloApi.Controllers.ConfigController.GetConfigurations (HelloApi)           

ASP.NET Core 終結點是:

  • 可執行:具有 RequestDelegate。
  • 可擴充:具有中繼資料集合。
  • Selectable:可選擇性包含路由資訊。
  • 可枚舉:可通過從 DI 中檢索 EndpointDataSource 來列出終結點集合。

終結點可以:

  • 通過比對 URL 和 HTTP 方法來選擇。
  • 通過運作委托來執行。
.NET 雲原生架構師訓練營(子產品二 基礎鞏固 路由與終結點)--學習筆記2.3.3 Web API -- 路由與終結點

中間件的每一步都在比對終結點,是以路由和終結點之間的中間件可以拿到終結點的資訊

app.UseRouting();

// 路由和終結點之間的中間件可以拿到終結點的資訊
app.Use(next => context =>
{
    // 擷取目前已經被選擇的終結點
    var endpoint = context.GetEndpoint();
    if (endpoint is null)
    {
        return Task.CompletedTask;
    }
    // 輸出終結點的名稱
    Console.WriteLine($"Endpoint: {endpoint.DisplayName}");
    // 列印終結點比對的路由
    if (endpoint is RouteEndpoint routeEndpoint)
    {
        Console.WriteLine("Endpoint has route pattern: " +
                          routeEndpoint.RoutePattern.RawText);
    }
    // 列印終結點的中繼資料
    foreach (var metadata in endpoint.Metadata)
    {
        Console.WriteLine($"Endpoint has metadata: {metadata}");
    }

    return Task.CompletedTask;
});

app.UseEndpoints(endpoints =>
{
    //endpoints.MapControllers();

    // 将終結點綁定到路由上
    endpoints.MapGet("/blog/{title}", async context =>
    {
        var title = context.Request.RouteValues["title"];
        await context.Response.WriteAsync($"blog title: {title}");
    });
});           
https://localhost:5001/blog/my-first-blog

控制台輸出如下:

Endpoint: /blog/{title} HTTP: GET
Endpoint has route pattern: /blog/{title}
Endpoint has metadata: System.Runtime.CompilerServices.AsyncStateMachineAttribute
Endpoint has metadata: System.Diagnostics.DebuggerStepThroughAttribute
Endpoint has metadata: Microsoft.AspNetCore.Routing.HttpMethodMetadata           

列印 http 方法

// 列印終結點的中繼資料
foreach (var metadata in endpoint.Metadata)
{
    Console.WriteLine($"Endpoint has metadata: {metadata}");
    // 列印 http 方法
    if (metadata is HttpMethodMetadata httpMethodMetadata)
    {
        Console.WriteLine($"Current Http Method: {httpMethodMetadata.HttpMethods.FirstOrDefault()}");
    }
}           
Current Http Method: GET           

修改終結點名稱、中繼資料

app.UseEndpoints(endpoints =>
{
    //endpoints.MapControllers();

    // 将終結點綁定到路由上
    endpoints.MapGet("/blog/{title}", async context =>
        {
            var title = context.Request.RouteValues["title"];
            await context.Response.WriteAsync($"blog title: {title}");
        }).WithDisplayName("Blog")// 修改名稱
        .WithMetadata("10001");// 修改中繼資料
});           
  • 調用 UseRouting 之前,終結點始終為 null。
  • 如果找到比對項,則 UseRouting 和 UseEndpoints 之間的終結點為非 null。
  • 如果找到比對項,則 UseEndpoints 中間件即為終端。 稍後會在本文檔中定義終端中間件。
  • 僅當找不到比對項時才執行 UseEndpoints 後的中間件。

GitHub源碼連結:

https://github.com/MINGSON666/Personal-Learning-Library/tree/main/ArchitectTrainingCamp/HelloApi

繼續閱讀