ASP.Net MVC開發基礎學習筆記:三、Razor視圖引擎、控制器與路由機制學習


一、天降神器“剃須刀” — Razor視圖引擎

1.1 千呼萬喚始出來的MVC3.0

  在MVC3.0版本的時候,微軟終於引入了第二種模板引擎:Razor。在這之前,我們一直在使用WebForm時代沿留下來的ASPX引擎或者第三方的NVelocity模板引擎。

  Razor在減少代碼冗余、增強代碼可讀性和Visual Studio智能感知方面,都有着突出的優勢。Razor一經推出就深受廣大ASP.Net開發者的喜愛。

1.2 Razor的語法

  (1)Razor文件類型:Razor支持兩種文件類型,分別是.cshtml 和.vbhtml,其中.cshtml 的服務器代碼使用了c#的語法,.vbhtml 的服務器代碼使用了vb.net的語法。

  (2)@字符:@是Razor中的一個重要符號,它被定義為Razor服務器代碼塊的開始符號。例如,我們可以在View中直接寫C#代碼輸出日期

1 <p>@DateTime.Now.ToString()</p>

1.3 Razor語句塊

  (1)在Razor視圖引擎中,我們可以使用@{code}來定義一段代碼塊

  (2)Razor支持代碼混寫:在代碼塊中插入HTML、在HTML中插入Razor語句都是可以的。例如,我們可以使用@來作for循環,還可以進行if判斷

@for (int i = 0; i < 10; i++)
{
     <p>@i</p>
}

@if (ViewData.Count > 0)
{
     <p>ViewData有數據</p>
     ViewData["Key"] = "Edison Chou";
}
else
{
     <p>ViewData暫無數據</p>
}

1.4 Razor頁面輸出特殊字符串

  與在ASPX試圖引擎中類似,如果要輸出特殊字符串,還是借助HtmlHelper類提供的擴展方法來實現。

  (1)輸出原生的字符串:@Html.Raw(html)
@Html.Raw("<h1>Razor</h1>")
  PS:默認的@會解析掉html代碼
  (2)還可以通過使用 HtmlString類型和 MvcHtmlString類型字符串輸出原生包含HTML的字符串
@{
            IHtmlString html = new HtmlString("<span style='color:red'>哈哈,我是Razor剃須刀!</span>");
            Response.Write(html);
}

1.5 Razor中的注釋

  Razor服務器端注釋為:@*  注釋內容  *@

@*<p>你好,Razor引擎!</p>*@

1.6 Razor中轉換數據類型

  在Razor中提供了很多方便我們進行數據類型轉換的方法以及類型判斷的方法,如下圖所示:

  例如,我們可以在View中對一個字符串進行判斷和轉換:

@{
        string test = "Edison Chou";
        <p>@test.IsInt()</p>
        <p>@test.AsInt()</p>
}

二、Controller深入詳解

2.1 控制器的三個職責 

  (1)處理跟用戶的交互
  (2)處理業務邏輯的調用
  (3)指定具體的視圖顯示數據,並且把數據傳遞給視圖

2.2 控制器的三個約定

  (1)必須是非靜態類
  (2)必須實現IController接口
  (3)必須是以Controller結尾命名

2.3 無所不能的Action

  首先,在一個Controller中可以包含多個Action. 每一個Action都是一個方法, 返回一個ActionResult實例。那么,這個ActionResult是什么東東呢?

  由微軟給出的注釋可以知道,ActionResult是一個操作方法的結果,並且是一個抽象類,那么,也就代表了可以有多重結果的實現。這樣就解釋了,我們在Action中可以不僅可以返回ViewResult還可以返回JsonResult的原因。通過下表,我們可以清晰地看到,ActionResult的各種派生類的詳情:

  從表中可以看出,我們所常用的各種XXXXResult都不約而同地繼承了ActionResult這個基類,或者是其父類(例如:ViewResultBase)繼承了ActionResult這個基類。因此,我們既可以在Action中返回視圖,還可以返回文件流、重定向、空內容等結果。特別是,以前我們在WebForm時代常常與瀏覽器交互采用JSON格式的數據,需要使用JavaScriptSerializer這個類進行Serialize后返回。但是,在MVC的Action中,微軟已經幫我們封裝了好了JsonResult,因此,我們可以高興地感慨:返回Json,So Easy!

2.4 ActionResult用法

  這里只介紹幾個最常用的Result用法:

  (1)EmptyResult:當用戶有誤操作或者是圖片防盜鏈的時候,這個EmptyResult就可以派上用場,返回它可以讓用戶啥也看不到內容,通過訪問瀏覽器端的源代碼,發現是一個空內容;

public ActionResult Empty()
{
      return new EmptyResult();
}

  (2)Content:通過Content可以向瀏覽器返回一段字符串類型的文本結果,就相當於Response.Write("xxxx");一樣的效果;

public ActionResult ContentResultDemo()
{
      string contentString = "Hello Edison Chou!";
      return Content(contentString);
}

  (3)File:通過File可以向瀏覽器返回一段文件流,主要用於輸出一些圖片或文件提供下載等;

public ActionResult FileStreamResultDemo()
{
      FileStream fs = new FileStream(Server.MapPath(@"/Content/programmer.jpg"),
      FileMode.Open, FileAccess.Read);
      return File(fs, @"image/gif");
}

  (4)HttpUnauthorizedResult:通過HttpUnauthorizedResult可以向瀏覽器輸出指定的狀態碼和狀態提示,如果不指定狀態碼,則默認為401無權訪問;

public ActionResult HttpUnauthorizedResultDemo()
{
     return new HttpUnauthorizedResult();
}

  (5)RedirectRedirectToAction:重定向與重定向到指定Action,我一般使用后者,主要是向瀏覽器發送HTTP 302的重定向響應;

public ActionResult RedirectResultDemo()
{
      return Redirect(@"http://localhost:23531/Home/ContentResultDemo");
}

public ActionResult RedirectToRouteResultDemo()
{
      return RedirectToAction("FileStreamResultDemo", "Home");
}

  (6)Json:通過Json可以輕松地將我們所需要返回的數據封裝成為Json格式,進行Ajax開發可以變得so easy!

public ActionResult JsonResultDemo()
{
      var tempObj = new { Controller = "HomeController", Action = "JsonResultDemo" };
      return Json(tempObj, JsonRequestBehavior.AllowGet);
}

  (7)JavaScript:可以通過JavaScriptResult向瀏覽器單獨輸出一段JS代碼,不過由於主流瀏覽器都對此進行了安全檢查,因此你的JS代碼也許無法正常執行,反而是會以字符串的形式顯示在頁面中;

三、Routing深入詳解

  首先,ASP.Net MVC項目是URL請求驅動的,為什么訪問localhost/home/index會傳遞給HomeController中名為index的action(即HomeController類中的index方法)?下面,我們一一來看下。

3.1 Routing的作用

  假如有一個請求:localhost/home/index,那么路由需要做的事情如下:

  (1)確定Controller

    (2)確定Action

  (3)確定其他參數

  (4)根據識別出來的數據,將請求傳遞給Controller和Action

3.2 神奇的路由規則

  根據路由的作用,我們可以知道它是一個“指路人”,指示我們的請求應該到達哪個Controller中的Action。那么,它是根據什么規則來指路的呢?我們可以在App_Start文件夾中的RouteConfig類中找到這個神奇的規則是如何制定的。

public static void RegisterRoutes(RouteCollection routes)
{
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
      );
}

  (1)首先,第一句routes.IgnoreRoute代表對所有axd的資源訪問請求進行忽略,直接進行URL訪問;這里可以閱讀參考資料第(5)篇,了解其詳細含義,這里就不再贅述;

  (2)然后,第二句開始使用MapRoute方法對整個網站定義了一個路由識別規則,這個規則的name是Default,url規則為:{controller}/{action}/{id}。例如我們要訪問的URL為:localhost/home/index,在這個URL中,localhost是域名, 所以首先要去掉域名部分: home/index,也是就對應了上面代碼中的這種URL結構: {controller}/{action}/{id}。正是因為我們建立了這種URL結構的識別規則,,所以能夠識別出 Controller是home, action是index, id沒有則為默認值""。

  (3)在MapRoute方法中為所有URL請求定義了一個defaults默認值:controller為空則指向Home,action為空則指向Index,而id則是可選的,非必須要的。

  這里,對於路由規則需要注意的有兩點:

  (1)可以有多條路由規則;

  (2)路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配);

  我們可以在RegisterRoutes這個方法中添加一條自定義路由規則,並取名為Default2,具體規則代碼如下:

routes.MapRoute(
     name: "Default2",
     url: "{controller}-{action}-{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

  這下如果我們以:localhost/Home-Index來訪問時,我們原本想要的是根據Default2這個路由規則訪問Home控制器下的Index這個Action,但卻被告知以404提示:

  這是為什么呢?我們再來看看RegisterRoutes這個方法:

public static void RegisterRoutes(RouteCollection routes)
{
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                name: "Default2",
                url: "{controller}-{action}-{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
}

  我們剛剛提到,路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配)。那么,可以推斷,由於Default2在Default之后,有可能我們的請求localhost/Home-Index已經被Default這個規則所匹配了,因此Default2規則根本沒有出場Show一下。那么,在Default規則中,它將Home-Index作為Controller的名字匹配,去訪問Home-Index這個Controller,而Action使用默認的Index,那么它所請求的應該是這個URL:/localhost/Home-Index/Index。由於網站中,並沒有Home-Index這個Controller,所以也就出現了剛剛那個404頁面。

3.3 MapRoute方法介紹

  (1)MapRoute方法提供了以下幾種方式的重載

  MapRoute( string name, string url);
  MapRoute( string name, string url, object defaults);
  MapRoute( string name, string url, string[] namespaces);
  MapRoute( string name, string url, object defaults, object constraints); MapRoute( string name, string url, object defaults, string[] namespaces);
  MapRoute( string name, string url, object defaults, object constraints, string[] namespaces);

  我們在上面所使用的便是第二種重載。

  (2)MapRoute方法參數詳細介紹:

  ①name參數:

  規則名稱, 可以隨意起名。不可以重名,否則會發生錯誤: “路由集合中已經存在名為“Default”的路由。路由名必須是唯一的”。

  ②url參數:

  url獲取數據的規則,這里不是正則表達式,將要識別的參數括起來即可,比如: {controller}/{action}

  最少只需要傳遞name和url參數就可以建立一條Routing(路由)規則,比如實例中的規則完全可以改為:

  routes.MapRoute( "Default", "{controller}/{action}");

  ③ defaults參數:
  url參數的默認值:如果一個url只有controller: localhost/home/,而且我們只建立了一條url獲取數據規則: {controller}/{action},那么這時就會為action參數設置defaults參數中規定的默認值。由於defaults參數是Object類型,所以可以傳遞一個匿名類型來初始化默認值:new { controller = "Home", action = "Index" }。
  在ASP.Net MVC網站默認實例中使用的是三個參數的MapRoute方法:
routes.MapRoute(
     name: "Default",
     url: "{controller}/{action}/{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
  ④ constraints參數: 
  用來限定每個參數的規則或Http請求的類型。constraints屬性是一個RouteValueDictionary對象,也就是一個字典表,但是這個字典表的值可以有兩種類型:
  一是:用於定義 正則表達式的字符串(正則表達式不區分大小寫)。通過使用正則表達式可以規定參數格式,比如controller參數只能為4位數字:new { controller = @"\d{4}"}
routes.MapRoute(
     name: "Default2",
     url: "{controller}-{action}-{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
     constraints: new { controller = @"\d{4}" }
);
 
  二是:一個用於實現 IRouteConstraint 接口且包含Match方法的對象。

  例如:通過第IRouteConstraint 接口可以限制請求的類型(是GET還是POST)。因為System.Web.Routing中提供了HttpMethodConstraint類,,這個類實現了IRouteConstraint 接口。

  我們可以通過為RouteValueDictionary字典對象添加鍵為"httpMethod", 值為一個HttpMethodConstraint對象來為路由規則添加HTTP 謂詞的限制,比如限制一條路由規則只能處理GET請求:httpMethod = new HttpMethodConstraint( "GET" )

routes.MapRoute(
     name: "Default2",
     url: "{controller}-{action}-{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
     constraints: new { 
           controller = @"\d{4}", 
           httpMethod = new HttpMethodConstraint("GET") }
);

3.4 URL路由實例詳解

  一般來說,對於一個網站為了SEO友好,網址的URL層次最好不要超過三層:localhost/{頻道}/{具體網頁},其中域名第一層, 頻道第二層, 那么最后的網頁就只剩下最后一層了。如果使用默認實例中的“{controller}/{action}/{其他參數}"的形式則會影響網站的SEO。

  假設我們有一個綜合型服務網站,其中有租房頻道、酒店頻道、KTV頻道、電影院頻道等等。我們應該怎樣來設計URL路由規則呢?

  (1)首先,我們知道:可以有多條路由規則,但是路由規則是有順序的(前面的規則被匹配后,后面的規則就不再匹配);所以,我們可以定義多條路由規則,粒度細的模塊(比如:具體的酒店列表頁面)路由規則放最前面,粒度粗的模塊(比如:門戶網站的首頁)路由規則放在最后面。

  (2)其次,根據模塊粒度划分層次結構,以粒度粗細排序為:網站首頁->頻道首頁->具體內容;

  (3)最后,我們可以看一個具體的URL路由實例來分析一下:

// 酒店列表頁匹配
routes.MapRoute(
     "酒店列表頁",
     "hotels/{action}-{city}-{price}-{star}",
     new { controller = "Hotel", action = "list", city = "beijing", price = "-1,-1", star = "-1" },
     new { city = @"[a-zA-Z]*", price = @"(\d)+\,(\d)+", star = "[-1-5]" }
);

// 酒店頻道所有匹配 
routes.MapRoute(
     "酒店首頁",
     "hotels/{*iiii}",
     new { controller = "Hotel", action = "default", hotelid = "" }
);
            
// 網站首頁默認匹配
routes.MapRoute(
     "網站首頁",
     "{*values}",
     new { controller = "Home", action = "index" }
);

  (4)我們可以分析一下上面的路由規則所實現的功能:

   ①訪問 www.mywebsite.com/hotels/list-chengdu-100,200-3 會訪問酒店頻道的列表頁,並傳入查詢參數(price為100,200,star為3);

   ②訪問 www.mywebsite.com/hotels 下面的任何其他頁面地址,都會跳轉到酒店首頁;

   ③訪問 www.mywebsite.com 下面的任何地址,如果未匹配上面2條,則跳轉到首頁;

  (5)根據上面的規則和實現的功能,我們可以做一個簡單的總結如下:

   ①Routing規則有順序(按照添加是的順序),如果一個url匹配了多個路由規則,則按照第一個匹配的路由規則執行

   ②由於上面的規則,要將具體頻道的具體頁面放在最上方,將頻道首頁 和 網站首頁 放在最下方。

   ③{*values}表示后面可以使用任意的格式。

3.5 URL路由調試

  在ASP.Net MVC中,默認是不允許對路由規則進行調試的。但是,我們可以通過使用RouteDebug來輔助進行調試。

  (1)首先,我們下載RouteDebug.dll到我們的項目中,並添加對其的引用。

  (2)其次,在Global.asax中的Application_Start方法中添加一句代碼:

  RouteDeug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes); 

protected void Application_Start()
{
     AreaRegistration.RegisterAllAreas();

     WebApiConfig.Register(GlobalConfiguration.Configuration);
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
     RouteConfig.RegisterRoutes(RouteTable.Routes);
     BundleConfig.RegisterBundles(BundleTable.Bundles);
     RouteDeug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}

  (3)最后,F5調試運行,我們請求localhost/Home-Index這個URL時,可以清楚地發現,系統將Home-Index匹配了第一條默認路由規則,也就是將Home-Index作為Controller的名稱進行匹配,這也就證明了為什么我們輸入這個請求不會匹配第二條Default2的路由規則出現剛剛那個404頁面了。

參考資料

  (1)馬倫,《ASP.Net MVC視頻教程》,http://bbs.itcast.cn/thread-26722-1-1.html

  (2)葡萄城控件技術團隊,《ASP.NET MVC 5—控制器》,http://www.cnblogs.com/powertoolsteam/p/aspnet-mvc5-controller.html

  (3)李亮,《ASP.Net MVC3 Controller》,http://www.cnblogs.com/wlitsoft/archive/2012/05/28/2520799.html

  (4)顧里江,《ActionResult解析》,http://blog.csdn.net/gulijiang2008/article/details/7642213

  (5)紫魚Tiler,《MVC路由中routes.IgnoreRoute》,http://www.cnblogs.com/flyfish2012/archive/2013/02/01/2889184.html

  (6)Capricornus,《路由匹配檢測組件RouteDebug.dll》,http://www.cnblogs.com/Capricornus/archive/2010/08/26/1808907.html

 


免責聲明!

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



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