如何在 ASP.NET Core 中使用 Route 特性

ASP.NET Core 中的 Route 中間件的職責在於將 request 匹配到各自 Route 處理程序上,Route 分兩種:基於約定基本特性 模式。

基於約定 模式的Route採用集中化的方式,而 基於特性 的方式允許你在 Action 或者 Controller 上單獨定義,到底採用哪一種可以基於你自己的應用場景,本篇就來討論如何使用 基於特性 模式。

創建 Controller 類

創建一個 DefaultController 類,新增如下代碼。


    public class DefaultController : Controller
    {
        [Route("")]
        [Route("Default")]
        [Route("Default/Index")]
        public ActionResult Index()
        {
            return new EmptyResult();
        }
        [Route("Default/GetRecordsById/{id}")]
        public ActionResult GetRecordsById(int id)
        {
            string str = string.Format
            ("The id passed as parameter is: {0}", id);
            return Ok(str);
        }
    }

Controller 級別定義 Route 特性

Route特性可用於 Controller 和 Action 級別,值得注意的是,如果應到到前者,那麼 Controller 下的所有 Action 都受這個 Route 管控。

如果你仔細觀察上面的 DefaultController 類代碼,你會發現兩個 Action 方法的 Route 路徑都有 Default 前綴,這就不優雅了,優化方式就是把 Route 路徑中的 Default 提取到 Controller 級別,代碼如下:


[Route("Default")]   
public class DefaultController : Controller
{
  [Route("")]
  [Route("Index")]
  public ActionResult Index()
  {
      return new EmptyResult();
   }
  [HttpGet]
  [Route("GetRecordsById/{id}")]
  public ActionResult GetRecordsById(int id)
  {
      string str = string.Format("The id passed as parameter is: {0}", id);
      return Ok(str);
   }
}

可以看出當 Controller 和 Action 級別都被 Route 打上標記之後,Asp.Net Core 中的 Route 引擎會自動將兩者拼接起來,當然更簡單粗暴的做法就是在 Controller 上使用 RoutePrefix 特性,如下代碼所示:


[RoutePrefix("services")]
public class HomeController : Controller
{
   //Action methods
}

Action 級別定義 Route 特性

參考剛纔的 DefaultController 類,我在 Index 方法上面定義了三個 Route 特性,這就意味着下面三種 Route 都可以訪問到 Index() 方法,如下代碼所示:


http://localhost:11277
http://localhost:11277/home
http://localhost:11277/home/index


常常在 基於約定 模式的Route中,它的 Route template 會有一些對參數的約定,比如下面的代碼:


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

同樣 基於特性 模式的 Route 也是可以使用參數模式的,比如文章之前的 DefaultController.GetRecordsById 就是的,值得注意的是模板中的 {id} 表示可接收任何參數,如 string,int 等等,如果你想限定爲 int 的話,也是可以實現的。

使用 Route 約束

Route 約束 就是 Controller 前的一個防火牆,他會踢掉一些不合規範的 Action 請求,比如說:你要求某個 Action 接收的參數必須是 int,那在 Route 模板中定義的語法格式一定是這樣的 {parameter:constraint},如下代碼所示:


[Route("Default/GetRecordsById/{id:int}")]
public ActionResult GetRecordsById(int id)
{
  string str = string.Format("The id passed as parameter is: {0}", id);
  return Ok(str);
}

在 Route 中使用可選參數

你也可以在 Route Template 上指定可選參數,意味着這個參數可傳可不傳,格式如下:


[Route("Sales/GetSalesByRegionId/{id?}")]

有一點非常重要,當你使用了 Route特性 之後,其實 Controller 或者 Action 的名字就不再重要了,因爲 Route處理引擎 已經不再將其作爲參考選項,下面的代碼片段展示瞭如何在 Action 方法上變更 Route template 格式。


[Route("Home/GetRecordsById/{id:int}")]
public ActionResult GetRecordsById(int id)
{
   string str = string.Format("The id passed as parameter is: {0}", id);
   return Ok(str);
}

接下來可以直接使用如下地址訪問 GetRecordsById 方法。


http://localhost:11277/home/GetRecordsById/1

對 Action 中的參數使用多個約束

真實場景中你不僅要求 id 必須是整數,還要求必須有一定意義,比如說最小值爲1,對這種有 多重約束 的需求如何去實現呢?請看下面代碼。


[Route("Default/GetRecordsById/{id:int:min(1)}")]
public ActionResult GetRecordsById(int id)
{
    string str = string.Format("The id passed as parameter is: {0}", id);
    return Ok(str);
}

常使用的 Route 約束

  • int       限定爲 int 類型

  • max/min   限定 int 的最大數和最小數

  • minlength 限定 string 的最小長度

  • regex     限定符合的正則

創建自定義的 Route 約束

如果上面的一些約束不滿足你的要求,你完全可以爲你的場景深度定製,做法就是使用 IRouteConstraint 接口並實現它的 Match 方法即可,如下代碼所示:


    public class CustomRouteConstraint : IRouteConstraint
    {
        public bool Match(HttpContext httpContext, IRouter route,
        string routeKey,
        RouteValueDictionary values, RouteDirection routeDirection)
        {
            throw new NotImplementedException();
        }
    }

在 Controller 上使用 token 佔位符

所謂的 token 佔位符 就是具有一些特定含義的佔位符號,比如說:[action], [area] 和 [controller],分別表示用你真實的 Controller 和 Action 去替換,下面的代碼展示瞭如何使用這種模式去實現。


[Route("[controller]/[action]")]
public class HomeController : Controller
{
   private readonly ILogger<HomeController> _logger;
   public HomeController(ILogger<HomeController> logger)
   {
       _logger = logger;
   }
   public IActionResult Index()
   {
       return View();
   }
   //Other action methods
}

整體來看,基於特性 的 Route 給了你更多的操控權限,靈活的 Route Template 配置實現了 Controller 和 Action 的解耦,當然這裏也不是說 基於約定 的Route 不好,畢竟人家是 Global 級別的,真實場景下兩者更多的是混着用。

譯文鏈接:https://www.infoworld.com/article/3569369/how-to-use-attribute-routing-in-aspnet-core.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章