【ASP.NET Core】給路由規則命名有何用處

上一篇中老周給夥伴們介紹了自定義視圖搜索路徑的方法,本篇我們扯一下有關 URL 路徑規則的名稱問題。在扯今天的話題以前,先補充點東東。在上一篇中設置視圖搜索路徑時用到三個有序參數:{2}{1}{0},分別是 Area、Controller、Action。其中說到幾個特殊的視圖,如_Layout.cshtml、_ViewStart.cshtml等。_Layout.cshtml 頁默認放在 /Views/Shared 目錄下,但,_ViewStart.cshtml 和 _ViewImports.cshtml 這兩個不該該放在 Shared 目錄下,通常應放到 /Views 下,這樣它們能夠做用於全部的視圖。若是放到了 Shared 目錄下,它們只對 Shared 目錄中的視圖起做用,而對於 Views 下的其餘視圖不起做用。html

好比,放到 /Views 下。app

Views(目錄)
    │  _ViewImports.cshtml
    │  _ViewStart.cshtml
    │  
    └─Home(目錄,Controller的名字)
            Index.cshtml(視圖,Action)

其中,Home 是子目錄,對應着控制器 Home,Home 中的 Index.cshtml 視圖對應着 Action 名 Index。此時,_ViewStart 和 _ViewImports 中的內容會應用到 /Views 下的全部視圖中(如 Index.cshtml)。asp.net

要是改成這樣。佈局

Views
    ├─Home
    │      About.cshtml
    │      Index.cshtml
    │      _ViewImports.cshtml
    │      _ViewStart.cshtml
    │      
    └─Users
            AddNew.cshtml

此時,Views 有兩個子目錄,Home 是一個控制器,Users 是另外一個控制器,這時候,_ViewStart 和 _ViewImports 只對 Home 下面的視圖起做用,對 Users 目錄下的視圖是不起做用的。post

