4.2 Razor视图引擎
4.2.1 什么是Razor
Razor是ASP.NET MVC 3.0出现的新的视图引擎,为视图提供精简的语法,最大限度减少了语法和额外字符串。Razor不是编程语言,而是服务器端标记语言,是一种允许用户向网页中嵌入基于服务器代码(Visual Basic和C#)的标记语法。
语境A:在前台声明和使用C#变量
@{ string schoolName="湖南第一师范"; } <span>@schoolName.Models</span> @*错误*@ <span>@(schoolName).Models</span> <span>我毕业于 @schoolName信息系</span>
说明:声明C#变量的代码必须写在@{ }代码块中,使用@+C#变量名就能取得C#变量的值,注意前后必须有空格,否则会被当作普通字符输出。
语境B:邮件格式中的@符号
<span>zouqiongjun@uuch.net</span>@*通过,因为@前面没有空格*@ <span>zouqiongjun @uuch.net</span>@*不通过,因为@前面有空格*@
4.2.2 Razor语法
C#的主要Razor语法规则如下:
● Razor代码封装于@{ ... }中。
● 行内表达式(变量和函数)以@开头。
● 代码语句以分号结尾。
● 字符串由引号包围。
● C#代码对大小写敏感。
● C#文件的扩展名是.cshtml。
● Razor通过理解标记的结构来实现代码和标记之间的顺畅切换。
● @核心转换字符,用来标记代码的转换字符串。
● Razor表达式自动使用了HTML编码。
如果想向浏览器输出HTML源代码,那么使用System.Web.IHtmlString: @Html.Raw("<b>zouyujie</b>")会输出结果zouyujie。
查看HTML源码:
<b>zouyujie</b>
JS字符串编码:
<script> alert('@Ajax.JavaScriptStringEncode("小李飞刀")'); </script>
运行结果如图4-2所示。
图4-2
@代码块:
@{ string s ="zouyujie"; int age =27; } @{Html.RenderPartial("TestPartial"); }//调用无返回值方法
注释:
@* ............*@
调用泛型方法:
@(Html.SomeMethod<User>());
混合代码与文本:
@if(1==1){ <text>我要输出文本在这里!</text> @:我要输出文本在这里! }
说明:当前台的C#代码有多行时,如果这些代码是连续的,中间没有HTML代码隔断,那么只需要开头的一个@符号即可;否则HTML代码之后被隔断的C#代码开头需要加上@符号。
@转义:
@@
操作Web内置对象:
@Request.RawUrl @Response.Write
@作用域与HTML标记混合使用:
@{ string userName="刘邦"; <p>@userName</p> }
在@作用域中输出未转义的HTML代码。
(1)使用字符串描述输出:
@{ string strHtm="<p>你好~</p>"; @strHtml }
(2)使用HTMLHelper输出:
@{ @Html.Raw("<p>哇哈哈哈~</p>"); }
(3)使用HtmlString类输出:
@{ HtmlString htm =new HtmlString("<p>哈哈</p>"); @htm }
(4)使用MvcHtmlString输出:
@{ var strHtml=MvcHtmlString.Create("<p>哈哈~</p>"); @strHtml }
数据类型转换:用As....()方法转换,例如
@("120".AsInt())
数值类型判断:用IsInt()方法,例如
@(strAge.IsInt()? "是":"否")
路径转换用Href()方法,例如
@Href("~/Home/Index");
using System.Web.WebPages; //内部为string扩展了很多As..方法
HtmlHelper重用:相当于是在视图中定义方法。
@helper List(List<string> dogs){ <ul> @foreach (string s in dogs) { <li>@s</li> } </ul> } @List(new List<string>(){"ruiky", "lisa", "lucy"})
运行效果如图4-3所示。
图4-3
4.2.3 Razor布局——整体视图模板
这个整体视图模板就和以前WebForm中的Mater母版页一样。
View模板显示页面的规则:先找对应的Controller文件夹,再找对应的Shared文件夹。
为演示应用整体模板视图,在网站根目录下的Views/Shared目录新建视图SiteLayout,代码如下:
@{ ViewBag.Title = "我是模板页"; } <body> <h2>欢迎光临</h2> @RenderBody() @*模板页里的占位符*@ </body>
然后在RazorTest控制器中新建Action方法ViewTemplate,添加视图ViewTemplate:
@{ Layout = "~/Views/Shared/SiteLayout.cshtml"; ViewBag.Title = "我是子页面"; } <h2>今天天气不错</h2>
在浏览器中输入地址“http://localhost:3291/RazorTest/ViewTemplate”,效果如图4-4所示。
图4-4
我们会发现子页所有HTML代码都将替换到模板页的@RenderBody()处应用整体视图模板,我们还可以设置多个“占位符”。
要在模板页设置多个节,可在模板页SiteLayout.cshtml添加如下代码:
<footer> @if(IsSectionDefined("Footer")){ @RenderSection("Footer"); }else { <b>子页面没有设置Footer节点</b> } </footer>
在子页面ViewTemplate.cshtml定义节点:
@section Footer{ <b>@@ 超级牛逼公司所有</b> }
运行结果如图4-5所示。
图4-5
4.2.4 Razor布局——ViewStart
每个子页面都使用一个Layout指定布局。如果多个视图都用同一个布局就会产生冗余,修改维护麻烦。
_ViewStart.cshtml可解决此问题,此文件代码优先于同目录及子目录下的任何视图代码。执行View目录下自动添加的_ViewStart.cshtml:
@{ Layout = "~/Views/Shared/_Layout.cshtml"; }
有了它,就可以为某个文件夹下所有的视图添加相同的视图布局了。
因为这个文件代码优先于任何视图,所以任何一个视图都可以重写Layout属性来指定自己想要的模板布局页面。
4.2.5 Razor布局——部分视图
ASP.NETMVC里的部分视图相当于WebForm里的User Control。我们的页面往往会有许多重用的地方,可以进行封装重用。使用部分视图的好处是既可以简写代码,又可以使页面代码更加清晰、更好维护。
在视图里有多种方法可以加载部分视图,包括Partial()、Action()、RenderPartial()、RenderAction()、RenderPage()方法。
下面说明一下这些方法的差别。
1. Partial与RenderPartial方法
(1)Razor语法:@Html.Partial()与@{Html.RenderPartial();}。
(2)区别:Partial可以直接输出内容,在内部将html内容转换为String字符(MVCHtmlString),然后缓存起来,最后一次性输出到页面。显然,这个转换过程会降低效率,所以通常使用RenderPartial代替。
2. RenderPartial与RenderAction方法
(1)Razor语法:@{Html.RenderPartial(); }与@{Html.RenderAction(); }。
(2)区别:RenderPartial不需要创建Controller的Action,而RenderAction需要在Controller中创建要加载的Action。
RenderAction会先去调用Contorller的Action再呈现视图,所以这里会再发起一个链接。
如果这个部分视图只是一些简单的HTML代码,请使用RenderPartial。如果这个部分视图除了有HTML代码外,还需要通过读取数据库里的数据来渲染,就必须使用RenderAction了,因为它可以在Action里调用Model里的方法读取数据库,渲染视图后再呈现,而RenderPartial没有Action,所以无法做到。
3. RenderAction与Action
(1)Razor语法:@{Html.RenderAction(); }与@Html.Action();。
(2)区别:Action也是直接输出,和Partial一样,也存在一个转换的过程,但是不如RenderAction直接输出到当前HttpContext的效率高。
4. RenderPage与RenderPartial方法
(1)Razor语法:@{Html.RenderPartial(); }与@RenderPage()。
(2)区别:也可以使用RenderPage来呈现部分,但它不能使用原来视图的Model和ViewData,只能通过参数来传递;而RenderPartial可以使用原来视图的Model和ViewData。
Action方法可以通过PartialView方法以PartialViewResult形式返回部分视图。部分视图一般用在Ajax请求部分代码中。
在RazorTest控制器中添加Action方法PartialViewTest:
public ActionResult PartialViewTest() { ViewData["Msg"] = "Hello world! "; return PartialView(); }
然后添加PartialViewTest视图,PartialViewTest.cshtml代码如下:
<div>@ViewData["Msg"] </div>
最后在ViewTemplate.cshtml视图中添加如下代码:
<div id="divTest"> @{Html.RenderAction("PartialViewTest"); } </div>
生成项目,运行结果如图4-6所示。
图4-6
4.2.6 视图引擎
视图引擎仅仅是一个尖括号生成器而已,如图4-7所示。
图4-7
图4-7仅仅为了强调如下两点:
(1)视图引擎发挥作用的地方紧跟在Action方法执行后,目的是获取从控制器传递的数据,并生成经过格式化的输出。
(2)控制器并不渲染视图,它仅仅准备数据(Model)并通过ViewResult实例来决定调用哪个视图。
视图引擎接口IViewEngine:
public interface IViewEngine { ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache); ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache); void ReleaseView(ControllerContext controllerContext, IView view); }
ViewEngineResult属性如表4-1所示。
表4-1 ViewEngineResult属性表
其他视图引擎:Spart、NHaml、Brail、StringTemplate、NVelocity。
视图IView:
public interface IView { void Render(ViewContext viewContext, TextWriter writer); }
ViewPage类的属性如表4-2所示。
表4-2 ViewPage类属性表
4.2.7 MVC视图的“秘密”
其实我们的cshtml视图页面在被访问的时候也编译成了页面类,继承于WebViewPage<T>。
在View页面ViewTemplate中添加代码:
<div>@{Response.Write(this.GetType().Assembly.Location); }</div>
运行结果(不同机器运行结果会不一致)如下:
C:\Users\zouqi\AppData\Local\Temp\Temporary ASP.NET Files\vs\11bac735\53d0ffb7\ App_Web_b5yz5bvn.dll
用reflector反编译工具查看这个dll:
namespace ASP { [Dynamic(new bool[] { false, true })] public class _Page_Views_razortest_Index_cshtml : WebViewPage<object> [Dynamic(new bool[] { false, true })] public class _Page_Views_razortest_OutputTest_cshtml : WebViewPage<object> [Dynamic(new bool[] { false, true })] public class _Page_Views_razortest_PartialViewTest_cshtml : WebViewPage<object> [Dynamic(new bool[] { false, true })] public class _Page_Views_razortest_ViewTemplate_cshtml : WebViewPage<object> }
可以看到cshtml页面里的所有代码都编译到这个类的Execute方法里了。这里以最后一个类为例:
public override void Execute() { ((dynamic) base.ViewBag).Title = "OutputTest"; base.BeginContext("~/Views/razortest/OutputTest.cshtml", 40, 2, true); this.WriteLiteral("\r\n"); base.EndContext("~/Views/razortest/OutputTest.cshtml", 40, 2, true); HtmlString str = new HtmlString("<p>哈哈</p>"); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0x66, 3, false); this.Write(str); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0x66, 3, false); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0x6c, 2, true); this.WriteLiteral("\r\n"); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0x6c, 2, true); MvcHtmlString str2 = MvcHtmlString.Create("<p>哈哈~</p>"); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xae, 7, false); this.Write(str2); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xae, 7, false); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xb8, 2, true); this.WriteLiteral("\r\n"); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xb8, 2, true); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xc3, 0x18, false); this.Write(base.Html.Raw("<p>哇哈哈哈~</p>")); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xc3, 0x18, false); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xdf, 2, true); this.WriteLiteral("\r\n"); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xdf, 2, true); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xea, 0x10, false); this.Write(base.ViewData["Text"]); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xea, 0x10, false); base.BeginContext("~/Views/razortest/OutputTest.cshtml", 0xfd, 4, true); this.WriteLiteral("\r\n\r\n"); base.EndContext("~/Views/razortest/OutputTest.cshtml", 0xfd, 4, true); }