View 部分是简单的 HTML 代码, Controller 是简单的 .NET 类
ASP.NET MVC的请求流程如下:
- 触发 Controller
- 依据行为 Controller 创建 Model 对象, Model 反过来通过调用数据接口层来向 Model对象填充数据
- 填充完的 Model 对象将数据传输给 View 层, 然后展示出来
控制器类的命名一定要含有 Controller 关键字, 含有 Controller 的是一个类名称, 不含的则是控制器名称
Action 则是控制器里的一个 public 的方法, 非 public 方法不能被 Web 调用, 如果需要实现 public 的非 Action 方法则需要给方法加上 [NonAction]
注解, 注意该方法的返回类型, 如果是类的话则会返回ToString()
方法的返回值
IIS 访问时使用的 URL 是 ${ControllerName}/${ActionName}, 注意 ${ControllerName} 不含关键字Controller
在 Action 上右击则可使用 VS 创建 View, View 总是与特定的 Controller 相关联被放到一个特殊的文件夹中(Views/${ControllerName}), View 只有放在正确的文件夹下才能被 Controller 访问, 而 Views/Shared 文件夹下的视图是所有控制器可以共用的
View 的作用是创建 ViewResult
对象, 无参调用 View
函数将自动通过 ActionName
来查找视图
ViewResult
内部创建ViewPageActivator
对象ViewResult
选择正确的ViewEngine
,并将ViewPageActivator
对象作为参数传输给ViewEngine
构造函数ViewEngine
创建View
类的对象ViewResult
调用View
类的RenderView
方法
ActionResult
是一个抽象类, ViewResultBase
是 ActionResult
的子类, 而 ViewResult
是 ViewResultBase
的子类
ViewResult
呈现了一个完整的 HTML 响应, 而 ContentResult
呈现的是一个纯文本响应, 就像返回一个纯 String
类型意义, 所不同的是, ContentResult
是一个 ActionResult
的子类型, 包装 String
结果
ViewData 是一个字典, 它存储了 Controller 传输给 View 的数据(以 Object
类型, 所以取出时需要强制类型转换), Controller 将向 ViewData 字典添加条目, 然后 View 从这个字典里读取, 一个例子如下
- Model 中定义数据类型:
// Models/Employee.cs
namespace WebDemo.Models
{
public class Employee
{
public string FirstName { set; get; }
public string LastName { set; get; }
public int Salary { set; get; }
}
}
- Controller 中添加数据:
// Controllers/TestController.cs
using System.Web.Mvc;
using WebDemo.Models;
namespace WebDemo.Controllers
{
public class TestController : Controller
{
public ActionResult GetView()
{
Employee employee = new Employee();
employee.FirstName = "Sukesh";
employee.LastName = "Marla";
employee.Salary = 20000;
ViewData["Employee"] = employee;
return View("MyView");
}
}
}
- View 中取出并呈现数据(两种方法):
<!-- Views/Test/MyView.cshtml -->
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MyView</title>
</head>
<body>
@{
WebDemo.Models.Employee employee = (WebDemo.Models.Employee)ViewData["Employee"];
}
<b>Employee Details:</b> <br/>
Employee Name: @(employee.FirstName + " " + employee.LastName) <br/>
Employee Salary: @employee.Salary.ToString("C");
@{
Response.Write(employee.FirstName + " " + employee.LastName + "\r\n" + employee.Salary);
}
</body>
</html>
之后访问 http://localhost:${portNum}/Test/GetView 进行访问, 值得注意的是, 输出中 Response.Write
的数据在最开始
![](/qrcode.jpg)
View 层代码中 @
后有 {}
则可用于 CSharp 代码, 否则只用于展示变量或者表达式的值
ViewBag
类似于 ViewData
的语法糖, 添加了动态特征, 两者可以混合使用, 使用方法是:
- Controller 代码中添加数据
ViewBag.Employee = employee;
- View 中进行解析
WebDemo.Models.Employee employee = (WebDemo.Models.Employee)ViewBag.Employee;
ViewData
与 ViewBag
传输类型都是 Object
, 强制类型转换会有性能问题, Key 值不正确会导致运行时出错, 前后端分离不优雅, Views 可以使用强类型一定程度解决上面三个问题
使用强类型 Views
的方法如下:
- Model 层与上方的一致, 使用 Employee
- Controller 中添加数据, 注意这里不再使用 ViewData 或 ViewBag 而是通过 View 进行传递
// Controllers/TestController.cs
using System.Web.Mvc;
using WebDemo.Models;
namespace WebDemo.Controllers
{
public class TestController : Controller
{
public ActionResult GetView()
{
Employee employee = new Employee();
employee.FirstName = "Sukesh";
employee.LastName = "Marla";
employee.Salary = 20000;
return View("MyView", employee);
}
}
}
- 指定 View 成为某个类型的强类型视图, 并使用相应数据, 注意首行的
@model ${className}
用于指定强类型视图, 一个 View 只能指定一个 model, 需要使用 Controller 中数据的时候直接使用@Model.${attributeName}
<!-- Views/Test/MyView.cshtml -->
@model WebDemo.Models.Employee
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MyView</title>
</head>
<body>
<b>Employee Details:</b> <br />
Employee Name: @Model.FirstName @Model.LastName
@if (Model.Salary > 15000)
{
<span style="background-color:yellow">
Employee Salary: @Model.Salary.ToString("C")
</span>
}
else
{
<span style="background-color:green">
Employee Salary: @Model.Salary.ToString("C")
</span>
}
</body>
</html>
ViewModel 是 MVC 应用中没有声明出的层, 适用于 Model 和 View 之间并且为 View 作为一个数据容器, Model 特指业务数据, 它基于业务和数据结构创建, ViewModel 特指 View 数据, 它基于视图 View 创建, View 将会是一个以 ViewModel 为强类型的视图, 但是实际上就是将 Model 多封装了一层(这样就可以使用多 Model), 在原始的业务数据之上加上了其他的所需数据并将需要在 View 层中使用的逻辑也移动到 ViewModel 层中, 数据与显示的解耦和与冗余, 增加了设计的弹性, 一个例子:
- Model 层仍然不变, 使用上方的 Employee
- 新建一个 ViewModels 文件夹并新建一个 ViewModel 类
// ViewModels/EmployeeViewModel.cs
namespace WebDemo.ViewModels
{
public class EmployeeViewModel
{
// 处理 Model 中的数据
public string EmployeeName { get; set; }
public string Salary { get; set; }
// 处理 View 中的逻辑
public string SalaryColor { get; set; }
// 添加无关数据
public string UserName { get; set; }
}
}
- Controller 层中实际上是对 Model 多了一层封装
// Controllers/TestController.cs
using System.Web.Mvc;
using WebDemo.Models;
using WebDemo.ViewModels;
namespace WebDemo.Controllers
{
public class TestController : Controller
{
public ActionResult GetView()
{
// Model 层
Employee employee = new Employee();
employee.FirstName = "Sukesh";
employee.LastName = "Marla";
employee.Salary = 20000;
// ViewModel层
EmployeeViewModel employeeViewModel = new EmployeeViewModel();
employeeViewModel.EmployeeName = employee.FirstName + " " + employee.LastName;
employeeViewModel.Salary = employee.Salary.ToString("C");
if (15000 > employee.Salary)
{
employeeViewModel.SalaryColor = "green";
}
else
{
employeeViewModel.SalaryColor = "yellow";
}
employeeViewModel.UserName = "Admin";
return View("MyView", employeeViewModel);
}
}
}
- View 层中还是使用原有的方法, 只不过使用了其他数据源
<!-- Views/Test/MyView.cshtml -->
@using WebDemo.ViewModels
@model EmployeeViewModel
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MyView</title>
</head>
<body>
Hello @Model.UserName
<hr />
<div>
<b>Employee Details</b>
Employee Name: @Model.EmployeeName <br />
<span style="background-color:@Model.SalaryColor">
Employee Salary: @Model.Salary
</span>
</div>
</body>
</html>