如果写过 HTML 里嵌 <?php ?> 的页面吧?Razor 干的事差不多,只不过嵌入式用的是 C#。Razor 标记可以塞进 .cshtml(MVC 视图/Razor Pages)或者 .razor(Blazor 组件),写法跟 Vue 的模板语法有几分神似。下面从最基础的开始,一路讲到那些容易被忽视的坑。
1. 怎么输出 HTML
Razor 的默认语言就是 HTML,你写 <p>Hello</p>,它就乖乖输出 <p>Hello</p>。但是想混入 C# 逻辑的时候,就得靠 @ 符号了。
2. @ 符号的用法
@ 是 Razor 的开关——从 HTML 模式切到 C# 模式。
- 输出
@ 本身:写@@,比如@@Username 输出就是@Username。 - 邮件地址里的
@ :Razor 不会碰它,Support@contoso.com照原样输出。 - 隐式表达式:
@DateTime.Now,直接跟在@ 后面的 C# 代码。不能有空格(await 例外),也不能写泛型——<>会被当成 HTML 标签。 - 显式表达式:
@(DateTime.Now - TimeSpan.FromDays(7)),括号里头随便写,泛型也行:@(GenericMethod<int>())。
3. SVG 里的 Razor
SVG 的 foreignObject 元素里可以用 Razor 表达式,没什么特别的限制。
4. 表达式编码
字符串表达式默认会被 HTML 编码。@("<span>bold</span>") 输出的是 <span>bold</span> 文本,而不是一个 span 标签。
非要输出原始 HTML,用 @Html.Raw(...)。但注意:永远别对用户的输入用 Raw,那是 XSS 漏洞直通车。
5. 代码块
用 @{ ... } 把多行 C# 代码包起来,里面的代码不会直接输出。但你可以在代码块里嵌套 HTML:
1 @{ 2 var name = "张三"; 3 <p>你好,@name</p> 4 } 5
你还可以在代码块里定义本地函数:
1 @{ 2 void RenderName(string name) { 3 <p>@name</p> 4 } 5 RenderName("李四"); 6 } 7
6. 从 C# 切回 HTML
代码块里想回到 HTML 模式,有 3 种办法:
| 方式 | 示例 | 说明 |
|---|---|---|
| 直接写 HTML | <div>内容</div> | 碰到 HTML 标签自动切回 |
| <text> 标签 | <text>纯文本</text> | 输出纯文本,不生成 DOM 元素 |
| @: 语法 | @:这是一行文本 | 把整行切回 HTML |
常见坑:代码块里多写一个
@就会引发诡异的编译器错误,排查起来很头大。
7. 条件属性呈现
属性值是 null 或 false 时,Razor 会直接省略该属性。比如 class="@false" 最终输出 <div>False</div>(因为 false 不是 null 也不是 false 这个单词,它是布尔值)。
8. 控制结构一览
| 类型 | 语法 |
|---|---|
| 条件 | @if,else if,else,@switch |
| 循环 | @for,@foreach,@while,@do while |
| 复合语句 | @using (Html.BeginForm()) { ... } |
| 异常 | @try ... catch ... finally |
| 锁 | @lock (SomeLock) { ... } |
9. 三种注释
- C# 注释:
/* ... */ 和// ...,在服务端处理,不会到客户端。 - HTML 注释:
<!-- ... -->,会发送到浏览器。 - Razor 注释:
@* ... *@,服务端直接删除,客户端拿不到。适合隐藏敏感逻辑。
10. 指令大全
指令是 Razor 的「元操作」,控制页面怎么编译、怎么跑、从哪拿数据。
| 指令 | 干的事 | 用在哪儿 |
|---|---|---|
| @attribute | 往生成的类上加属性,比如[Authorize] | 视图/页面/组件 |
| @code | 加 C# 成员(属性、方法) | .razor组件 |
| @functions | 同上,但用于.cshtml | .cshtml视图 |
| @implements | 实现接口 | 所有文件 |
| @inherits | 指定自定义基类 | 所有文件 |
| @inject | 依赖注入 | 所有文件 |
| @layout | 指定布局 | .razor组件 |
| @model | 指定强类型模型 | .cshtml |
| @namespace | 设置命名空间 | 导入文件(_ViewImports.cshtml) |
| @page | 声明为可路由的页面/组件 | .cshtml/.razor |
| @preservewhitespace | 控制是否保留多余空白 | .razor |
| @rendermode | 设置 Blazor 渲染模式 | .razor |
| @section | 定义布局节 | .cshtml |
| @typeparam | 泛型类型参数 | .razor |
| @using | 添加using指令 | 所有文件 |
11. 指令属性
这些属性直接用在 HTML 标签上:
-
@attributes:批量展开任意属性(仅限组件) -
@bind、@bind:culture:双向数据绑定 + 区域性格式 -
@formname:防止表单冲突 -
@on{event}、@on{event}:preventDefault、@on{event}:stopPropagation:事件处理全家桶 -
@key:保留元素/组件身份(Diff 算法用) -
@ref:获取组件实例引用
12. 模板化 Razor 委托
可以用 @<tag>...</tag> 定义一段 UI 模板,赋值给委托变量:
1 @{ 2 Func<dynamic, object> itemTemplate = @<li>@item</li>; 3 } 4 <ul>@itemTemplate("A")@itemTemplate("B")</ul> 5
也可以当参数传给方法——比如定义一个 Repeat 方法,接收模板委托做内联渲染。
13. 标记帮助程序指令
-
@addTagHelper:注册标记帮助程序 -
@removeTagHelper:移除已注册的 -
@tagHelperPrefix:设置前缀(比如<vc:...>)
14. 保留关键字
有些词 Razor 自己用了,你不能直接拿来当变量名。
Razor 关键字(需用 @(keyword) 转义):page, namespace, functions, inherits, model, section, helper(不支持了但保留)
C# Razor 关键字(需用 @(@keyword) 双转义):case, do, default, for, foreach, if, else, lock, switch, try, catch, finally, using, while
未使用的保留关键字:class
15. 调试小技巧:查看编译器生成的 C# 类
在 .csproj 里加一行:
1 <PropertyGroup> 2 <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> 3 </PropertyGroup> 4
编译后在 obj/Debug/net9.0/Razor/ 目录下找 .g.cshtml.cs 文件,能看到 Razor 把你的标记翻译成了什么 C# 代码。排查绑定错误时特别好用。
16. 视图查找与大小写
- Windows 文件系统不区分大小写,Linux/macOS 区分。但预编译视图不区分。
- 建议统一用小写命名,跨平台迁移时不踩坑。
17. 默认导入
_ViewImports.cshtml 默认帮你 using 了这些命名空间,开箱即用:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 using Microsoft.AspNetCore.Mvc.Rendering; 7 using Microsoft.AspNetCore.Mvc.ViewFeatures; 8
一点背景:Razor 最早出现在 ASP.NET MVC 3(2011 年),取代了古老的 Web Forms 视图引擎。到 ASP.NET Core 时代,它不仅是 MVC 的视图引擎,还成了 Blazor 组件的基础。虽然本文描述的某些底层机制在 ASP.NET Core 3.0 后有所调整,但核心语法几乎没有变过。
《ASP.NET Core Razor 语法简述》 是转载文章,点击查看原文。