MVC基础编程
Q7nl1s admin

MVC基本概念

◼ MVC:是一种体系结构模式,它将应用程序分成 3 个主要组件组:模型(Model)视图(View)控制器(Controller)

◼ MVC模式有助于实现**关注点分离(松耦合)**:

​ ◼ 关注点:输入逻辑(Controller)、业务逻辑(Model)、UI 逻辑(View)

MVC_0


MVC图解

MVC_1

模型M:应用的实体类,用于在内存中暂时存储数据,并在数据变化时通知控制器。

控制器C:处理浏览器的请求,决定如何调用业务层数据的增、删、改、查等业务操作,以及如何将结果返回给视图进行渲染。

视图V:主要用来解析、处理、显示内容,并进行模板的渲染。


MVC模式优点

◼ Controller与View完全分离(松耦合),有利于前、后台分工合作

◼ 一个Model可建立多个视图,满足用户不同需求

◼ Model独立于视图,可移植到新的平台,代码重用高,易于维护

◼ 表现层的性能可以优化到极致

◼ 容易对界面逻辑进行单元测试

◼ 非常强大的URL映射组件,非常干净的URL来建造应用

◼ 有利于软件工程化管理

◼ …

新建MVC项目

MVC_2


步骤 1:启动VS2022

MVC_3


步骤2:创建新项目

MVC_4


步骤3:配置新项目

MVC_5


步骤4:其他信息

MVC_6


步骤5:完成项目创建

MVC_7


步骤6:运行程序

运行当前程序

MVC_8

◼ 初次运行有如下两个提示:

MVC_9程序运行结果:

注:一开始界面不是这样的,我为了方便就不去重做截图了

MVC_2


步骤7:修改一下程序

MVC_10


添加控制器

MVC_11 MVC_12 MVC_13

HelloController.cs代码:

1
2
3
4
5
6
7
8
9
10
11
using Microsoft.AspNetCore.Mvc;	// using命令:导入命名空间(namespace)
namespace MvcMovie.Controllers // namespace命令:定义名字空间(包名)
{
public class HelloController : Controller // 控制器类 (继承Controller)
{
public IActionResult Index() // 控制器方法 (也称为Action)
{
return View(); // 返回一个视图 (下节再使用)
}
}
}

添加代码:不带视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HelloController : Controller
{
//public IActionResult Index() // ① 先注释这个Index方法 (工具栏有注释按钮)
//{
// return View();
//}
public String Index() // ② 添加一个新的Index方法(Action)
{
return "这是Index首页方法…"; // 简单返回一个string
}
public IActionResult Welcome() // ③ 添加一个Welcome方法(Action)
{
return Content("<h3>这是Welcome方法…</h3>", "text/html; charset=utf-8"); //可以返回HTML内容串(第二个参数是内容的类型说明)
}
}


如何运行测试代码?(URL请求路径问题)

◼ Action通过如下URL请求来访问(执行):

1
https:// ip地址:端口号 / 控制器名 / action名

MVC_14


具体情况:

1
2
3
4
5
6
7
8
9
10
11
public class HelloController : Controller
{
public String Index() // https://localhost:端口号/Hello/Index(Index是默认的首页Action,可省略不写)
{
...
}
public IActionResult Welcome() // https://localhost:端口号/Hello/Welcome
{
...
}
}

注:上述两个都为url请求,且两者默认都属于HTTP GET请求

运行结果:

MVC_15


思考:URL格式为何如此?

◼ 原因:在 Program.cs 中有如下路由模式定义:

MVC_16


补充:返回值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"); //返回纯文本文件
//return File("~/images/cat.jpg", "image/jpg"); //返回图片
// 第二个参数是MIME类型
}

public IActionResult MyJson()
{
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
return Json(new { name = "小明", age="20" }, options); //返回json
// 第二个参数用于设置字符编码(中文字符串将不转义)
}

public IActionResult MyRedirect()
{
return Redirect("https://www.wust.edu.cn"); //重定向(跳转)到外部链接
}

public IActionResult MyRedirectAction()
{
return RedirectToAction("Index","Hello"); //重定向(跳转)到内部链接
}

public async Task<IActionResult> MyAsync() //封装为async/await异步任务操作
{
await Task.Delay(1000); // 异步任务暂停1秒
return Content("ok"); // 返回Content文本内容
}

public IActionResult Index()
{
return View(); //返回视图(下节讲)
}

添加视图

◼ 视图(View)使用Razor模板引擎来创建,视图模板的扩展名:.cshtml

◼ 先了解两个重要概念:

​ ◼ 什么是模板引擎?

​ ◼ 什么是Razor?

模板引擎

◼ 模板引擎是为了使用户界面与业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎会生成一个标准的HTML文档

MVC_17


Razor

◼ Razor是一个视图模板引擎,它提供了优雅的方法将基于服务器的代码(如C#)嵌入到HTML页面中。

MVC_18

◼ 基于服务器的代码可以在网页传送给浏览器时,创建动态Web内容。

◼ 当一个网页被请求时,服务器在返回页面给浏览器之前,会先执行页面中基于服务器的代码。

◼ 通过服务器的运行,代码能执行复杂的任务,比如数据库业务。


如何创建视图

1
2
3
4
5
6
7
8
public class HelloController : Controller
{
public IActionResult Index()
{
// 在Action方法内部右键 → "添加视图"
return View();
}
}

关于上面return View();语句的解释

◼ 是 return View(“Index”); 的简写形式

◼ 表示返回一个名为”Index”的视图(扩展名为.cshtml)

◼ 如果视图名与方法同名则可参数省略不写

MVC_19


查看Index视图代码 – Razor模板文件

MVC_20


先了解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"] = "小明"; // 定义一个ViewData值,该值将传给Index视图
return View();
}

运行结果

MVC_21

运行原理图示

MVC_22


一个问题:导航栏和页脚从哪儿来滴?

MVC_23

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">
&copy; 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>

MVC_24


又一个问题:视图为何能和布局页结合?

◼ 答案:_ViewStart.cshtml

◼ 解释:该文件会在所有视图被执行之前先执行,可以将一些公共代码(如设置布局页)写在该文件中。

MVC_25

1
2
3
@{
Layout = "_Layout"; // 如果不想用使用布局,可设置 Layout = null;
}

_Layout:布局页名称

Layout对象用于设置布局


布局页用法示例:添加新导航链接

MVC_26

主要代码:红色部分

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>
<!-- 注:也可使用原生标签:<a class="nav-link text-dark" href="/Hello/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"); // 作用:将JS代码渲染到布局页指定的"Scripts"节中 (在浏览器中检查一下)
p.addEventListener("click", () => {
alert("欢迎!");
});
</script>
}

补充:如何加载外部样式或JS文件

MVC_27

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 ) // Action方法的参数用于接收前端URL传值(注:url请求必须用这些参数名来传值,方法参数可设置默认值)
{
string tmp = "name=" + name + ", n=" + n;
return tmp; // 思考:如何把接收到的值传到视图
}
}

运行示例:手工输入带参数的URL请求

MVC_28

  1. 是分隔符,后面跟查询字符串&用于分割查询参数:参数1=值&参数2=值…
  2. 参数名必须和action参数同名
  3. 参数值默认都是字符串(不加引号)

补充:后台代码调试

MVC_29


关于特殊参数 id 的传值:

1
2
3
4
5
6
7
8
public class HelloController : Controller
{
public string Details( int id ) //参数名为 id 的参数是特殊参数 (string类型也可以)
{
string tmp = "id=" + id;
return tmp;
}
}

id 参数传值情况

MVC_30

原因是在 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) // 接收URL传值
{
ViewData["Message"] = "Hello " + name; // String类型
ViewData["Count"] = n; // int类型
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元素

运行情况

MVC_31


示例:简单登录

MVC_32

(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
<!-- 数据提交到Login控制器Check方法 -->
<!-- 表单使用get方式传 -->
<form action="/Login/Check" method="get">
<!-- 注:传值要用name属性,否则数据不会被提交 -->
用户名:<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)	// 接收登录视图(Index)提交的两个数据
{
if (username.Equals("zz")&&pwd.Equals("888")) // 字符串比较用Equals()方法
{
ViewData["username"] = username;
ViewData["pwd"] = pwd;
return View(); // 新建Check视图
}
return RedirectToAction("Index"); // 跳转到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"] = "用户名或密码错"; // 在重定向前先设置好TempData信息
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信息(一次性) -->
@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

MVC_33


如何配置Session(重要)

(1)首先安装 Session 包:

MVC_34

(2)在Program.cs文件中配置Session:2处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
builder.Services.AddControllersWithViews();

//先添加Session服务
builder.Services.AddSession();
...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

//再使用Session,必须在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", "小明");
// Context是视图内置上下文对象
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", "小明"); // 在Index Action中设置Session值
return View();
}
public IActionResult Welcome()
{
return View(); // 在Welcome视图中读取Session值(跨请求)
}
}

Welcome视图代码:

1
2
3
4
5
6
7
8
9
@* 先导入命名空间(包) *@
@using Microsoft.AspNetCore.Http

@{
String name = Context.Session.GetString("username"); // 读取Session值
}

<!-- 显示一下 -->
<h3>欢迎 @name</h3>

运行结果:体会Session传值

① 先执行请求:https://localhost:端口/Hello/Index (创建Session值)

② 再执行请求:https://localhost:端口/Hello/Welcome (读取Session值)

MVC_35


综合示例 – 简单的Session权限控制

MVC_36

准备工作:

◼ 首先配置好Session。

◼ 然后新建两个控制器:

​ ◼ TestController:测试页面,添加简单的Session权限控制

​ ◼ LoginController:登录控制


TestController主要代码:

1
2
3
4
5
public IActionResult Index()
{
ViewData["Message"] = "欢迎"; // 使用ViewData传值给当前视图
return View(); // 新建Index视图
}

新建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"] = "登录";
}

<!-- 在wwwroot/css中创建login.css样式表文件 -->
<link rel="stylesheet" href="~/css/login.css" />

<!-- 数据提交到Login控制器Check方法 -->
<form id="login_form" class="form" action="/Login/Check"> <!-- 数据提交到Login控制器Check方法 -->
<div class="imgcontainer">
<!-- 在wwwroot中新建images文件夹并添加图片资源(支持拖放操作) -->
<img src="~/images/img_avatar.png" alt="Avatar" class="avatar">
</div>

<div class="container">
<label><b>用户名</b></label>
<input type="text" placeholder="请输入用户名" name="username"> <!-- name属性用于传值到后台 -->
<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>

运行一下:

MVC_37


LoginController添加Check登录验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
//登录验证
public IActionResult Check( String username, String pwd ) // 接收登录视图(Index)提交的两个数据
{
// 登录成功
// String.IsNullOrEmpty()判断字符串不为空,C#字符串比较用Equals()方法
if( !String.IsNullOrEmpty(username)&&username.Equals("zz") && !String.IsNullOrEmpty(pwd) && pwd.Equals("888") ) // 假设用户名是zz,密码是888
{
return RedirectToAction("Index", "Test"); // 登录成功后,将请求重定向到Test控制器的Index方法 (成功跳转)
}
// 登录失败
TempData["error"]="用户名或密码错"; // 使用TempData来传送登录错误信息
return RedirectToAction("Index", "Login"); // 将请求重定向到Login控制器的Index动作 (即重新登录)
}

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 ( 有登录凭证 ) // 添加拦截判断 (思路:通过Session传递登录凭证,在登录成功的时,将登录凭证存储在Session中)
{
ViewData["Message"] = "欢迎";
return View();
}
else
{
return RedirectToAction("Index", "Login"); // 重定向"/Login/Index"到登录
}
}
}

因此修改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 // usename值为空表明没有登录凭证,则重定向到Login的Index去重新登录
{
return RedirectToAction("Index", "Login");
}
}

测试拦截:未登录时

MVC_38

登录成功后:再次访问测试页面

MVC_39


添加注销功能:

LoginController.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LoginController : Controller
{
// 其他代码不变


// 注销
[Route("Logout")] // [Route]功能:自定义路由
public IActionResult Logout()
{
HttpContext.Session.Clear(); // 清除session信息
return RedirectToAction("Index", "Home"); // 重定向到网站首页
}
}
默认路由 自定义路由:更简洁
https://localhost:端口号/Login/Logout https://localhost:端口号/Logout

测试注销功能:手工注销

MVC_40

最后:在导航栏中添加注销链接

MVC_41

修改布局页右侧导航(根据登录状态,设置不同的导航内容):

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 href="/Logout">注销</a> -->
<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";
// @代码块中的html代码直接写
// color:后不用空格,文本求和后要空一格
<div style="color:@color;">求和 @a+@b=@s</div>
}
@for (int i = 0; i < 10; i++)
{
<span>第 @i 行<br /></span>
//此处前后都空一格,如果不想空格可用 @(i)
}

输出带有html标签的字符串

1
2
3
4
5
@{
string s = "<div style='color:red;'>测试文本</div>";
<h3>@s</h3> // 不解析html
<h3>@Html.Raw(s)</h3> // 解析
}
MVC_42

或用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 a = 1;
int b = 2;
// <div>@a</div>
}
<!-- 这是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 // 错误用法,纯文本前面要加 @:
// 正确用法:@:结果是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>

运行结果:

MVC_43

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View