ASP.NET MVC企业级实战
上QQ阅读APP看书,第一时间看更新

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);
}