原文:Razor Syntax Reference
作者:Taylor Mullen、Rick Anderson
翻譯:劉怡(AlexLEWIS)
校對:何鎮汐
什么是 Razor?
Razor 是一種基於服務器端代碼的可以轉換為網頁的標記語法。Razor 語法包括 Razor 標記、C# 和 HTML 組成。包含 Razor 的文件通常后綴名為 .cshtml 。
渲染 HTML
Razor 的默認語言是 HTML。從 Razor 渲染為 HTML 和直接一個 HTML 文件沒啥區別,這種 Razor 文件包含下面的標記:
<p>Hello World</p>
服務器最后渲染出的頁面也是 <p>Hello World</p>,沒有任何改變。
Razor 語法
Razor 支持 C# 並通過使用 @ 符號從 HTML 切換到 C#。Razor 運算 C# 表達式並將之渲染為 HTML 輸出。Razor 能通過 Razor 指定的標記從 HTML 切換到 C#。當 @ 符號后面緊跟一個 Razor 保留字 ,則將切換為 Razor 特定標記,不然的話切換到普通的 C#。
HTML 如果需要包含 @ 符號的話需要使用兩個 @@ 符號來進行轉義,比如:
<p>@@Username</p>
這樣將渲染成下面的 HTML:
<p>@Username</p>
這樣就不會因為在 HTML 特性或內容中包含郵件地址而誤將 @ 處理為轉換字符(進而切換到 Razor 指定標記或 C# 模式)。
<a href="mailto:Support@contoso.com">Support@contoso.com</a>
隱式 Razor 表達式
隱式 Razor 表達式起於 @ 符號,后面緊跟 C# 代碼,比如:
<p>@DateTime.Now</p>
<p>@DateTime.IsLeapYear(2016)</p>
除 C# 關鍵字 await 以外的隱式表達式都不能包含空格。比如你可以在 C# 語句中混進一些空格,只要 C# 語句的結尾明確:
<p>@await DoSomething("hello", "world")</p>
顯式 Razor 表達式
顯式 Razor 表達式包含一個帶一對括號的 @ 符號,比如在頁面上渲染上周的時間:
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
任何在 @() 內的內容都會被運算並渲染輸出。
隱式表達式通常不能包含空格,比如下面這段代碼,上周的時間並不能通過減去當前時間來獲得:
<p>Last week: @DateTime.Now - TimeSpan.FromDays(7)</p>
這將會被渲染為:
<p>Last week: 7/7/2016 4:39:52 PM - TimeSpan.FromDays(7)</p>
不過你可以使用顯式表達式在表達式結果中把這段文本連接起來:
@{
var joe = new Person("Joe", 33);
}
<p>Age@(joe.Age)</p>
如果寫成 <p>Age@joe.Age</p> 這種非顯式表達式,那么它將當做郵件地址來處理並渲染為 <p>Age@joe.Age</p>。當寫成顯式表達式時,將渲染為 <p>Age33</p> 。
Expression 編碼
C# 表達式計算后的字符串是 HTML 編碼的。C# 表達式的計算結果為 IHtmlContent,將直接通過 IHtmlContent.WriteTo 渲染到頁面。不會計算為 IHtmlContent 的 C# 表達式將會轉換為字符串(通過 ToString)並在渲染前編碼。比方說下面這段 Razor 標記:
@("<span>Hello World</span>")
將渲染為這段 HTML:
<span>Hello World</span>
而瀏覽器將顯示為:
<span>Hello World</span>
HtmlHelper Raw 的輸出不會被編碼但會被渲染為 HTML 標記。
警告
為未經認可的用戶輸入使用HtmlHelper.Raw是存在安全風險的。用戶輸入可能會包含惡意的 JavaScript 代碼或其他攻擊。為用戶輸入的信息進行過濾和清理是非常困難的,所以盡量避免為用戶輸入使用HtmlHelper.Raw。
下面這段 Razor 標記:
@Html.Raw("<span>Hello World</span>")
將渲染為:
<span>Hello World</span>
Razor 代碼塊
Razor 代碼塊起於 @ 並用 {} 包圍起來。不像表達式,代碼塊內的 C# 代碼不會被渲染到頁面中。Razor 頁面中的代碼塊和表達式將共享同一個作用域,並按順序定義(也就是說,之前在代碼塊中聲明的對象可以在之后的代碼塊與表達式中使用)。
@{
var output = "Hello World";
}
<p>The rendered result: @output</p>
將渲染為:
<p>The rendered result: Hello World</p>
隱式轉換
代碼塊的默認語言是 C#,但你可以隨時切換到 HTML。代碼塊內的 HTML 可以正確渲染。
@{
var inCSharp = true;
<p>Now in HTML, was in C# @inCSharp</p>
}
顯式分隔轉換
為了在代碼塊中定義可渲染 HTML 的子區域,應在需要渲染的字符周圍用 Razor <text> 標簽環繞:
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
<text>Name: @person.Name</text>
}
當你需要渲染一段不包含 HTML 標簽的 HTML 內容時可以試試這種辦法。不過如果既不包含 HTML 標簽也不包含 Razor 標簽的話,你的 Razor 頁面會在運行時出錯。
以 @: 符號顯式行轉換
為了將 HTML 內嵌到代碼塊中(以便能渲染出來),可以使用 @: 語法:
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
@:Name: @person.Name
}
如果上面代碼不使用 @: ,你的 Razor 頁面會在運行時出錯。
控制結構
控制結構(controller structures)是代碼塊表達式。所有類型的代碼塊(包括過渡標記、內聯式C#)都適用以下結構:
@if、else if、else 與 @switch 條件
當 @if 滿足指定條件時,@if 系列關鍵詞將獲得控制權並運行 if 內的代碼:
@if (value % 2 == 0)
{
<p>The value was even</p>
}
else 和 else if 並不一定需要 @ 符號:
@if (value % 2 == 0)
{
<p>The value was even</p>
}
else if (value >= 1337)
{
<p>The value is large.</p>
}
else
{
<p>The value was not large and is odd.</p>
}
你可以使用 switch 語句,就像這樣:
@switch (value)
{
case 1:
<p>The value is 1!</p>
break;
case 1337:
<p>Your number is 1337!</p>
break;
default:
<p>Your number was not 1 or 1337.</p>
break;
}
@for、@foreach、@while 與 @do while 循環
你可以使用循環控制語句渲染出經過排版的 HTML,比如人名表:
@{
var people = new Person[]
{
new Person("John", 33),
new Person("Doe", 41),
};
}
你可以使用下面任意一種循環語句:
@for
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
@foreach
@foreach (var person in people)
{
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
@while
@{ var i = 0; }
@while (i < people.Length)
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
i++;
}
@do while
@{ var i = 0; }
@do
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
i++;
} while (i < people.Length);
@using 復合
在 C# 中 using 語句用於確保對象被正確釋放。在 Razor 中這一相同機制被用於創建包含額外內容的 HTML helpers 。比如我們可以利用 HTML helpers ,通過 @using 語句渲染 form 標簽:
@using (Html.BeginForm())
{
<div>
email:
<input type="email" id="Email" name="Email" value="" />
<button type="submit"> Register </button>
</div>
}
你也可以在作用級別上執行一些類似上面這樣的帶 Tag Helpers 的操作。
@try、catch 與 finally
異常處理和 C# 十分類似:
@try
{
throw new InvalidOperationException("You did something invalid.");
}
catch (Exception ex)
{
<p>The exception message: @ex.Message</p>
}
finally
{
<p>The finally statement.</p>
}
@lock
Razor 能通過 lock 語句保護重要代碼:
@lock (SomeLock)
{
// Do critical section work
}
注釋
Razor 支持 C# 和 HTML 注釋。比如下面的標記:
@{
/* C# comment. */
// Another C# comment.
}
<!-- HTML comment -->
被服務器渲染為:
<!-- HTML comment -->
Razor 注釋將在頁面渲染之前被服務器移除。Razor 使用 @* *@ 來界定注釋。下面這段代碼就被注釋掉了,因此服務器不會渲染出任何標記:
@*
@{
/* C# comment. */
// Another C# comment.
}
<!-- HTML comment -->
*@
指令
Razor 指令表現為「@ 符號 + 保留關鍵字」的隱式表達式。指令通常能改變頁面的解析或為 Razor 頁面啟用不同的功能。
理解 Razor 如何為視圖生成代碼后,就能輕松理解指令是如何工作的。Razor 頁面用於創建 C# 文件,比如這樣一個 Razor 頁面:
@{
var output = "Hello World";
}
<div>Output: @output</div>
將生成一個類似下面這樣的類:
public class _Views_Something_cshtml : RazorPage<dynamic>
{
public override async Task ExecuteAsync()
{
var output = "Hello World";
WriteLiteral("/r/n<div>Output: ");
Write(output);
WriteLiteral("</div>");
}
}
查看生成視圖的 Razor C# 類解釋了如何查看這段自動生成的類。
@using
@using 指令將為 razor 頁面增加 C# 的 using 指令。
@using System.IO
@{
var dir = Directory.GetCurrentDirectory();
}
<p>@dir</p>
@model
@model 指令讓你可以為傳入 Razor 頁面的模型指定類型,其語法為:
@model TypeNameOfModel
比方說,如果你創建了一個帶身份驗證的 ASP.NET Core MVC 應用,你可以在 Views/Account/Login.cshtml Razor 視圖文件中看到包含如下這段模型聲明:
@model LoginViewModel
在 指令 樣例類中,自動生成的類從 RazorPage<dynamic> 繼承。通過添加 @model,你可以控制繼承什么,比如:
@model LoginViewModel
將生成下面這個類
public class _Views_Account_Login_cshtml : RazorPage<LoginViewModel>
Razor 頁面將暴露一個 Model 屬性給傳入頁面的模型訪問。
<div>The Login Email: @Model.Email</div>
@model 指令能為這個屬性指定類型(通過為自動生成的類 RazorPage<T> 中的 T 指定類型)。如果你沒有指定 @model 指令,那么 Model 屬性將使用類型 dynamic 。模型的值將從控制器傳入視圖。更多請查閱 Strongly typed models and the @model keyword 。
@inherits
@inherits 指令讓你具有 Razor 頁面所繼承類的完整控制權:
@inherits TypeNameOfClassToInheritFrom
例如讓我們來看下面這個自定義的 Razor 頁面類型:
using Microsoft.AspNetCore.Mvc.Razor;
public abstract class CustomRazorPage<TModel> : RazorPage<TModel>
{
public string CustomText { get; } = "Hello World.";
}
隨后 Razor 將生成 <div>Custom text: Hello World</div> 。
@inherits CustomRazorPage<TModel>
<div>Custom text: @CustomText</div>
你不能在同一個頁面中同時使用 @model 和 @inherits。你可以在 _ViewImports.cshtml 文件中使用 @inherits 指令,然后在其他 Razor 頁面中導入。舉例來說,如果你的 Razor 視圖導入了下面這個 _ViewImports.cshtml 文件:
@inherits CustomRazorPage<TModel>
那么在下面這個強類型 Razor 文件
@inherits CustomRazorPage<TModel>
<div>The Login Email: @Model.Email</div>
<div>Custom text: @CustomText</div>
將生成這么一段 HTML 標記:
<div>The Login Email: Rick@contoso.com</div>
<div>Custom text: Hello World</div>
此時模型中被傳入了「Rick@contoso.com」。
更多信息請查看 布局視圖
@inject
@inject 指令可讓你在 Razor 頁面中從 服務容器 注入服務,更多請查看 See Injecting Services Into Views 。
@functions
@functions 指令讓你能在 Razor 頁面中添加函數級別的內容,其語法為:
@functions { // C# Code }
例如:
@functions {
public string GetHello()
{
return "Hello";
}
}
<div>From method: @GetHello()</div>
生成如下 HTML 標記:
<div>From method: Hello</div>
生成的 Razor C# 類似下面這段:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor;
public class _Views_Home_Test_cshtml : RazorPage<dynamic>
{
// Functions placed between here
public string GetHello()
{
return "Hello";
}
// And here.
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
WriteLiteral("\r\n<div>From method: ");
Write(GetHello());
WriteLiteral("</div>\r\n");
}
#pragma warning restore 1998
@section
@section 指令通常與 布局頁 一起使用,這樣可以使視圖所渲染的 HTML 頁面能具有不同的內容。更多請查看 Sections 。
TagHelpers
下列 Tag Helpers 指令的詳細信息可以點擊鏈接查看。
Razor 保留關鍵字
Razor 關鍵字
- functions
- inherits
- model
- section
- helper (ASP.NET Core 不支持)
Razor 關鍵字可以轉義,形如 @(Razor Keyword) ,舉一個例子:@(functions) 。上面是完整舉例。
C# Razor 關鍵字
- case
- do
- default
- for
- foreach
- if
- lock
- switch
- try
- using
- while
C# Razor 關鍵字需要使用兩個轉義符號,形如 @(@C# Razor Keyword),舉一個實際例子:@(@case) 。第一個 @ 轉義符用於 Razor 解析,第二個 @ 轉義符用於 C# 解析。上面是完整舉例。
Razor 未使用的保留關鍵字
- namespace
- class
下列是所有 Razor 保留字的轉義:
@{
// Razor keywords.
var @functions = "functions";
var @inherits = "inherits";
var @model = "model";
var @section = "section";
var @helper = "helper"; // Not supported by ASP.NET Core.
// C# Razor keywords.
var @case = "case";
var @do = "do";
var @default = "default";
var @for = "for";
var @foreach = "foreach";
var @if = "if";
var @lock = "lock";
var @switch = "switch";
var @try = "try";
var @using = "using";
var @while = "while";
// Reserved keywords not used.
var @namespace = "namespace";
var @class = "class";
}
<p>Razor keywords.</p>
<div>@(functions)</div>
<div>@(inherits)</div>
<div>@(model)</div>
<div>@(section)</div>
<div>@(helper)</div>
<p>C# Razor keywords.</p>
<div>@(@case)</div>
<div>@(@do)</div>
<div>@(@default)</div>
<div>@(@for)</div>
<div>@(@foreach)</div>
<div>@(@if)</div>
<div>@(@lock)</div>
<div>@(@switch)</div>
<div>@(@try)</div>
<div>@(@using)</div>
<div>@(@while)</div>
<p>Reserved keywords not used</p>
<div>@(@namespace)</div>
<div>@(@class)</div>
查看生成視圖的 Razor C# 類
在 ASP.NET Core MVC 項目中增加下面這個類:
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
public class CustomCompilationService : DefaultRoslynCompilationService, ICompilationService
{
public CustomCompilationService(ApplicationPartManager partManager,
IOptions<RazorViewEngineOptions> optionsAccessor,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
: base(partManager, optionsAccessor, fileProviderAccessor, loggerFactory)
{
}
CompilationResult ICompilationService.Compile(RelativeFileInfo fileInfo,
string compilationContent)
{
return base.Compile(fileInfo, compilationContent);
}
}
通過為 MVC 加上上面這個類來覆蓋 ICompilationService :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ICompilationService, CustomCompilationService>();
}
在 Compile 方法的 CustomCompilationService 上、在視圖compilationContent 上設斷點。

