MVC的定义
MVC(Model View Controller)模型-视图-控制器。
- M:处理数据的存取 - 包含一组数据的类和管理该数据的逻辑信息
- V:处理数据的显示 - 包含显示逻辑,用于显示Controller提供给它的模型中的数据
- C:处理用户的交互 - 处理HTTP请求,调用模型,选择一个视图来呈现该模型
Model
模型是描述用户界面上需要渲染的数据或是部分数据,但需要区分实体和模型的概念。
Entity-实体,是业务逻辑中使用的数据结构,一般与数据库中对应的表一致;
Model-模型,是经过转化处理的页面可接收的数据,比如数据库中的ID等敏感信息、bool类型、日期类型的转换等,均不适合直接绑定实体到页面,而需要转换成模型进行展示。
命名规则
- 符合类命名规范
- 标识符必须以字母或下划线_开头
- 使用Pascal规则命名类型,即首字母大写
- 使用能够反应类功能的词或词语
- 不要使用I、C、_等特定含义的前缀
- 自定义异常类应以Exception结尾
- 文件名要能反应类的内容,最好是和类同名
View
视图绑定模型
1 | @model MvcDemo.Models.Student |
Controller
一般不负责具体的逻辑业务和逻辑实现,但负责接收和响应请求。
主要作用:
- 接收请求并解析参数
- 调用Service执行具体的业务代码(可能包含参数校验)
- 捕获业务逻辑异常做出反馈
- 业务逻辑执行成功做出响应
Action
控制器中每个Public方法均可作为HTTP终结点调用,称之为Action,通常表示一个请求响应,默认返回一个IActionResult,表示一个页面,同时也可以返回其它数据类型,如String、int等。
1 | using Microsoft.AspNetCore.Mvc; |
控制器视图的数据传递
常见的方法:
- ViewData,一个Key/Value键值对集合,通过ViewData可以方便的进行数据对象的存储和获取,只是ViewData获取的对象是Object类型需要进行一定的类型转换
- ViewBag,一定Dynamic类型对象,需要在运行时进行解析操作
- Model,对强类型视图,既可以接收参数也可以回传数据
- TempData,支持页面跳转数据传递,但也只支持一次页面跳转
ViewData
定义:是控制器中一个ViewDataDictionary类型的属性,用来存储Key/Value的字典集合,在控制器中可以直接使用。
- ViewData特征
- ViewData是一个继承自ViewDataDictionary类的Dictionary对象,用来从Controller向对应的View传递值
- ViewData的只在当前的请求中有效,生命周期和View相同,其值不能在多个请求中共享
- 在重定向(Redirection)后,ViewData中存储的变量值将变为Null
- 在取出ViewData中的变量值时,必须进行合适的类型转换(隐式或显示)和空值检查
- ViewData示例
- 首先在Controller中对ViewData赋值
1
2
3
4
5
6public IActionResult Test()
{
ViewData.Add("Name", "Sleny");
ViewData.Add("Age", 18);
return View();
} - 在View中对ViewData中的值进行获取,格式为
@ViewData[Key]
1
2
3
4
5
6
7
8
9<h1>欢迎光临!~</h1>
<div>
<span>姓名:</span>
<span>@ViewData["Name"]</span>
</div>
<div>
<span>年龄:</span>
<span>@ViewData["Age"]</span>
</div>
ViewBag
定义:是一个动态类型变量(Dynamic),是C#4.0引入的新特性,变量类型会在运行时进行解析
- ViewBag特征
- ViewBag基本上是ViewData的包装,也是从Controller向View传值
- ViewBag只在当前的请求中有效
- 在重定向(Redirection)后,ViewBag中存储的变量值将变为Null
- 因为其是动态类型,所以取值时不需要进行类型转换
- ViewBag示例
- 首先在Controller中对ViewBag进行赋值
1
2
3
4
5
6public IActionResult Test2()
{
ViewBag.Name = "Sleny";
ViewBag.Age = 25;
return View();
} - 在View中对ViewBag中的值进行获取,格式为
ViewBag.属性名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@{
var name = ViewBag.Name;
var age = ViewBag.Age;
name = name + "Zl";
age = age + 1;
}
<h1>欢迎光临!~</h1>
<div>
<span>姓名:</span>
<span>@ViewBag.Name</span>
</div>
<div>
<span>年龄:</span>
<span>@ViewBag.Age</span>
</div>
<div>
<span>姓名:</span>
<span>@name</span>
</div>
<div>
<span>年龄:</span>
<span>@age</span>
</div>
Model
定义:主要作用就是在Controller和View之间进行数据交互
- Model特征
在控制器中初始化模型数据,然后通过View(Model)方法将创建的模型数据传递给视图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
27using Microsoft.AspNetCore.Mvc;
using MvcDemo.Models;
namespace MvcDemo.Controllers
{
public class HelloController : Controller
{
public IActionResult Index()
{
//创建的数据模型
var student = new Student()
{
Id = 1,
Name = "Job",
Age = 18,
Sex = "男"
};
// 传递给视图
return View(student);
}
public string Welcome()
{
return "Hello World.";
}
}
} - Model示例
- 首先创建模型
1
2
3
4
5
6
7
8
9
10
11// Model下创建模型数据
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
} - 控制器实例化模型
1
2
3
4
5
6
7
8
9
10
11
12
13// Controller下编写实例化代码
public IActionResult Index()
{
var student = new Student()
{
Id = 1,
Name = "Sleny",
Age = 22,
Sex = "男"
};
return View(student);
} - 视图指定模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// Views创建视图,并指定模型
// @Model 完整类名
// @Model.属性名
@model MvcDemo.Models.Student
<h1>欢迎光临!~</h1>
<div>
<span>学号:</span>
<span>@Model.Id</span>
</div>
<div>
<span>姓名:</span>
<span>@Model.Name</span>
</div>
<div>
<span>年龄:</span>
<span>@Model.Age</span>
</div>
<div>
<span>性别:</span>
<span>@Model.Sex</span>
</div>
TempData
定义:ViewData和ViewBag都是一次性传递数据,若跳转其它页面,则无法进行获取,但TempData支持页面跳转后仍可获取内容
- TempData特征
- 类型为TempDataDictionary
- 用于把数据从一个Action方法传到另一个Action方法,两个Action可以不在同一个Controller中,也可在同一个中
- TempData示例
- 首先在Controller中创建两个视图Test3和Test4,分别创建ViewData和TempData,并赋予Name名称的值,再让页面从Test3 -> Test4,最后在Test4中分别获取两个值
1
2
3
4
5
6
7
8
9
10
11public IActionResult Test3()
{
ViewData.Add("Name", "ViewData");
TempData.Add("Name", "TempData");
return View();
}
public IActionResult Test4()
{
return View();
} - 其次在Views下创建两个页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14// Test3.cshtml
<a href="~/Hello/Test4">跳转Test4</a>
// Test4.cshtml
<h1>欢迎光临!</h1>
<div>
<span>姓名:</span>
<span>@ViewData["Name"]</span>
</div>
<div>
<span>姓名:</span>
<span>@TempData["Name"]</span>
</div> - 最终可以发现:(1) ViewData在页面传递后,前一个Action添加的键值已经被清除;(2) TempData中的值在页面跳转后仍然保存;(3) TempData在第二次请求后会被清空,第三次请求则获取不到
默认路由
当通过模板创建项目时,默认会支持MVC路由,但如果创建的是空项目时,则需要手动添加,主要有以下三行代码。
- 注入支持控制器视图服务
1
2// Add services to the container.
builder.Services.AddControllersWithViews(); - 使用路由
1
app.UseRouting();
- 默认路由配置,即缺省配置
1
2
3app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
默认约定
- Controller的约定
- 所有的Controller必须放在Controllers文件夹中,并以[Name]Controller的方式命名,如:HomeController
- 每个Controller都对应View中的一个文件夹,文件夹的名称与Controller名相同,如:Home
- Controller中的方法名都对应一个View视图且View的名字和Action的名字相同
- 控制器必须是非静态类,并且要实现IController接口,默认继承自Controller
- Controller类型可以放在其他项目中
- View的约定
- 所有视图必须放到Views目录下
- 不同控制器的视图用文件夹进行分隔,每个控制器都对应一个视图目录
- 一般视图名字跟控制器的Action相对应
- 多个控制器公共的视图放到Shared,如公共的错误页、列表模板页、表单模板页等
页面布局
页眉与页脚均由Layout布局模板生成,如果不需要加载布局模板内容或者指定新的Layout布局模板,可以在页面中进行设置
1 | @{ |
参数的传递
接收URL参数
- 参数名称自动匹配
如果Action的形参名称和QueryString的Key一致,则MVC框架会自动绑定参数的值,不用手动获取【注】:如果参数绑定的名称和QueryString的Key不一致,可以使用FromQueryAttribute强制指定绑定的Key的名称1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public IActionResult ShowStudent(int id, string name, int age, string sex)
{
var student = new Student()
{ Id = id, Name = name, Age = age, Sex = sex };
return Json(student);
}
// URL访问:http://localhost:5228/Hello/ShowStudent?id=2&name=Lily&age=19&sex=%E5%A5%B3
// --->
// {"id":2,"name":"Lily","age":19,"sex":"\u5973"}
// 如若中文被重新编码,则可以通过在Program.cs中添加如下代码来修复
// Add services to the container.
builder.Services.AddControllersWithViews().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
}); - Request.Query获取参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public IActionResult ShowStudent2()
{
var id = Request.Query["id"];
var name = Request.Query["name"];
var age = Request.Query["age"];
var sex = Request.Query["sex"];
var student = new Student()
{
Id = string.IsNullOrEmpty(id) ? 0 : int.Parse(id),
Name = name,
Age = string.IsNullOrEmpty(age) ? 0 : int.Parse(age),
Sex = sex
};
return Json(student);
} - 通过路由获取参数【注】:如果Action的形参名称和RouteAttribute模板中的名称不同,可以使用FromRoute强制指定解析的名称。
1
2
3
4
5
6
7
8
9
10
11
12[ ]
public IActionResult ShowStudent3(int id, string name, int age, string sex)
{
var student = new Student()
{
Id = id,
Name = name,
Age = age,
Sex = sex
};
return Json(student);
}
接收Body参数
Request.Body是一个Stream对象,通过获取流对象中的内容,然后进行转化,就可以获取参数。
1 |
|
通过Body获取,然后JsonConvert进行反序列化,前提是Body内容是JOSN格式,否则不能进行反序列化,主要应用于接口调用,Ajax方式请求等。
接收Form表单
最常用的就是Form表单传递参数,客户端将所有需要传递的内容包括在Form表单内容,在服务端Action中通过Request.Form[“Kye”]进行获取。
1 | // Hello/Form.cshtml |
通过模型接收参数
一般通常为了简便,会直接采用模型来接收参数,如果模型的属性名和参数的Key一致,则可以自动匹配
1 | public IActionResult ShowStudent4(Student student) |
【注】:Get/Post方法均可以采用模型接收参数