_ViewStart 主要用途是在全部視圖文件執行以前執行,通常咱們用它來設置 Layout 屬性,以指定使用的佈局頁(至關於頁面母板),這樣一來,咱們不須要在每一個視圖上都加 Layout = "xxxx" 了。_ViewImports 主要是用來引入要用到的命名空間(就是 C# 中的 using),這樣你不須要在每一個視圖中寫一堆 @using Razor 標記了。spa

這兩個文件都是約定式的,因此你不該該隨便改它的名字,_ViewImports 能夠經過 RazorTemplateEngineOptions 類的 ImportsFileName 屬性來修改,不過,_ViewStart 好像不能改,老周看到 asp.net core 源碼中是寫死了的,估計是不能改文件名的。.net

其實,這兩個文件不該該更名,並且你改了名字也沒啥用,反正功能是不變的,仍是遵照約定好一些,這樣人家看你的項目時也看得懂。_Layout.cshtml 文件如非必要,也不該該更名字,若是你的應用要用多個佈局視圖,可能建個子目錄,而後每一個子目錄下放_Layout,這樣結構清晰一些,畢竟,看到 _Layout.cshtml 就明白它是母板頁了。code

 

規則模板

咱們都知道,在 Startup.Configure 方法中,會以此方式來指定 URL 路徑規則。orm

            app.UseMvc(route =>
            {
                route.MapRoute("main", "{controller=Students}/{action=List}/{sid?}");
                route.MapRoute("edit_post", "{controller}-{action}");
            });

你能夠添加 K 條規則,好比上面的例子,我添加了兩條規則。htm

{controller} 和 {action} 是約定的名稱,用來識別 Controller 和 Action ,因此你不要自做聰明亂來,必要有些寫死了的參數才能進行 URL 分析,否則,你給個 URL http://dog.org/shopping/pay/500,那應用程序根本不知道哪一段是表示 Controller,哪一段是表示 action。

若是肯定了 controller 和 action 這兩個值,那麼其餘的參數就好分析了。

其餘參數若是是可選的,能夠在後面加個問號,好比 {controller}/{action}/{id?},這表示 id 的值是可選的。

上面老周添加的兩個規則中,edit_post 那個其實不太規範,URL 中各段最好用 「/」 來分隔,由於 「-」 有時候是不容許用的,好比,id 參數前面就不能用,你不能寫成 {controller}-{action}-{id?},要是 id 中包含了字符「-」,咋辦呢?而「/」則不一樣,URL Encode 後不會冒出這個字符來。

因此用 / 最好,這裏用 - 只是老周故意用來演示而已,URL 嘛,不必玩花樣,沒意義。

 

基於 Attribute 指定的 URL 路由

在 Startup.Configure 方法中指定的 URL 路由是做用於整個應用程序的,若是想爲個別控制器或個別 Action 指定路由規則,那麼能夠考慮使用 Attribute 的形式。

attribute 形式的路由規則和應用程序級別的規則類似,只是,在應用級別時,用大括號來包裹參數名(如 {controller}),而在 Attribute 方案中,是用中括號的,它只能用兩個值:[controller]、[action]。其餘參數也是用大括號。好比,[controlloer]/[action]/[id?] 會報錯,你得改成 [controller]/[action]/{id?}。

RouteAttribute 既能夠用於 Controller 類型,也可能用於單個 Action 方法上。我舉個例子,像這樣。

    [Route("hello/[controller]/[action]")]
    public class SomethingController : Controller
    {
        [Route("{name?}")]
        public IActionResult SayHi(string name)
        {
            ……
        }
    }

在類上應用用的 Attribute 中,可使用這樣的 URL :http://localhost:999/hello/something/sayhi 。而在 SayHi 方法上,又用了 Route Attribute,指定了一個附加參數 name,而且是可選的。因而它能夠與類上的 Route attribute 合併,變成:http://localhost:999/hello/something/sayhi/Peter。這時,字符串 Peter 會傳給 SayHi 方法的 name 參數,由於,參數的名字與 Route 中的參數名是相同的,都叫 name。若是 SayHi 中的參數名不叫 name,那你得運用一下 FromRouteAttribute 了。就像這樣。

        [Route("{name?}")]
        public IActionResult SayHi([FromRoute(Name = "name")]string who)
        {
            ……
        }

若是你但願 URL 中給 name 傳入 int 類型的值,你還能夠限制它。

 [Route("{name:int}")]

其實這些約束條件對應的是 Microsoft.AspNetCore.Routing.Constraints 命名空間下面的類型。

 

Route Data

Route data 其實就是一個字典,存放的就是 URL 路徑規則中參數與值的 key-value 對。這個很簡單,我舉個例子,你就明白了。

我們就直接用上面那個例子吧。

    [Route("hello/[controller]/[action]")]
    public class SomethingController : Controller
    {
        [Route("{name?}")]
        public IActionResult SayHi([FromRoute(Name = "name")]string who)
        {
            return Json(RouteData.Values);
        }
    }

在 SayHi 方法中,我們把 route data 返回。

運行應用後,輸入地址:http://localhost/hello/something/sayhi/Tom,獲得的輸出以下:

不用我解釋了吧。

 

給路由命名

上面的都是 F 話,本小節纔是本文的主題。咱們回頭看看上面老周舉過例的那個 route。

            app.UseMvc(route =>
            {
                route.MapRoute("main", "{controller=Students}/{action=List}/{sid?}");
                route.MapRoute("edit_post", "{controller}-{action}");
            });

每條路由規則都會有本身的 name,爲啥要命名?最直接的理由是爲了惟一標識每條規則。除了此因素外,咱們能夠在開發過程當中選擇使用哪條規則,有了 name,想找出某條規則就好辦了,就比如你上學的時候,老師點名,要麼點姓名,要麼點學號。

基於 Attribute 的路由規則也能夠命名的,例如。

 [Route("hello/[controller]/[action]", Name = "prv")]

這樣就把它命名爲 prv 了,你還能夠這樣寫。

  [Route("hello/[controller]/[action]", Name = "[controller]_[action]")]

這樣也能夠用 Controller 和 Action 的名字生成一個惟一的名字,好比 Something_SayHi。可是這種方法太動態了,好像不那麼好操控,仍是用一個固定的名字好一點。

要在開發的時候選擇使用指定的 URL 路由,須要在 Razor 頁中添加 Tag Helper,標記幫助類能夠擴展 HTML 標記的某些功能。在須要使用 tag helper 的頁面,或者統一在 _ViewImports.cshtml 頁中加入這些指令。

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

格式是這樣的:

<類型全路徑>, <程序集>

類型寫在前面(包括 namespace 名),程序集名寫在後面,用逗號分隔。這裏用星號(*)是最爽的,它是通配符,表示引入全部 tag helper 類型。這樣快捷,一行代碼了事。

而後在 HTML 中你這樣寫。

    <form method="post" asp-route="edit_post">         <div class="form-group">
            <label asp-for="Name"></label>
            <input asp-for="Name" class="form-control"/>
            <span asp-validation-for="Name" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="Age"></label>
            <input asp-for="Age" class="form-control"/>
            <span asp-validation-for="Age" class="text-danger"></span>
        </div>
        <input asp-for="ID"/>
        <button type="submit" class="btn btn-dark">提交</button>
    </form>

其餘代碼你不用看了,只看這一句就夠了:

 asp-route="edit_post"

它的意思就是使用我剛剛定義的那條規則。

 route.MapRoute("edit_post", "{controller}-{action}");

因此,在運行後就會生成這樣的 HTML。

   <form method="post" action="/Students-Editdata">
        <div class="form-group">
            此處省略 1650 個字
</form>
          

由於我定義的規則是 {controller}-{action}的形式,因此,Controller 是 Students,Action 是 Editdata,連起來就是 Students-Editdata。

那麼,這裏它爲何能識別出 controller 和 action 的值呢,你看看個人代碼就知道了。

    public class StudentsController : Controller
    {
        readonly StudentDBContext m_context;
        // 接收依賴注入
        public StudentsController(StudentDBContext c)
        {
            m_context = c;
        }

        public IActionResult List()
        {
            var q = from s in m_context.Students
                    orderby s.ID
                    select s;

            return View(q.ToList());
        }

        /***************************************************/
        // 如下方法用於編輯頁
        [HttpGet]
        public IActionResult Editdata([FromRoute(Name = "sid")] int id)
        {
            var q = from s in m_context.Students
                    where id == s.ID
                    select s;
            Student stu = q.FirstOrDefault();
            if(stu == null)
            {
                return Content("在地球上找不到此學員。");
            }
            return View(stu);
        }

        [HttpPost]
        public IActionResult Editdata(Student s)
        {
            if (ModelState.IsValid == false)
            {
                return View(s);
            }
            m_context.Students.Update(s);
            m_context.SaveChanges();
            return RedirectToAction(nameof(List));
        }
    }

我定義了 Editdata 方法的重載,一個用於 get 請求,一個用於 post 請求,form 是以 post 方式提交,所以它能自動識別出 controller 和 action 的名字。

那萬一,若是不是同名的呢,好辦。你用 asp-route-<value> 來指定各個參數的值。好比這樣

    <form method="post"
          asp-route="edit_post" asp-route-controller="Demo" asp-route-action="Runwork" asp-route-sid="1">

在 asp-route- 後面直接跟上路由規則參數的名稱就能夠了。

 

有一點要注意,asp-route 與 asp-controller、asp-action是會衝突的,若是你用了這兩個標記,就不能用 asp-route 標記了,固然 asp-route-xxx 是能夠用的。

好了,今天的內容就扯到這兒了,順便把示例的代碼也傳上來,以供夥伴們娛樂。

示例源代碼下載地址

相關文章
相關標籤/搜索