目录
作为ASP.NET开发的核心环节,View 层(UI 渲染)是连接后端逻辑与前端界面的 “桥梁”,而 Razor 视图引擎(.cshtml 文件)则是这座桥梁的 “核心建材”—— 它把 C# 代码和 HTML 标签无缝融合,让开发者既能用 C# 处理业务逻辑,又能通过 HTML 构建界面。但新手在使用.cshtml 时,很容易因语法混淆、逻辑滥用、数据传递不当踩坑。本文从基础认知、代码实战、避坑指南三个维度,把 Razor 视图引擎讲透,让你写.cshtml 既高效又少出错。
一、先搞懂核心:Razor 视图引擎是什么?(生活类比 + 流程图)
小节:Razor = 前端界面的 “智能模板引擎”
先举个生活例子:你去奶茶店点单,店员会拿着 “点单模板”(固定格式:饮品名称 + 甜度 + 冰度 + 配料),根据你的需求填充具体内容(比如 “珍珠奶茶 + 正常糖 + 少冰 + 加椰果”),最终生成你的专属订单。
Razor 视图引擎(.cshtml)就像这个 “点单模板”:
- 模板基础框架:HTML 标签(对应订单模板的固定格式);
- 动态填充内容:C# 代码(对应你自定义的饮品需求);
- 最终输出:浏览器能识别的纯 HTML 页面(对应最终的订单)。
Razor 视图渲染流程图
Razor 核心语法规则(新手必记):
| 语法 | 作用 | 示例 |
|---|---|---|
| @ | 开启 C# 代码块 / 输出变量 | @Model.UserName、@(1+2) |
| @{ } | 多行 C# 代码块 | @{ var age = 25; if(age>18) { 成年 } } |
| @if/@for/@foreach | 流程控制( | 无需额外闭合符) @foreach(var item in Model.List) {
|
| @Model | 接收 Controller 传递的模型数据 | @model WebDemo.Models.UserDto(声明模型) |
| @Html | Razor 内置辅助类(生成表单 / 链接等) | @Html.TextBoxFor(m => m.Name) |
二、代码实战:.cshtml 完整使用示例
小节:从 Controller 传数据到.cshtml 渲染全流程
我们以 “用户信息展示 + 新增用户表单” 为例,覆盖 Razor 最常用的场景:模型绑定、循环渲染、表单生成、条件判断。
步骤 1:定义数据模型(ViewModel)
// Models/UserDto.cs
namespace WebDemo.Models
{
/// <summary>
/// 用户数据模型
/// </summary>
public class UserDto
{
public int Id { get; set; }
[Display(Name = "用户名")]
[Required(ErrorMessage = "用户名不能为空")]
public string UserName { get; set; }
[Display(Name = "年龄")]
[Range(18, 100, ErrorMessage = "年龄需在18-100之间")]
public int Age { get; set; }
[Display(Name = "手机号")]
public string Phone { get; set; }
}
/// <summary>
/// 用户列表页面模型
/// </summary>
public class UserListViewModel
{
public string Title { get; set; } // 页面标题
public List<UserDto> UserList { get; set; } // 用户列表
}
}
步骤 2:Controller 传递数据到 View
// Controllers/UserController.cs
using Microsoft.AspNetCore.Mvc;
using WebDemo.Models;
namespace WebDemo.Controllers
{
public class UserController : Controller
{
// 展示用户列表页面
public IActionResult Index()
{
// 模拟从数据库获取用户数据
var userList = new List<UserDto>
{
new UserDto { Id = 1, UserName = "张三", Age = 28, Phone = "13800138000" },
new UserDto { Id = 2, UserName = "李四", Age = 32, Phone = "13900139000" },
new UserDto { Id = 3, UserName = "王五", Age = 25, Phone = "13700137000" }
};
// 封装页面模型
var viewModel = new UserListViewModel
{
Title = "系统用户列表",
UserList = userList
};
// 将模型传递给View
return View(viewModel);
}
// 新增用户表单页面
[HttpGet]
public IActionResult Add()
{
return View(new UserDto()); // 传递空模型,用于表单绑定
}
// 提交新增用户数据
[HttpPost]
public IActionResult Add(UserDto user)
{
if (!ModelState.IsValid)
{
// 模型验证失败,返回表单页面并显示错误
return View(user);
}
// 模拟保存用户数据到数据库
return RedirectToAction("Index");
}
}
}
步骤 3:编写.cshtml 视图文件(核心)
1. 用户列表页面(Index.cshtml)
@* 声明页面接收的模型类型(必须放在第一行) *@
@model WebDemo.Models.UserListViewModel
@{
// 多行C#代码块:定义页面变量、处理逻辑
ViewData["Title"] = Model.Title; // 设置页面标题(兼容传统ViewData方式)
var adultCount = Model.UserList.Count(u => u.Age >= 18); // 统计成年用户数
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<style>
table { border-collapse: collapse; width: 80%; margin: 20px auto; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: center; }
th { background-color: #f5f5f5; }
.adult { color: green; font-weight: bold; }
.teen { color: red; }
</style>
</head>
<body>
<h1 style="text-align: center;">@Model.Title</h1>
<p style="text-align: center;">成年用户数量:<span class="adult">@adultCount</span> / 总用户数:@Model.UserList.Count</p>
<table>
<tr>
<th>ID</th>
<th>用户名</th>
<th>年龄</th>
<th>手机号</th>
<th>状态</th>
</tr>
@* 循环渲染用户列表 *@
@foreach (var user in Model.UserList)
{
<tr>
<td>@user.Id</td>
<td>@user.UserName</td>
<td>@user.Age</td>
<td>@user.Phone</td>
<td>
@* 条件判断 *@
@if (user.Age >= 18)
{
<span class="adult">成年</span>
}
else
{
<span class="teen">未成年</span>
}
</td>
</tr>
}
</table>
<div style="text-align: center; margin-top: 20px;">
@* Razor辅助类生成链接 *@
@Html.ActionLink("新增用户", "Add", "User", null, new { style = "text-decoration: none; color: white; background-color: #007bff; padding: 8px 16px; border-radius: 4px;" })
</div>
</body>
</html>
2. 新增用户表单页面(Add.cshtml)
@model WebDemo.Models.UserDto
@{
ViewData["Title"] = "新增用户";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<style>
.form-container { width: 50%; margin: 20px auto; }
.form-group { margin-bottom: 15px; }
label { display: inline-block; width: 100px; }
input { width: 300px; padding: 5px; }
.error { color: red; font-size: 12px; margin-left: 105px; }
button { padding: 8px 20px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<div class="form-container">
<h2>新增用户</h2>
@* Razor辅助类生成表单(自动绑定模型) *@
@using (Html.BeginForm("Add", "User", FormMethod.Post))
{
<div class="form-group">
@* 生成标签 + 输入框 + 验证错误提示 *@
@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName, new { placeholder = "请输入用户名" })
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "error" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Age)
@Html.TextBoxFor(m => m.Age, new { placeholder = "请输入年龄" })
@Html.ValidationMessageFor(m => m.Age, "", new { @class = "error" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Phone)
@Html.TextBoxFor(m => m.Phone, new { placeholder = "请输入手机号" })
@Html.ValidationMessageFor(m => m.Phone, "", new { @class = "error" })
</div>
<div class="form-group">
<button type="submit">提交</button>
</div>
}
<div>
@Html.ActionLink("返回列表", "Index", "User")
</div>
</div>
@* 引入验证脚本(必须加,否则客户端验证不生效) *@
@Scripts.Render("~/bundles/jqueryval")
</body>
</html>
步骤 4:运行效果
1.访问/User/Index:展示用户列表,统计成年用户数,支持条件渲染用户状态;
2.点击 “新增用户”:跳转到表单页面,输入不符合规则的内容(如年龄 17),提交后会显示验证错误;
3.输入合法数据提交:跳回用户列表页面。
三、常踩的坑(附解决方案 + 生活类比)
小节:避开这些坑,.cshtml 开发不踩雷
新手写.cshtml 最容易踩以下 7 个坑,每个坑都配生活类比和解决方案:
| 坑点 | 生活类比 | 解决方案 |
|---|---|---|
| 1.忘记声明 @model 或声明位置错误 | 奶茶店店员拿到订单模板,却没看模板要求的填写格式,导致填错内容 | 1. @model必须放在.cshtml 第一行;2. 模型类型要写完整命名空间(如@model WebDemo.Models.UserListViewModel);3. 区分@model(声明类型)和@Model(使用实例)的大小写 |
| 2.C# 代码与 HTML 混写时语法错误(如缺少 @、大括号不匹配) | 写作文时中英文标点混用、段落没有闭合,导致读者看不懂 | 1. 单行输出变量必须加@(如@user.Name);2. 多行 C# 代码必须用@{ }包裹;3. 流程控制(if/for)后直接跟 HTML,无需额外闭合符;4. 使用 VS 的语法高亮和错误提示排查 |
| 3.模型验证不生效(客户端无提示) | 奶茶店设置了 “甜度只能选正常 / 少糖 / 无糖”,但顾客选了 “超甜” 却没提示,直到结账才发现 | 1. 确保模型加了验证特性(如[Required]);2. 表单页面引入验证脚本@Scripts.Render(“~/bundles/jqueryval”);3. Controller 中判断ModelState.IsValid |
| 4.循环渲染时复用变量导致数据错乱 | 店员给多个顾客点餐时,把上一个顾客的配料写到下一个订单里 | 1. 循环内变量命名唯一(如foreach(var user in Model.UserList)中的user);2. 避免在循环外定义临时变量供循环内使用;3. 复杂循环可拆分为局部视图(PartialView) |
| 5.滥用 C# 逻辑(把业务逻辑写在.cshtml) | 奶茶店店员不只是填订单,还自己决定奶茶的配方、定价(越权) | 1. .cshtml 只做 “数据展示 + 简单逻辑”(如条件 / 循环);2. 复杂业务逻辑(如数据过滤、计算)放在 Controller/Service 层;3. 可使用 ViewBag/ViewData 传递简单临时数据,复杂数据用 ViewModel |
| 6.静态资源(CSS/JS)路径错误 | 顾客按地址找奶茶店,却因地址写错找不到 | 1. 使用~表示网站根目录(如);2. 避免相对路径(如…/css/style.css),尤其是多级目录的视图;3. 配置静态文件中间件(app.UseStaticFiles()) |
| 7.PartialView(局部视图)传参失败 | 餐厅把凉菜的配料单传给后厨,但传错了单子,导致凉菜做错 | 1. 渲染局部视图时指定模型:@Html.Partial(“_UserItem”, user);2. 局部视图声明@model接收参数;3. 避免在局部视图中使用 ViewData/ViewBag(易受父视图影响) |
| 坑点实战示例(解决 “静态资源路径错误”) | ||
| 错误写法(相对路径,多级目录视图会失效): |
<link href="../css/style.css" rel="stylesheet" />
正确写法(根路径,适配所有视图目录):
<link href="~/css/style.css" rel="stylesheet" />
同时确保Program.cs中配置静态文件中间件:
var app = builder.Build();
// 启用静态文件(必须加,否则无法访问CSS/JS/图片)
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
四、总结
Razor 视图引擎(.cshtml)是ASP.NET View 层的核心,其核心价值是 “动静结合”—— 用 HTML 构建界面骨架,用 C# 填充动态数据。新手掌握 “模型绑定、语法规则、数据渲染” 三大核心,避开 “语法错误、逻辑滥用、路径错误” 等坑,就能写出清晰、易维护的视图代码。
记住一个原则:.cshtml 的职责是 “展示数据”,而不是 “处理数据”,把业务逻辑交给 Controller/Service 层,视图只做最精简的渲染逻辑,这是保证项目可维护性的关键。
留言互动
你在项目中用.cshtml 实现过哪些复杂场景?(比如分页、表格筛选、动态表单等)有没有遇到过本文没提到的坑?欢迎在评论区分享你的解决方案,一起交流进阶技巧~
专栏说明: 本文聚焦 Razor 视图引擎核心实战,后续会更新 PartialView(局部视图)、ViewComponent(视图组件)、Razor Pages 等进阶内容,关注我,持续解锁ASP.NET View 层干货~

2271

被折叠的 条评论
为什么被折叠?



