【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 下的其他視圖不起作用。

比如,放到 /Views 下。

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

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

要是改為這樣。

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

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

_ViewStart 主要用途是在所有視圖文件執行之前執行,一般我們用它來設置 Layout 屬性,以指定使用的布局頁(相當於頁面母板),這樣一來,我們不需要在每個視圖上都加 Layout = "xxxx" 了。_ViewImports 主要是用來引入要用到的命名空間(就是 C# 中的 using),這樣你不需要在每個視圖中寫一堆 @using Razor 標記了。

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

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

 

規則模板

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

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

你可以添加 K 條規則,比如上面的例子,我添加了兩條規則。

{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 是可以用的。

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

示例源代碼下載地址

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM