MVC基本概念 ◼ MVC:是一种体系结构模式,它将应用程序分成 3 个主要组件组:模型(Model)、 视图(View) 和控制器(Controller)
◼ MVC模式有助于实现**关注点分离(松耦合)**:
◼ 关注点:输入逻辑(Controller)、业务逻辑(Model)、UI 逻辑(View)
MVC图解
模型M:应用的实体类,用于在内存中暂时存储数据,并在数据变化时通知控制器。
控制器C:处理浏览器的请求,决定如何调用业务层数据的增、删、改、查等业务操作,以及如何将结果返回给视图进行渲染。
视图V:主要用来解析、处理、显示内容,并进行模板的渲染。
MVC模式优点
◼ Controller与View完全分离(松耦合),有利于前、后台分工合作
◼ 一个Model可建立多个视图,满足用户不同需求
◼ Model独立于视图,可移植到新的平台,代码重用高,易于维护
◼ 表现层的性能可以优化到极致
◼ 容易对界面逻辑进行单元测试
◼ 非常强大的URL映射组件,非常干净的URL来建造应用
◼ 有利于软件工程化管理
◼ …
新建MVC项目
步骤 1:启动VS2022
步骤2:创建新项目
步骤3:配置新项目
步骤4:其他信息
步骤5:完成项目创建
步骤6:运行程序
运行当前程序
◼ 初次运行有如下两个提示:
程序运行结果:
注:一开始界面不是这样的,我为了方便就不去重做截图了
步骤7:修改一下程序
添加控制器
HelloController.cs代码:
1 2 3 4 5 6 7 8 9 10 11 using Microsoft.AspNetCore.Mvc; namespace MvcMovie.Controllers { public class HelloController : Controller { public IActionResult Index () { return View(); } } }
添加代码:不带视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class HelloController : Controller { public String Index () { return "这是Index首页方法…" ; } public IActionResult Welcome () { return Content("<h3>这是Welcome方法…</h3>" , "text/html; charset=utf-8" ); } }
如何运行测试代码?(URL请求路径问题)
◼ Action通过如下URL请求来访问(执行):
1 https:// ip地址:端口号 / 控制器名 / action名
具体情况:
1 2 3 4 5 6 7 8 9 10 11 public class HelloController : Controller { public String Index () { ... } public IActionResult Welcome () { ... } }
注:上述两个都为url请求,且两者默认都属于HTTP GET 请求
运行结果:
思考:URL格式为何如此?
◼ 原因:在 Program.cs 中有如下路由模式定义:
补充:返回值IActionResult用法汇总
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public IActionResult MyFile (){ return File("~/css/site.css" , "text/plain" ); } public IActionResult MyJson (){ var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) }; return Json(new { name = "小明" , age="20" }, options); } public IActionResult MyRedirect (){ return Redirect("https://www.wust.edu.cn" ); } public IActionResult MyRedirectAction (){ return RedirectToAction("Index" ,"Hello" ); } public async Task<IActionResult> MyAsync () { await Task.Delay(1000 ); return Content("ok" ); } public IActionResult Index (){ return View(); }
添加视图 ◼ 视图(View)使用Razor模板引擎 来创建,视图模板的扩展名:.cshtml
◼ 先了解两个重要概念:
◼ 什么是模板引擎?
◼ 什么是Razor?
模板引擎
◼ 模板引擎是为了使用户界面与业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎会生成一个标准的HTML文档
Razor
◼ Razor是一个视图模板引擎,它提供了优雅的方法将基于服务器的代码(如C#)嵌入到HTML页面中。
◼ 基于服务器的代码可以在网页传送给浏览器时,创建动态Web内容。
◼ 当一个网页被请求时,服务器在返回页面给浏览器之前,会先执行页面中基于服务器的代码。
◼ 通过服务器的运行,代码能执行复杂的任务,比如数据库业务。
如何创建视图
1 2 3 4 5 6 7 8 public class HelloController : Controller { public IActionResult Index () { return View(); } }
关于上面return View();
语句的解释
◼ 是 return View(“Index”); 的简写形式
◼ 表示返回一个名为”Index”的视图(扩展名为.cshtml)
◼ 如果视图名与方法同名则可参数省略不写
查看Index视图代码 – Razor模板文件
先了解Razor语法规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @* 以下@{}内的是Razor代码块(C#代码) *@ @{ ViewData["Title"] = "测试" ; // 定义一个ViewData值,该值将传给布局页,用于给视图页面设置title标题 } @* 内部样式 *@ <style> .test p{ color:red; margin:10px; } </style> @* 一些HTML代码 (称为静态的) *@ <div class="test"> <h3>我的第一个视图!</h3> <p>欢迎 @ViewData["Name"] </p> <p>日期:@DateTime.Now.ToString() </p> <!-- Razor行内表达式(嵌入在HTML中的C#代码,称为动态值) --> </div> @* JS代码 *@ <script> let p = document.querySelector(".test p"); p.addEventListener("click", () => { alert("欢迎!"); }); </script>
在Index方法中添加代码:
1 2 3 4 5 6 public IActionResult Index (){ ViewData["Name" ] = "小明" ; return View(); }
运行结果
运行原理图示
一个问题:导航栏和页脚从哪儿来滴?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <!DOCTYPE html > <html lang ="en" > <head > <title > @ViewData["Title"] - MvcMovie</title > </head > <body > <header > <nav class ="navbar navbar-expand-sm …" > ... <div class ="navbar-collapse …" > <ul class ="navbar-nav flex-grow-1" > <li class ="nav-item" > <a ... asp-controller ="Home" asp-action ="Index" > Home</a > </li > ... </ul > </div > </nav > </header > <div class ="container" > <main role ="main" class ="pb-3" > @RenderBody() </main > </div > <footer class ="border-top footer text-muted" > <div class ="container" > © 2021 - MvcMovie - <a asp-area ="" asp-controller ="Home" asp-action ="Privacy" > Privacy</a > </div > </footer > <script src ="…" > </script > @await RenderSectionAsync("Scripts", required: false) </body > </html >
又一个问题:视图为何能和布局页结合?
◼ 答案:_ViewStart.cshtml
◼ 解释:该文件会在所有视图被执行之前先执行,可以将一些公共代码(如设置布局页)写在该文件中。
↓
1 2 3 @{ Layout = "_Layout" ; }
_Layout:布局页名称
Layout对象用于设置布局
布局页用法示例:添加新导航链接
主要代码:红色部分
1 2 3 4 5 6 7 8 9 10 11 12 13 <div class ="navbar-collapse …" > <ul class ="navbar-nav flex-grow-1" > ... <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Home" asp-action ="Privacy" > Privacy</a > </li > <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Hello" asp-action ="Index" > Hello</a > </li > </ul > </div >
最后:修改一下Index视图JS代码块
1 2 3 4 5 6 7 8 9 10 ... 其他代码不做修改 @* 将视图中的JS代码放在 @section scripts { } 命令中 *@ @section scripts { <script> let p = document.querySelector(".test p" ); p.addEventListener("click" , () => { alert("欢迎!" ); }); </script> }
补充:如何加载外部样式或JS文件
1 2 3 4 5 6 <link rel="stylesheet" href="~/css/my.css" > ... @section scripts { <script src="~/js/my.js" ></script> ... }
◼ 静态资源(如css,js,图片等)放在 wwwroot 中
◼ css文件放在 wwwroot/css 目录下
◼ js文件放在 wwwroot/js 目录下
◼ 图片文件在 wwwroot 下自建文件夹(如image)
传值问题 URL传值 前端直接传值到后台
1 2 3 4 5 6 7 8 public class HelloController : Controller { public string Welcome ( string name, int n = 1 ) { string tmp = "name=" + name + ", n=" + n; return tmp; } }
运行示例:手工输入带参数的URL请求
?
是分隔符,后面跟查询字符串&
用于分割查询参数:参数1=值&参数2=值…
参数名必须和action参数同名
参数值默认都是字符串(不加引号)
补充:后台代码调试
关于特殊参数 id 的传值:
1 2 3 4 5 6 7 8 public class HelloController : Controller { public string Details ( int id ) { string tmp = "id=" + id; return tmp; } }
id 参数传值情况
原因是在 Program.cs 下有如下的路由模式定义
1 2 3 app.MapControllerRoute( name: "default" , pattern: "{controller=Home}/{action=Index}/{id?}" );
在上述路由定义中id
为默认参数。
ViewData传值 常用于:后台传值给视图
◼ ViewData是一个字典对象 ,用来从Controller向对应的View视图传值
◼ 基本用法:ViewData["属性名"] = 值
(任意类型值 )
◼ ViewData是一种弱类型 传值,因此在视图中使用ViewData值时必须进行合适的类型转换 (强转之前建议空值检查)。
特别注意:
◼ ViewData只在当前请求中有效 ,生命周期和View相同,其值不能在多个请求中共享 。
◼ 即:在重定向(新请求)后,ViewData存储的值将变为null。
ViewData传值示例:
1 2 3 4 5 6 7 8 9 public class HelloController : Controller { public IActionResult Welcome (string name, int n = 1 ) { ViewData["Message" ] = "Hello " + name; ViewData["Count" ] = n; return View(); } }
新建Welcome视图:注意添加类型转换
1 2 3 4 5 6 7 8 9 10 11 @{ ViewData["Title" ] = "欢迎" ; } <h2>Welcome</h2> <ul> @for ( int i = 0 ; i < (int )ViewData["Count" ]; i++ ) @* 此处需要强转,不然会报错 *@ { <li>@ViewData["Message" ]</li> @* 这个不用强转,行内表达式自动强转为字符串 *@ } </ul>
体会一下 Razor 强大特点:
◼ 在HTML页面中嵌入C#代码
◼ 在C#代码中嵌入HTML元素
运行情况
示例:简单登录
(1)新建控制器:LoginController
1 2 3 4 5 public class LoginController : Controller { public IActionResult Index () { return View(); } }
(2)新建Index视图
1 2 3 4 5 6 7 8 9 10 <form action ="/Login/Check" method ="get" > 用户名:<input type ="text" name ="username" > <br /> 密 码:<input type ="password" name ="pwd" > <br /> <input type ="submit" value ="提交" > <input type ="reset" value ="重填" > </form >
(3)添加Check方法
1 2 3 4 5 6 7 8 9 10 public IActionResult Check (string username, string pwd ) { if (username.Equals("zz" )&&pwd.Equals("888" )) { ViewData["username" ] = username; ViewData["pwd" ] = pwd; return View(); } return RedirectToAction("Index" ); }
问题:如何传递用”户名或密码错”信息
(4)新建Check视图
1 2 3 4 <div > <h4 > 欢迎 @ViewData["username"]</h4 > <h4 > 你的密码:@ViewData["pwd"]</h4 > </div >
TempData传值 常用于:后台传值给视图
◼ TempData也是一个字典对象,但是基于Session存储机制
◼ TempData用在多个Action间或页面重定向(Redirection)时传递共享数据
◼ 但TempData存放的数据只一次访问中有效,一次访问完成后就会删除
◼ TempData用法和ViewData相同
示例:传递用”户名或密码错”信息
1 2 3 4 5 6 7 8 9 10 11 public IActionResult Check (string username, string pwd ){ if (username.Equals("zz" )&&pwd.Equals("888" )) { ViewData["username" ] = username; ViewData["pwd" ] = pwd; return View(); } TempData["error" ] = "用户名或密码错" ; return RedirectToAction("Index" ); }
在Index视图中显示信息
1 2 3 4 5 6 7 8 9 10 11 <form action ="/Login/Check" method ="get" > 用户名:<input type ="text" name ="username" > <br /> 密 码:<input type ="password" name ="pwd" > <br /> <input type ="submit" value ="提交" > <input type ="reset" value ="重填" > </form > <div style ="color:red;" > @TempData["error"] </div >
Session传值 常用于:后台传值给视图
◼ 什么是Session?
◼ 从一个客户端打开浏览器并连接到服务器开始,到客户关闭浏览器离开这个服务器结束,被 称为一个Session(会话)
◼ Session传值作用: 实现在一个会话期间(同一用户)的所有请求中进行数据共享和传递。
汇总一下:
数据传递
ViewData
TempData(一次性)
Session
Controller to Controller
No
Yes
Yes
Controller to View
Yes
Yes
Yes
View to Controller
No
No
Yes
如何配置Session(重要)
(1)首先安装 Session 包:
(2)在Program.cs文件中配置Session:2处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... builder.Services.AddControllersWithViews(); builder.Services.AddSession(); ... app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseSession(); app.UseAuthorization(); ...
如何使用Session传值
◼ 在控制器中使用:
◼ 创建Session值:HttpContext.Session.SetString(“username”, “小明”); // 注意前缀
◼ 读取Session值:String? name = HttpContext.Session.GetString(“username”);
◼ 清空session:HttpContext.Session.Clear();
◼ 在视图中使用:
1 2 3 4 5 6 7 @* 视图中导入命名空间 *@ @using Microsoft.AspNetCore.Http @{ Context.Session.SetString("username" , "小明" ); String? name = Context.Session.GetString("username" ); }
Session传值示例:
1 2 3 4 5 6 7 8 9 10 11 12 public class HelloController : Controller { public IActionResult Index () { HttpContext.Session.SetString("username" , "小明" ); return View(); } public IActionResult Welcome () { return View(); } }
Welcome视图代码:
1 2 3 4 5 6 7 8 9 @* 先导入命名空间(包) *@ @using Microsoft.AspNetCore.Http @{ String name = Context.Session.GetString("username" ); } <!-- 显示一下 --> <h3>欢迎 @name</h3>
运行结果:体会Session传值
① 先执行请求:https://localhost:端口/Hello/Index (创建Session值)
② 再执行请求:https://localhost:端口/Hello/Welcome (读取Session值)
综合示例 – 简单的Session权限控制
准备工作:
◼ 首先配置好Session。
◼ 然后新建两个控制器:
◼ TestController:测试页面,添加简单的Session权限控制
◼ LoginController:登录控制
TestController主要代码:
1 2 3 4 5 public IActionResult Index (){ ViewData["Message" ] = "欢迎" ; return View(); }
新建Index视图,代码如下:
1 2 <h2 > @ViewData["Message"]</h2 > <p > Hello!我的第一个视图!</p >
在布局页_Layout.cshtml中添加“测试”导航:
1 2 3 4 5 6 7 <ul class ="navbar-nav flex-grow-1" > ... <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Test" asp-action ="Index" > 测试</a > </li > </ul >
LoginController主要代码:
1 2 3 4 5 6 7 public class LoginController : Controller { public IActionResult Index () { return View(); } }
登录界面视图:Index视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @{ ViewData["Title"] = "登录"; } <link rel ="stylesheet" href ="~/css/login.css" /> <form id ="login_form" class ="form" action ="/Login/Check" > <div class ="imgcontainer" > <img src ="~/images/img_avatar.png" alt ="Avatar" class ="avatar" > </div > <div class ="container" > <label > <b > 用户名</b > </label > <input type ="text" placeholder ="请输入用户名" name ="username" > <label > <b > 密码</b > </label > <input type ="password" placeholder ="请输入密码" name ="pwd" > <button type ="submit" > 登陆</button > <input type ="checkbox" checked ="checked" > 记住我 </div > <div class ="container" > <button type ="button" id ="btn_cancel" class ="cancelbtn" onclick ="window.location='/'" > 取消</button > <span class ="psw" > <a href ="#" > 忘记密码</a > </span > </div > </form >
login.css样式代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 .form { width : 40% ; border : 1px solid #888 ; margin : 35px auto; } .form .imgcontainer { text-align : center; margin : 24px 0 12px 0 ; } .form img .avatar { width : 30% ; border-radius : 50% ; } .form .container { padding : 5px 16px ; } .form label { margin :0 ; } .form input [type=text] ,.form input [type=password] { font-size : 16px ; width : 100% ; padding : 10px 15px ; margin : 8px 0px ; border : 1px solid #ccc ; box-sizing : border-box; } .form input [type=text] :focus ,.form input [type=password] :focus { outline : 1px solid #03a9f4 ; border : 1px solid #03a9f4 ; } .form button { background-color : #4CAF50 ; color : white; width : 100% ; cursor : pointer; border : 0px ; padding : 14px 20px ; margin : 8px 0 ; outline-color :white; } .form button :hover { opacity : 0.8 ; } .form .cancelbtn { width : auto; background-color : #f44336 ; padding : 10px 18px ; } .form span .psw { float : right; padding-top : 16px ; } .form .container :last-child { background-color : #f5f5f5 ; }
在布局页中添加“登录”导航:红色部分
1 2 3 4 5 6 7 8 9 10 11 <ul class ="navbar-nav flex-grow-1" > ... <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Test" asp-action ="Index" > 测试</a > </li > </ul > <ul class ="navbar-nav" > <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Login" asp-action ="Index" > 登录</a > </li > </ul >
运行一下:
LoginController添加Check登录验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 public IActionResult Check ( String username, String pwd ) { if ( !String.IsNullOrEmpty(username)&&username.Equals("zz" ) && !String.IsNullOrEmpty(pwd) && pwd.Equals("888" ) ) { return RedirectToAction("Index" , "Test" ); } TempData["error" ]="用户名或密码错" ; return RedirectToAction("Index" , "Login" ); }
Index视图改进:显示登录错误信息(红色部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @{ ViewData["Title"] = "登录"; } <link rel ="stylesheet" href ="~/css/login.css" /> <form id ="login_form" class ="form" action ="/Login/Check" > <div class ="imgcontainer" > <img src ="~/images/img_avatar.png" alt ="Avatar" class ="avatar" > </div > <div class ="container" > <label > <b > 用户名</b > </label > <input type ="text" placeholder ="请输入用户名" name ="username" > <label > <b > 密码</b > </label > <input type ="password" placeholder ="请输入密码" name ="pwd" > <button type ="submit" > 登陆</button > <input type ="checkbox" checked ="checked" > 记住我 </div > <div style ="color:red;text-align:center;" > @TempData["error"] </div > <div class ="container" > <button type ="button" id ="btn_cancel" class ="cancelbtn" onclick ="window.location='/'" > 取消</button > <span class ="psw" > <a href ="#" > 忘记密码</a > </span > </div > </form >
需要解决的问题:如何在Test控制器的Index中拦截请求?
基本思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class TestController : Controller { public IActionResult Index () { if ( 有登录凭证 ) { ViewData["Message" ] = "欢迎" ; return View(); } else { return RedirectToAction("Index" , "Login" ); } } }
因此修改Check代码:添加Session存储登录凭证
1 2 3 4 5 6 7 8 9 10 11 12 public IActionResult Check (string username, string pwd ){ if ( !String.IsNullOrEmpty(username)&&username.Equals("zz" ) && !String.IsNullOrEmpty(pwd) && pwd.Equals("888" )) { HttpContext.Session.SetString("username" , username); return RedirectToAction("Index" , "Test" ); } TempData["error" ]="用户名或密码错" ; return RedirectToAction("Index" , "Login" ); }
拦截实现:修改Test控制器Index方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public IActionResult Index (){ string username = HttpContext.Session.GetString("username" ); if ( !String.IsNullOrEmpty(username) ) { ViewData["Message" ] = "欢迎" + username; return View(); } else { return RedirectToAction("Index" , "Login" ); } }
测试拦截:未登录时
登录成功后:再次访问测试页面
添加注销功能:
LoginController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 public class LoginController : Controller { [Route("Logout" ) ] public IActionResult Logout () { HttpContext.Session.Clear(); return RedirectToAction("Index" , "Home" ); } }
测试注销功能:手工注销
最后:在导航栏中添加注销链接
修改布局页右侧导航(根据登录状态,设置不同的导航内容):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @using Microsoft.AspNetCore.Http <ul class ="navbar-nav flex-grow-1" > ... </ul > <ul class ="navbar-nav" > @{ string? username = Context.Session.GetString("username"); if ( !String.IsNullOrEmpty(username) ) { <li class ="nav-item" > <span class ="nav-link" style ="color:blue;" > 欢迎 @username</span > </li > <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Login" asp-action ="Logout" > 注销</a > </li > } else { <li class ="nav-item" > <a class ="nav-link text-dark" asp-area ="" asp-controller ="Login" asp-action ="Index" > 登录</a > </li > } } </ul >
Razor基本用法 ◼ Razor 代码封装于 @{ ... }
中
◼ 代码语句以分号;
结尾
◼ 行内表达式(变量和函数)以 @
开头
◼ C# 代码对大小写敏感
Razor示例
◼ Razor 代码封装于 @{ … } 中,行内表达式以 @ 开头:
1 2 3 4 5 6 @{ string name = "小明" ; } <span>你好 @name</span> <span>今天日期:@DateTime.Now.ToString("yyyy-MM-dd" )</span>
Razor支持代码混写:在代码块中插入HTML,在HTML中插入Razor代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @* 下面是Razor代码块 *@ @{ int a = 1 ; int b = 2 ; int s = a + b; string color ="red" ; <div style="color:@color;" >求和 @a+@b=@s</div> } @for (int i = 0 ; i < 10 ; i++) { <span>第 @i 行<br /></span> }
输出带有html标签的字符串
1 2 3 4 5 @{ string s = "<div style='color:red;'>测试文本</div>" ; <h3>@s</h3> <h3>@Html.Raw(s)</h3> }
或用HtmlString
定义html串,也能支持解析: HtmlString s = new HtmlString("<div style='color:red;'>测试文本</div>");
Razor中@的处理
◼ 输出@符号:@@ (两个@连写)
◼ 输出Email地址: 自动识别,可直接用
例如:
1 2 <p>我要输出强大的@@符号</p> <!-- 输出一个@ --> <span>Email地址:wustzz@sina.com</span> <!-- 自动识别@ -->
或用HtmlString
定义html串,也能支持解析: HtmlString s = new HtmlString("<div style='color:red;'>测试文本</div>");
Razor注释(属于服务器端的注释)
◼ 在Razor代码块中,可用 //
和 /* */
注释代码。
◼ Razor还提供 @**@
注释,可注释C#和HTML代码。
1 2 3 4 5 6 7 8 9 10 @{ int b = 2 ; } <!-- 这是html注释 --> @* 这是一个注释 <span>hello</span> *@
Razor示例
用 @:文本
来显示纯文本( 不含html标签的文本)
1 2 3 4 5 6 7 8 9 @{ int a = 10 ; int b = 1 ; } @if(a>b) { 结果是a>b }
1 2 3 4 5 6 7 8 9 10 11 12 13 <ul> @for (int i = 0 ; i < 10 ; i++) { <li> 当前循环到第@(i + 1 )次 @if (i == 5 ) { @:遍历到数字5 了 } else if (i == 9 ) { @:Bye! } </li> } </ul>
运行结果: