作者:Rick Anderson、Luke Latham、Taylor Mullen 和 Dan Vicarel
Razor 是一种标记语法,用于将基于服务器的代码嵌入网页中。 Razor 语法由 Razor 标记、C# 和 HTML 组成。 包含 Razor 的文件通常具有 .cshtml 文件扩展名。 在 Razor 组件文件 (.razor) 中也可以找到 Razor 。
默认 Razor 语言为 HTML。 从 Razor 标记呈现 HTML 与从 HTML 文件呈现 HTML 并没有什么不同。 服务器会按原样呈现 .cshtml Razor 文件中的 HTML 标记。
Razor 支持 C#,并使用 @
符号从 HTML 转换为 C#。 Razor 计算 C# 表达式,并将它们呈现在 HTML 输出中。
当 @
符号后跟 Razor 保留关键字时,它会转换为 Razor 特定标记。 否则会转换为纯 C#。
若要对 Razor 标记中的 @
符号进行转义,请使用另一个 @
符号:
<p>@@Username</p>
该代码在 HTML 中使用单个 @
符号呈现:
<p>@Username</p>
包含电子邮件地址的 HTML 属性和内容不将 @
符号视为转换字符。 Razor 分析不会处理以下示例中的电子邮件地址:
<a href="mailto:Support@contoso.com">Support@contoso.com</a>
隐式 Razor 表达式以 @
开头,后跟 C# 代码:
<p>@DateTime.Now</p> <p>@DateTime.IsLeapYear(2016)</p>
隐式表达式不能包含空格,但 C# await
关键字除外。 如果该 C# 语句具有明确的结束标记,则可以混用空格:
<p>@await DoSomething("hello", "world")</p>
隐式表达式不能包含 C# 泛型,因为括号 (<>
) 内的字符会被解释为 HTML 标记。 以下代码无效:
<p>@GenericMethod<int>()</p>
上述代码生成与以下错误之一类似的编译器错误:
泛型方法调用必须包装在显式 Razor 表达式或 Razor 代码块中。
显式 Razor 表达式由 @
符号和平衡圆括号组成。 若要呈现上一周的时间,可使用以下 Razor 标记:
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
将计算 @()
括号中的所有内容,并将其呈现到输出中。
前面部分中所述的隐式表达式通常不能包含空格。 在下面的代码中,不会从当前时间减去一周:
<p>Last week: @DateTime.Now - TimeSpan.FromDays(7)</p>
该代码呈现以下 HTML:
<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>
。
显式表达式可用于从 .cshtml 文件中的泛型方法呈现输出。 以下标记显示了如何更正之前出现的由 C# 泛型的括号引起的错误。 此代码以显式表达式的形式编写:
<p>@(GenericMethod<int>())</p>
计算结果为字符串的 C# 表达式采用 HTML 编码。 计算结果为 IHtmlContent
的 C# 表达式直接通过 IHtmlContent.WriteTo
呈现。 计算结果不为 IHtmlContent
的 C# 表达式通过 ToString
转换为字符串,并在呈现前进行编码。
@("<span>Hello World</span>")
该代码呈现以下 HTML:
<span>Hello World</span>
该 HTML 在浏览器中显示为:
<span>Hello World</span>
HtmlHelper.Raw
输出不进行编码,但呈现为 HTML 标记。
警告
对未经审查的用户输入使用 HtmlHelper.Raw
会带来安全风险。 用户输入可能包含恶意的 JavaScript 或其他攻击。 审查用户输入比较困难。 应避免对用户输入使用 HtmlHelper.Raw
。
@Html.Raw("<span>Hello World</span>")
该代码呈现以下 HTML:
<span>Hello World</span>
Razor 代码块以 @
开头,并括在 {}
中。 代码块内的 C# 代码不会呈现,这点与表达式不同。 一个视图中的代码块和表达式共享相同的作用域并按顺序进行定义:
@{ var quote = "The future depends on what you do today. - Mahatma Gandhi"; } <p>@quote</p> @{ quote = "Hate cannot drive out hate, only love can do that. - Martin Luther King, Jr."; } <p>@quote</p>
该代码呈现以下 HTML:
<p>The future depends on what you do today. - Mahatma Gandhi</p> <p>Hate cannot drive out hate, only love can do that. - Martin Luther King, Jr.</p>
在代码块中,使用标记将本地函数声明为用作模板化方法:
@{ void RenderName(string name) { <p>Name: <strong>@name</strong></p> } RenderName("Mahatma Gandhi"); RenderName("Martin Luther King, Jr."); }
该代码呈现以下 HTML:
<p>Name: <strong>Mahatma Gandhi</strong></p> <p>Name: <strong>Martin Luther King, Jr.</strong></p>
代码块中的默认语言为 C#,不过,Razor 页面可以转换回 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 运行时错误。
<text>
标记可用于在呈现内容时控制空格:
<text>
标记之间的内容。<text>
标记之前或之后的空格不会显示在 HTML 输出中。要在代码块内以 HTML 形式呈现整个行的其余内容,请使用 @:
语法:
@for (var i = 0; i < people.Length; i++) { var person = people[i]; @:Name: @person.Name }
如果代码中没有 @:
,会生成 Razor 运行时错误。
Razor 文件中多余的 @
字符可能会导致代码块中后面的语句发生编译器错误。 这些编译器错误可能难以理解,因为实际错误发生在报告的错误之前。 将多个隐式/显式表达式合并到单个代码块以后,经常会发生此错误。
控制结构是对代码块的扩展。 代码块的各个方面(转换为标记、内联 C#)同样适用于以下结构:
@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 is odd and small.</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 wasn't 1 or 1337.</p> break; }
可以使用循环控制语句呈现模板化 HTML。 若要呈现一组人员:
@{ var people = new Person[] { new Person("Weston", 33), new Person("Johnathon", 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);
在 C# 中,using
语句用于确保释放对象。 在 Razor 中,可使用相同的机制来创建包含附加内容的 HTML 帮助程序。 在下面的代码中,HTML 帮助程序使用 @using
语句呈现 <form>
标记:
@using (Html.BeginForm()) { <div> Email: <input type="email" id="Email" value=""> <button>Register</button> </div> }
异常处理与 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> }
Razor 可以使用 lock 语句来保护关键节:
@lock (SomeLock) { // Do critical section work }
Razor 支持 C# 和 HTML 注释:
@{ /* C# comment */ // Another C# comment } <!-- HTML comment -->
该代码呈现以下 HTML:
<!-- HTML comment -->
在呈现网页之前,服务器会删除 Razor 注释。 Razor 使用 @* *@
来分隔注释。 以下代码已被注释禁止,因此服务器不呈现任何标记:
@* @{ /* C# comment */ // Another C# comment } <!-- HTML comment --> *@
Razor 指令由隐式表达式表示:@
符号后跟保留关键字。 指令通常用于更改视图分析方式或启用不同的功能。
通过了解 Razor 如何为视图生成代码,更易理解指令的工作原理。
@{ var quote = "Getting old ain't for wimps! - Anonymous"; } <div>Quote of the Day: @quote</div>
该代码生成与下面类似的类:
public class _Views_Something_cshtml : RazorPage<dynamic> { public override async Task ExecuteAsync() { var output = "Getting old ain't for wimps! - Anonymous"; WriteLiteral("/r/n<div>Quote of the Day: "); Write(output); WriteLiteral("</div>"); } }
本文后面的检查为视图生成的 Razor C# 类部分说明了如何查看此生成的类。
@attribute
指令将给定的属性添加到生成的页或视图的类中。 以下示例添加 [Authorize]
属性:
@attribute [Authorize]
此方案仅适用于 Razor 组件 (.razor)。
@code
块允许 Razor 组件将 C# 成员(字段、属性和方法)添加到组件:
@code { // C# members (fields, properties, and methods) }
对于 Razor 组件,@code
是 @functions
的别名,并且优先于 @functions
使用。 允许多个 @code
块。
@functions
指令允许将 C# 成员(字段、属性和方法)添加到生成的类中:
@functions { // C# members (fields, properties, and methods) }
在 Razor 组件中,请使用 @code
而不是 @functions
来添加 C# 成员。
例如:
@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
@functions
方法有标记时,会用作模板化方法:
@{ RenderName("Mahatma Gandhi"); RenderName("Martin Luther King, Jr."); } @functions { private void RenderName(string name) { <p>Name: <strong>@name</strong></p> } }
该代码呈现以下 HTML:
<p>Name: <strong>Mahatma Gandhi</strong></p> <p>Name: <strong>Martin Luther King, Jr.</strong></p>
@implements
指令为生成的类实现接口。
以下示例实现 System.IDisposable,以便可以调用 Dispose 方法:
@implements IDisposable <h1>Example</h1> @functions { private bool _isDisposed; ... public void Dispose() => _isDisposed = true; }
@inherits
指令对视图继承的类提供完全控制:
@inherits TypeNameOfClassToInheritFrom
下面的代码是一种自定义 Razor 页面类型:
using Microsoft.AspNetCore.Mvc.Razor; public abstract class CustomRazorPage<TModel> : RazorPage<TModel> { public string CustomText { get; } = "Gardyloo! - A Scottish warning yelled from a window before dumping" + "a slop bucket on the street below."; }
CustomText
显示在视图中:
@inherits CustomRazorPage<TModel> <div>Custom text: @CustomText</div>
该代码呈现以下 HTML:
<div> Custom text: Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below. </div>
@model
和 @inherits
可在同一视图中使用。 @inherits
可位于视图导入的 _ViewImports.cshtml 文件中:
@inherits CustomRazorPage<TModel>
下面的代码是一种强类型视图:
@inherits CustomRazorPage<TModel> <div>The Login Email: @Model.Email</div> <div>Custom text: @CustomText</div>
如果在模型中传递“rick@contoso.com”,视图将生成以下 HTML 标记:
<div>The Login Email: rick@contoso.com</div> <div> Custom text: Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below. </div>
@inject
指令允许 Razor 页面将服务从服务容器注入到视图。 有关详细信息,请参阅视图中的依赖关系注入。
此方案仅适用于 Razor 组件 (.razor)。
@layout
指令指定 Razor 组件的布局。 布局组件用于避免代码重复和不一致。 有关详细信息,请参阅 Blazor 布局 ASP.NET Core。
此方案仅适用于 MVC 视图和 Razor Pages (.cshtml)。
@model
指令指定传递到视图或页面的模型类型:
@model TypeNameOfModel
在使用个人用户帐户创建的 ASP.NET Core MVC 或 Razor Pages 应用中,Views/Account/Login.cshtml 包含以下模型声明:
@model LoginViewModel
生成的类继承自 RazorPage<dynamic>
:
public class _Views_Account_Login_cshtml : RazorPage<LoginViewModel>
Razor 公开了 Model
属性,用于访问传递到视图的模型:
<div>The Login Email: @Model.Email</div>
@model
指令指定 Model
属性的类型。 该指令将 RazorPage<T>
中的 T
指定为生成的类,视图便派生自该类。 如果未指定 @model
指令,则 Model
属性的类型为 dynamic
。 有关详细信息,请参阅强类型模型和 @model 关键字。
@namespace
指令:
@namespace Your.Namespace.Here
对于下表中显示的 Razor Pages 示例:
@namespace Hello.World
。Hello.World
,作为其命名空间的根。页面 | 命名空间 |
---|---|
Pages/Index.cshtml | Hello.World |
Pages/MorePages/Page.cshtml | Hello.World.MorePages |
Pages/MorePages/EvenMorePages/Page.cshtml | Hello.World.MorePages.EvenMorePages |
上述关系适用于与 MVC 视图和 Razor 组件一起使用的导入文件。
当多个导入文件具有 @namespace
指令时,最靠近目录树中的页面、视图或组件的文件将用于设置根命名空间。
如果前面示例中的 EvenMorePages 文件夹具有包含 @namespace Another.Planet
的导入文件(或 Pages/MorePages/EvenMorePages/Page.cshtml 文件包含 @namespace Another.Planet
),则结果如下表所示 。
页面 | 命名空间 |
---|---|
Pages/Index.cshtml | Hello.World |
Pages/MorePages/Page.cshtml | Hello.World.MorePages |
Pages/MorePages/EvenMorePages/Page.cshtml | Another.Planet |
@page
指令具有不同的效果,具体取决于其所在文件的类型。 指令:
.cshtml 文件第一行上的 @page
指令表示该文件是 Razor Page 。 有关详细信息,请参阅 ASP.NET Core 中的 Razor 页面介绍。
此方案仅适用于 MVC 视图和 Razor Pages (.cshtml)。
@section
指令与 MVC 和 Razor Pages 布局结合使用,允许视图或页面将内容呈现在 HTML 页面的不同部分。 有关详细信息,请参阅 ASP.NET Core 中的布局。
@using
指令用于向生成的视图添加 C# using
指令:
@using System.IO @{ var dir = Directory.GetCurrentDirectory(); } <p>@dir</p>
在 Razor 组件中,@using
还可控制哪些组件在范围内。
此方案仅适用于 Razor 组件 (.razor)。
@attributes
允许组件呈现未声明的属性。 有关详细信息,请参阅 创建和使用 ASP.NET Core Razor 组件。
此方案仅适用于 Razor 组件 (.razor)。
组件中的数据绑定通过 @bind
属性实现。 有关详细信息,请参阅 ASP.NET Core Blazor 数据绑定。
此方案仅适用于 Razor 组件 (.razor)。
Razor 为组件提供事件处理功能。 有关详细信息,请参阅 ASP.NET Core Blazor 事件处理。
此方案仅适用于 Razor 组件 (.razor)。
禁止事件的默认操作。
此方案仅适用于 Razor 组件 (.razor)。
停止事件的事件传播。
此方案仅适用于 Razor 组件 (.razor)。
@key
指令属性使组件比较算法保证基于键的值保留元素或组件。 有关详细信息,请参阅 创建和使用 ASP.NET Core Razor 组件。
此方案仅适用于 Razor 组件 (.razor)。
组件引用 (@ref
) 提供了一种引用组件实例的方法,以便可以向该实例发出命令。 有关详细信息,请参阅 创建和使用 ASP.NET Core Razor 组件。
此方案仅适用于 Razor 组件 (.razor)。
@typeparam
指令声明生成的组件类的泛型类型参数。 有关详细信息,请参阅 ASP.NET Core Blazor 模板组件。
通过 Razor 模板,可使用以下格式定义 UI 代码片段:
@<tag>...</tag>
下面的示例演示如何指定模板化 Razor 委托作为 Func<T,TResult>。 为委托封装的方法的参数指定动态类型。 将对象类型指定为委托的返回值。 该模板与 Pet
(具有 Name
属性)的 List<T> 一起使用。
public class Pet { public string Name { get; set; } }
@{ Func<dynamic, object> petTemplate = @<p>You have a pet named <strong>@item.Name</strong>.</p>; var pets = new List<Pet> { new Pet { Name = "Rin Tin Tin" }, new Pet { Name = "Mr. Bigglesworth" }, new Pet { Name = "K-9" } }; }
使用 foreach
语句提供的 pets
呈现该模板:
@foreach (var pet in pets) { @petTemplate(pet) }
呈现的输出:
<p>You have a pet named <strong>Rin Tin Tin</strong>.</p> <p>You have a pet named <strong>Mr. Bigglesworth</strong>.</p> <p>You have a pet named <strong>K-9</strong>.</p>
还可以提供内联 Razor 模板作为方法的参数。 如下示例中,Repeat
方法收到一个 Razor 模板。 该方法使用模板生成 HTML 内容,其中包含列表中提供的重复项:
@using Microsoft.AspNetCore.Html @functions { public static IHtmlContent Repeat(IEnumerable<dynamic> items, int times, Func<dynamic, IHtmlContent> template) { var html = new HtmlContentBuilder(); foreach (var item in items) { for (var i = 0; i < times; i++) { html.AppendHtml(template(item)); } } return html; } }
使用前面示例中的 pets 列表,调用 Repeat
方法以及:
Pet
的 List<T>。<ul> @Repeat(pets, 3, @<li>@item.Name</li>) </ul>
呈现的输出:
<ul> <li>Rin Tin Tin</li> <li>Rin Tin Tin</li> <li>Rin Tin Tin</li> <li>Mr. Bigglesworth</li> <li>Mr. Bigglesworth</li> <li>Mr. Bigglesworth</li> <li>K-9</li> <li>K-9</li> <li>K-9</li> </ul>
此方案仅适用于 MVC 视图和 Razor Pages (.cshtml)。
标记帮助程序有三个相关指令。
指令 | 函数 |
---|---|
@addTagHelper |
向视图提供标记帮助程序。 |
@removeTagHelper |
从视图中删除以前添加的标记帮助程序。 |
@tagHelperPrefix |
指定标记前缀,以启用标记帮助程序支持并阐明标记帮助程序的用法。 |
Razor 关键字使用 @(Razor Keyword)
进行转义(例如,@(functions)
)。
C# Razor 关键字必须使用 @(@C# Razor Keyword)
进行双转义(例如,@(@case)
)。 第一个 @
对 Razor 分析器转义。 第二个 @
对 C# 分析器转义。
在 .NET Core SDK 2.1 或更高版本中,Razor SDK 负责编译 Razor 文件。 生成项目时,Razor SDK 在项目根目录中生成 obj/<build_configuration>/<target_framework_moniker>/Razor 目录 。 Razor 目录中的目录结构反映项目的目录结构 。
在面向 .NET Core 2.1 的 ASP.NET Core 2.1 Razor Pages 项目中,请考虑以下目录结构:
在 Debug 配置下生成项目将生成以下 obj 目录 :
若要查看 Pages/Index.cshtml 的生成类,请打开 obj/Debug/netcoreapp2.1/Razor/Pages/Index.g.cshtml.cs 。
将下面的类添加到 ASP.NET Core MVC 项目:
using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; public class CustomTemplateEngine : MvcRazorTemplateEngine { public CustomTemplateEngine(RazorEngine engine, RazorProject project) : base(engine, project) { } public override RazorCSharpDocument GenerateCode(RazorCodeDocument codeDocument) { var csharpDocument = base.GenerateCode(codeDocument); var generatedCode = csharpDocument.GeneratedCode; // Look at generatedCode return csharpDocument; } }
在 Startup.ConfigureServices
中,使用 CustomTemplateEngine
类替代 MVC 添加的 RazorTemplateEngine
:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<RazorTemplateEngine, CustomTemplateEngine>(); }
在 CustomTemplateEngine
的 return csharpDocument;
语句上设置断点。 当程序执行在断点处停止时,查看 generatedCode
的值。
Razor 视图引擎为视图执行区分大小写的查找。 但是,实际查找取决于基础文件系统:
return View("Test")
可匹配 /Views/Home/Test.cshtml、 /Views/home/test.cshtml 以及任何其他大小写变体。EmbeddedFileProvider
构建的文件系统)上,查找区分大小写。 例如,return View("Test")
专门匹配 /Views/Home/Test.cshtml。建议开发人员将文件和目录名称的大小写与以下项的大小写匹配:
匹配大小写可确保无论使用哪种基础文件系统,部署都能找到其视图。