文章目录
Controller请求及相应流程说明
- MVC中的Controller是一个类文件,控制器继承自Microsoft.AspNetCore.Mvc.Controller。
- Controller类名称后缀为Controller,比如HomeController、StudentController。
- 当来自浏览器的请求到达我们的应用程序时,MVC中的Controller会处理传入的HTTP请求并响应用户操作。
- Controller类中包含一组公共方法。Controller类中的这些公共方法称为操作方法,通过这些操作方法可以处理传入的HTTP请求。
- 假设读者在浏览器地址栏中输入了http://localhost:5000/home/details并按Enter键。
- URL"/home/details"会映射到HomeController中的Details公共操作方法。
- 映射由应用程序中定义的路由规则完成。
- 请求到达Controller操作方法。作为处理该请求的一部分,Controller创建Model。
- Controller通过依赖注入注册的服务来查询模型数据。比如,我们要查询学生的数据,就需要通过HomeController依赖的IStudentRepository服务。
- IStudentRepository服务使用构造函数注入的方式注册到HomeController。
- 请注意,我们将注入的依赖项分配给了readonly字段。这是一个很好的做法,因为这可以防止在使用的过程中意外地为其分配另一个值。
- 当Controller拥有所需地模型数据时,比如我们正在提供的服务或RESTful API,它就可以简单地返回该模型数据。
从Controller中返回JSON数据
以下代码返回JSON数据,请注意,将Details()方法的返回类型设置为JsonResult,因为我们显式返回JSON类型数据。在这种情况下,Details()方法始终返回JSON类型数据。
public class HomeController:Controller
{
private readonly IStudentRepository _studentRepository;
public HomeController(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public JsonResult Details()
{
Student model = _studentRepository.GetStudent(1);
return Json(model);
}
}
现在我们运行项目。浏览器从服务端获得了JSON数据,并将其自动转移为Unicode字符集编码的文本。为了方便显示,需要额外安装一个插件(我用的是JSON Formatter,类似插件很多),格式化后的效果如图所示:
因为我们指定了返回类型是JSON,所以浏览器不需要进行内容协商并忽略Accept Header。
从控制器中返回内容协商内容
内容协商属于HTTP,HTTP提供了内容协商方法,允许客户端和服务端有这样的协定。通过这些方法,单一的URL就可以代表不同的资源(如同一个网站页面的法语版和英语版),这些不同的版本称为变体。
举个例子,如果某个站点有使用法语和使用英语的两种用户,需要用这两种语言提供网站站点信息。理想情况如下,服务端应当向英语用户发送英文版,向法语用户发送法文版——用户只要访问网站主页就能得到相应语言的内容。
修改HomeController中Details()方法的返回类型,代码如下:
public class HomeController:Controller
{
private readonly IStudentRepository _studentRepository;
public HomeController(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public ObjectResult Details()
{
Student model = _studentRepository.GetStudent(1);
return new ObjectResult(model);
}
}
请注意,上面的示例代码遵循内容协商,但是为了让代码能够返回XML格式的数据,我们必须通过调用ConfigureServices()方法中的AddXml SerialzerFormatters()。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(a => a.EnableEndpointRouting = false).AddXmlSerializerFormatters();
services.AddSingleton<IStudentRepository, StudentRepository>();
}
这样,如果将请求头设置为application/xml,则返回XML数据。如果将请求头设置为application/json,则返回JSON数据。
从Controller返回View
以下实例代码返回View类型。请注意,在返回View时要将Details()方法的返回类型设置为ViewResult。
public class HomeController:Controller
{
private readonly IStudentRepository _studentRepository;
public HomeController(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public ViewResult Details()
{
Student model = _studentRepository.GetStudent(1);
return View(model);
}
}
此时如果运行应用程序并导航到http://localhost:5000/home/details,我们会收到以下错误。这是因为还没有创建所需的View文件。
MVC中的View
- 用于显示Controller提供给它的Model的业务数据。
- 视图是带有嵌入Razor标记的HTML模板。
- 如果编程语言是C#,则视图文件具有.cshtml扩展名。
比如,在MVC项目中,我们有两个Controller:HomeController和StudentController。
HomeController有以下3个操作方法:
- Details()。
- Edit()。
- Index()。
StudentController有以下3个操作方法:
- Details()。
- Edit()。
- List()。
视图文件结构
我们看一下视图文件夹结构(稍后会有创建步骤),如图所示:
- 所有HomeController的视图都位于Views文件夹中的Home文件夹中。
- 所有StudentController的视图都位于Views文件夹中的Student文件夹中。
视图发现
查看HomeController,其中只有一个操作方法Details()。
- Details() 方法会调用View() 方法返回一个视图。
- View() 方法是由基类Controller提供。
public class HomeController:Controller
{
public ViewResult Details()
{
return View();
}
}
Details() 操作方法会返回一个视图,因此默认情况下MVC会查找具有相同名称且扩展名为.cshtml的视图文件。在这种情况下,它会查找Details.cshtml。它按指定的顺序查找此文件。
Controller的名称是HomeController,它在/Views/Home/文件夹中,如果在Views/Shared/文件夹中找到了视图文件,则视图会生成HTML响应发出请求的客户端。
如果找不到视图文件,则会报错。
现在可以在项目的根目录创建路径为/Views/Home/Details.cshtml的文件夹和视图文件,步骤如下:
- 创建Views文件夹和Home文件夹。
- 右击Home文件夹,选择添加➡新建项。
- 在弹出的窗口中,选泽Razor视图,修改名称为Details.cshtml。
View重载方法
ASP.NET Core MVC中还提供了几个View()重载方法。如果我们在使用下面的View()重载方法,则它将查找与Action()方法同名的视图文件。
- View()。
- View(object model)。
比如,我们从HomeController的Details() 方法返回一个View()。默认情况下,MVC在Views/Home文件夹中查找名为Details .cshtml的视图文件。
public class HomeController:Controller
{
public ViewResult Details()
{
return View();
}
}
自定义视图发现
如果读者不喜欢默认约定,则可以使用View(string viewName)方法的重载版本,该方法将view.Name作为参数,以查找具有读者自定义名称的视图文件。
在以下示例中,MVC查找名为Test.cshtml而不是Details.cshtml的视图文件。如果我们没有指定视图名称,则它会查找Details.cshtml。
public class HomeController:Controller
{
public ViewResult Details()
{
return View("Test");
}
}
指定视图文件路径
我们可以指定视图名称或视图文件路径。在以下示例中,我们指定了视图文件的绝对路径。因此,在这种情况下,MVC会在MyViews文件夹中查找名为Test.cshtml的视图文件。如果我们没有指定视图文件的路径,则默认情况下,MVC会在Views/Home文件夹中查找Details.cshtml文件。
public class HomeController:Controller
{
public ViewResult Details()
{
return View("MyViews/Test.cshtml");
}
}
请注意以下几点:
- 使用绝对路径时,必须加上.cshtml扩展名。
- 如果使用绝对路径,则MVC会从项目的根目录开始搜索,推荐使用/或~/。
以下三行代码实现的功能是一样的:
return View("MyViews/Test.cshtml");
return View("/MyViews/Test.cshtml");
return View("~/MyViews/Test.cshtml");
相对视图路径
指定视图文件路径时,我们也可以使用相对路径。使用相对路径,我们可以不指定文件扩展名.cshtml。在以下示例中,MVC会在路径为Views/Test的文件夹中查找Update.cshtml文件。
📎 ../表示父级目录,./表示当前目录,/表示根目录。
public class HomeController:Controller
{
public ViewResult Details()
{
return View("../Test/Update");
}
}
其他View()重载方法
重载方法 | 描述 |
---|---|
View(object model) | 使用此重载方法将模式数据从控制器传递到视图。 |
View(string viewName,object model) | 传递视图名称和模型数据 |
从Controller传递数据到View
数据从Controller传递到View的方法
在ASP.NET Core MVC中,有以下3种方法可以将数据从Controller传递到View。
- 使用ViewData。
- 使用ViewBag。
- 使用强类型模型对象,这也叫做强类型视图。
通过使用ViewData或ViewBag传递数据,我们会创建一个弱类型的视图。
使用ViewData将数据从Controller传递到View
我们希望将HomeController的Details()操作方法种的Student模型数据和视图页面的Title传递给Details.cshtml视图。因此,修改HomeController中的Details()方法,如下所示:
public ViewResult Details()
{
Student model = _studentRepository.GetStudent(1);
//使用ViewData将PageTitle和Student模型传递给View
ViewData["PageTitle"] = "Student Details";
ViewData["Student"] = model;
return View();
}
📎在View中访问ViewData
要将HomeController的Details()方法中的ViewData传递到View,需要修改Details.cshtml文件中的代码,如下所示:
<!--Studet类所在的命名空间为WebApplication12.Models,读者根据自己实际情况引用-->
@using WebApplication12.Models
<html>
<head>
<title>Details</title>
</head>
<body>
<h3>@ViewData["PageTitle"]</h3>
@{ var student = ViewData["Student"] as Student;}
<div>
姓名:@student.Name
</div>
<div>
邮箱:@student.Email
</div>
<div>
主修科目:@student.Major
</div>
</body>
</html>
运行项目后的结果如图所示:
📎弱类型对象DataView说明
-
ViewData时弱类型的字典(dictionary)对象。
-
我们使用string类型的键值对来存储和查询ViewData字典中的数据。可以从ViewData字典中直接访问数据,而无须将数据转换为string类型。
-
如果访问的是任何其他类型的数据,则我们需要将其显示的地转换为我们期望的类型。
-
在本例中,我们声明Student对象可以分别访问Student 对象的Name、Email和Major属性。
-
ViewData在运行时会进行动态解析,不提供编译时类型检查,因此我们不会获得智能提示。由于我们没有智能提示,因此会导致编写代码的速度降低,拼写错误和打错的可能性也变大。
-
我们只会在项目运行时知道这些错误。
-
出于这个原因,我们通常不使用View Data。
-
当使用ViewData时,我们最终会创建一个弱类型的视图。
实际上,ViewBag是ViewData的包装器。通过ViewData,我们使用string类型的键名来存储和查询数据。而对于ViewBag,我们则使用的是动态属性而不是字符串键。
使用ViewBag将数据从Controller传递到View
同样,修改HomeController中的Details()操作方法,如下所示。请注意,我们在ViewBag中使用的是动态属性而不是字符串键值对。
public ViewResult Details()
{
Student model = _studentRepository.GetStudent(1);
//将PageTitle和Student模型对象存储在ViewBag
//我们正在使用动态属性PageTitle和Student
ViewBag.PageTitle = "学生详情";
ViewBag.Student = model;
return View();
}
在View中访问ViewBag,要将ViewBag的数据从HomeController的Details()操作方法传递到View。
修改Details.cshtml视图文件中的代码,图下所示:
<head>
<title>Details</title>
</head>
<body>
<h3>@ViewBag.PageTitle</h3>
<div>
姓名:@ViewBag.Student.Name
</div>
<div>
邮箱:@ViewBag.Student.Email
</div>
<div>
主修科目:@ViewBag.Student.Major
</div>
</body>
</html>
请注意,我们使用相同的动态属性PageTitle和Student来访问ViewBag数据。
运行项目,结果如图所示:
ViewData与ViewBag的对比
- ViewData与ViewBag两者都可以从控制器传递数据到视图。
- ViewBag是ViewData的包装器。
- 它们都是创建弱类型的视图。
- 在ViewData中使用字符串键来存储和查询ViewData字典中的数据。在ViewBag中使用动态属性来存储和查询数据。
- ViewData和ViewBag都是在运行时动态解析。
- ViewData和ViewBag不提供编译时类型检查,因此我们不能得到智能提示。
在ASP.NET Core MVC中创建一个强类型视图
📎强类型视图——Controller代码
将数据从控制器传递到视图的首选方法是使用强类型视图。要创建强类型视图,请在控制器中将模型对象传递给View()方法。请注意,在下面的示例中,我们将Student模型对象传递给View()方法。
public ViewResult Details()
{
Student model = _studentRepository.GetStudent(1);
ViewBag.PageTitle = "学生详情";
return View(model);
}
📎强类型视图——View代码
要创建强类型视图,请使用@model指令在视图中指定模型类型。
在下面的示例代码中,我们告诉视图它将使用命名空间WebApplication12.Models中的对象。使用@model指令将Student对象作为模型。
请注意,在指令(@model
)中,m是小写的。而要访问模型对象属性时,我们使用@Model
。在@Model
中,M是大写的。
在下面的示例代码中,访问Student对象属性,如我们正在使用的姓名、邮箱和主修科目,对应@Model.Name
、@Model.Email
和@Model.Major
。
@model WebApplication12.Models.Student
<html>
<head>
<title>Details</title>
</head>
<body>
<h3>@ViewBag.PageTitle</h3>
<div>
姓名:@Model.Name
</div>
<div>
邮箱:@Model.Email
</div>
<div>
主修科目:@Model.Major
</div>
</body>
</html>
📎强类型视图优点
强类型视图与ViewData、ViewBag不同,强类型视图提供编译时类型检查与智能提示。通过智能提示,我们可以提高工作效率,错误拼写的概率几乎为零。如果我们确实犯了错,则将在编译时看到错误信息,而不是在运行时才看到它们。
因此,我们建议使用强类型视图将数据从控制器传递到视图。
总结
📃MVC的流程回顾。
- 当来自浏览器的请求到达我们的应用程序时,MVC中的控制器会处理传入的HTTP请求并响应用户操作。
- 控制器构建Model。
- 如果我们正在构建API,则将模型数据返回给调用方(比如:返回JSON数据)。
- 或选择View并将Model数据传递到View,然后视图生成所需的HTML来显示数据。
📃视图文件的特点如下:
- 视图文件具有.cshtml的扩展名。
- 视图文件是带有嵌入Razor标记的HTML模板。
- 可能包含Controller提供给它的Model业务数据。
📃视图发现的特点如下:
- View()或View(object model):查找与操作方法同名的视图文件。
- 关于View(string viewName)方法的内容,需要注意以下几点:
- 可查询自定义名称的视图文件。
- 读者可以指定视图名称或者试图路径。
- 视图文件路径可以是绝对路径,也可以是相对路径。
- 绝对路径必须指定.cshtml扩展名。
- 使用相对路径时,不用带扩展名.cshtml。
- 将数据从控制器传递到View的3种方法如下:
- 使用ViewData
- ViewData是弱类型的字典对象。
- 使用string类型的键值,存储和查询ViewData字典中的数据。
- 使用ViewBag
- ViewBag是ViewData的包装器。
- 它们都创建了一个弱类型的视图。
- ViewData使用字符串键来存储和查询数据。
- ViewBag使用动态属性来存储和查询数据。
- 它们均是在运行时解析。均不提供编译时类型检查,没有智能提示。
- 强类型视图
- 首选方法是使用强类型视图,将数据Controller传递到View。
- 请在View中使用@model指令指定Model类型。
- 使用@Model访问模型对象属性。
- 强类型视图提供编译时类型检查和智能提示。
- 使用ViewData