1. 程式人生 > >asp.net core 系列 5 MVC框架路由(上)

asp.net core 系列 5 MVC框架路由(上)

傳遞 交互 light 單個 sof () sta 合並 如何

一. 概述

  介紹asp.net core路由時,我初步想了下,分幾篇來說明。 路由的知識點很多,參考了官方文檔提取出一些重要的知識點來說。 在ASP.NET Core中是使用路由中間件來匹配傳入請求的 URL 並將它們映射到操作(action方法)。路由是在程序啟動時進行傳統路由或屬性路由定義。 路由描述如何將 URL 路徑與操作相匹配。 它還用於在響應中生成送出的 URL(用於鏈接)。

  路由操作既支持傳統路由,也支持屬性路由。也可混合使用。通常傳統路由用於為瀏覽器處理 HTML 頁面的控制器。屬性路由用於處理 web API 的控制器。

  1.1設置路由中間件

要使用傳統路由,必須在UseMVC中間件中配置實現IRouteBuilder接口,在asp.net core mvc 2.2 框架下,應用程序Startup的Configure 方法中,默認路由設置如下:

app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

在對 UseMvc調用中,MapRoute 用於創建單個路由,亦稱 default 路由。 大多數 MVC 應用使用帶有模板的路由。對於default路由簡便的方法可以使用:

app.UseMvcWithDefaultRoute();

  

    UseMvc 和 UseMvcWithDefaultRoute 可向中間件管道添加 RouterMiddleware 的實例。 MVC 不直接與中間件交互,而是使用路由來處理請求。 MVC 通過 MvcRouteHandler 實例連接到路由。

    UseMvc 不直接定義任何路由,它向屬性路由的路由集合添加占位符{controller=Home}/{action=Index}/{id?} 。通過重載 UseMvc(Action<IRouteBuilder>) 則允許用戶添加自己的路由,並且還支持屬性路由。

  

 1.2 傳統路由

 傳統路由是:具有描述性的路由方案,這樣URL具有可讀性。傳統路由格式:{controller=Home}/{action=Index}/{id?}這樣的url路徑是設定了一個約定: 第一段映射到控制器名稱, 第二段映射到操作名稱,第二段映射到可選ID。

 (1) 使用默認路由:  

routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");

使用此默認路由時: url路徑/Products/List 將映射到程序ProductsController(控制器).List(action)中。 url路徑/Blog/Article/17將映射到程序BlogController(控制器).Article(action)中。

 (2) 多個路由:

通過添加對 MapRoute 的多次調用,可以在 UseMvc 內添加多個路由。 這樣做可以定義多個約定,或添加專用於特定操作的傳統路由,比如:

app.UseMvc(routes =>
    {
     routes.MapRoute("blog", "blog/{*article}",
            defaults: new { controller = "Blog", action = "Article" });
     routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
   });

  這裏的blog路由是一個專用的傳統路由,這表示blog使用傳統路由系統,但專用於特定的操作,也就是對於BlogController控制器的Article操作,此專用路由將始終映射。對於多個路由的路由集合會進行排序,並按添加順序進行處理,因此,在此示例中,將先嘗試 blog 路由,再嘗試 default 路由。

 (3) action操作的區分

在處理url請求時,當通過路由匹配到一個控制器內兩項相同的action名稱時,mvc必須進行區分,以選擇最佳候選項,否則會引發異常(AmbiguousActionException)。

public class ProductsController : Controller
     {
       public IActionResult Edit(int id) { ... }

       [HttpPost]
       public IActionResult Edit(int id, Product product) { ... }
     }

此Products控制器定義了二項操作,這兩項操作均與 URL 路徑的 /Products/Edit/17 匹配相同。解決方案是將要提交的action加上 Http 謂詞為 POST。這樣post過來時,就會選擇Edit(int, Product)

  1.3 屬性路由

    通過在控制器(Controller)或操作(Action)上放置路由可實現屬性路由。 不能通過傳統路由訪問定義屬性路由的操作,反之亦然。 控制器上的任何路由屬性,都會使控制器中的所有操作使用屬性路由。

    屬性路由使用一組屬性將action直接映射到路由模板。在下面的示例中,Configure 方法使用 app.UseMvc();,不傳遞任何路由。 HomeController 將匹配一組 URL,這組 URL 與默認路由 {controller=Home}/{action=Index}/{id?} 匹配的 URL 類似:

    當去掉default默認路由模板後,只使用app.UseMvc()時。運行程序時,頁面報404錯誤:找不到 localhost 的網頁。

 app.UseMvc();

    (1) 屬性路由基本使用   

    如果定義了屬性路由的操作,此時就是啟動屬性路由功能。Home控制器的屬性路由示例如下:

public class HomeController : Controller
    {
       [Route("")]
       [Route("Home")]
       [Route("Home/Index")]
       public IActionResult Index()
       {
          return View();
       }
    }

    在index的action上加[Route("")]屬性路由。 瀏覽器可以使用下面三種url來訪問,也是程序啟動時的默認加載頁面:

      http://localhost:30081/

      http://localhost:30081/Home/

      http://localhost:30081/Home/index

(2) 屬性路由精確控制

屬性路由需要更多輸入來指定路由;傳統的默認路由處理路由的方式則更簡潔。 但是,屬性路由允許(並需要)精確控制應用於每項操作的路由模板。下面示例是精確控制每項操作的路由模板,比如url訪問/home/index時,即是調用MyIndex的action方法。

public class MyDemoController : Controller
    {
       [Route("")]
       [Route("Home")]
       [Route("Home/Index")]
       public IActionResult MyIndex()
       {
          return View("Index");
       }
    }
 1.4 使用 Http[Verb] 屬性的屬性路由

屬性路由還可以使用 Http[Verb] 屬性,比如 HttpPostAttribute 所有這些屬性都可采用路由模板。 此示例展示,同一路由模板匹配的兩項操作:

[HttpGet("/products")]
    public IActionResult ListProducts()
    {
       // ...
    }

    [HttpPost("/products")]
    public IActionResult CreateProduct(...)
    {
       // ...
    }

    當 Http 謂詞為 GET 時將執行ProductsApi.ListProducts 操作, 當 Http 謂詞為 POST 時將執行 ProductsApi.CreateProduct。生成 REST API 時,很少會在操作方法上使用 [Route(...)]。 建議使用更特定的 Http*Verb*Attributes 來明確 API 所支持的操作。 REST API 的客戶端需要知道映射到特定邏輯操作的路徑和 Http 謂詞。

    例如下面一個web api訪問路由,使用Http*Verb*Attributes 來明確定義如下:

 public class ProductsApiController : Controller
    {
       [HttpGet("/products/{id}", Name = "Products_List")]
       public IActionResult GetProduct(int id) { ... }
    }

  上面定義只有針對如訪問url如: /products/3(而非 /products)之類的 URL才會執行 ProductsApi.GetProduct(int) 操作。

 1.5 路由合並

    若要使屬性路由減少重復,可將控制器Controller上的路由屬性與各個操作Action上的路由屬性合並。 控制器上定義的所有路由模板均作為操作上路由模板的前綴。 在控制器上放置路由屬性會使控制器中的所有操作都使用屬性路由。

    下面是一個web api的路由合並,訪問Get的方法的訪問路徑為: http://localhost:30081/api/Products/1

[Route("api/Products")]
    public class ProductsApiController : Controller
    {
        // GET api/values/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }
    }

下面是一個控制器的路由合並。訪問index頁面的訪問路徑為: http://localhost:30081/home/index

[Route("Home")]
  public class HomeController : Controller
  {
    [Route("")]      // Combines to define the route template "Home"
    [Route("Index")] // Combines to define the route template "Home/Index"
    [Route("/")]     // Doesn‘t combine, defines the route template ""
    public IActionResult Index()
    {
      //...
    }
  }
 1.6 指定屬性路由參數約束
  [HttpGet("Home/{id:int}",Name = "Pri")]
        public IActionResult Privacy(int id)
        {
            return View();
        }

如果輸入非整數類型的參數,瀏覽器提示:找不到與以下網址對應的網頁:http://localhost:30081/home/dd

  1.7 自定義路由屬性

    該框架中提供的所有路由屬性([Route(...)]、[HttpGet(...)] 等)都可實現 IRouteTemplateProvider接口。 當應用啟動時,MVC 會查找控制器類和操作方法上的屬性,並使用可實現 IRouteTemplateProvider的屬性生成一組初始路由。

    下面使用IRouteTemplateProvider 來定義自己的路由屬性。每個 IRouteTemplateProvider 都允許定義一個包含自定義路由模板、順序和名稱的路由:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
    {
        //實現接口的三個屬性,這裏的[controller]是一個標記替換。
        public string Template => "api/[controller]/{action}/{id?}";

        public int? Order { get; set; }

        public string Name { get; set; }
    }    

    public class ProductsApiController : Controller
    {
        // GET api/values/5
        //  [HttpGet("{id}")]
        [MyApiController()]
        public string Get(int id)
        {
            return "value";
        }
    }

通過訪問url: http://localhost:30081/api/ProductsApi/get/1 來調用get方法。

參考文獻

  官方資料:asp.net core routing

asp.net core 系列 5 MVC框架路由(上)