asp.net core系列 40 Web 应用MVC 介绍与详细示例
一. MVC介绍
MVC架构模式有助于实现关注点分离。视图和控制器均依赖于模型。 但是,模型既不依赖于视图,也不依赖于控制器。 这是分离的一个关键优势。 这种分离允许模型独立于可视化展示进行构建和测试。ASP.NET Core MVC 包括以下功能:
路由、模型绑定、模型验证、依赖关系注入、筛选器、区域、Web API、可测试性、Razor 视图引擎、强类型视图、标记帮助程序、 视图组件。
(1) 路由
ASP.NET Core MVC 建立在 ASP.NET Core 的路由之上,是一个功能强大的 URL 映射组件,可用于生成具有易于理解和可搜索 URL 的应用程序。关于路由知识,请查看asp.net core 系列第5,6章。
(2) 模型绑定(Model)
ASP.NET Core MVC 模型绑定将客户端请求数据(窗体值(form)、路由数据、查询字符串参数、HTTP 头)转换到控制器(Controller)可以处理的对象中。 因此,控制器逻辑不必找出传入的请求数据;它只需具备作为其Action方法的参数的数据。下面的LoginViewModel就是一个模型类。
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
(3) 模型验证
ASP.NET Core MVC 通过使用数据注释验证属性。 验证属性在值发送到服务端前,在客户端上进行检查。并在调用控制器action前在服务端上进行检查。
using System.ComponentModel.DataAnnotations; public class LoginViewModel {[Required][EmailAddress]public string Email { get; set; }[Required][DataType(DataType.Password)]public string Password { get; set; }[Display(Name = "Remember me?")]public bool RememberMe { get; set; } }//服务端控制器action验证 public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) {//验证模型 if (ModelState.IsValid){// work with the model }return View(model); }
(4) 依赖注入
依赖关系注入除了在控制器上通过构造函数请求所需服务,还可以使用@inject 指令,应用在视图文件上。下面是视图页面上通过依赖注入获取服务对象。
@inject SomeService ServiceName <!DOCTYPE html> <html lang="en"> <head><title>@ServiceName.GetTitle</title> </head> <body><h1>@ServiceName.GetTitle</h1> </body> </html>
(5) 筛选器
筛选器帮助开发者封装,横切关注点,例如异常处理或授权。筛选器允许action方法运行自定义预处理和后处理逻辑,并且可以配置为在给定请求的执行管道内的特定点上运行。筛选器可以作为属性应用于控制器或Action(也可以全局运行)。例如MVC 授权筛选器。
[Authorize]public class AccountController : Controller
(6) 区域
区域用在大型Web开发上, 是功能分组的方法。区域是应用程序内的一个 MVC 结构。 例如,具有多个业务单位(如结账、计费、搜索等)的电子商务应用。每个单位都有自己的逻辑组件视图、控制器和模型。
(7) Web API
除了作为生成网站的强大平台,ASP.NET Core MVC 还对生成 Web API 提供强大的支持。 可以生成可连接大量客户端(包括浏览器和移动设备)的服务,前面章节有讲过。
(8) 可测试性
框架对界面和依赖项注入的使用非常适用于单元测试,并且该框架还包括使得集成测试快速轻松的功能(例如 TestHost 和实体框架的 InMemory 提供程序)
(9) Razor 视图引擎
ASP.NET Core MVC 视图使用 Razor 视图引擎呈现视图。 Razor 是一种紧凑、富有表现力且流畅的模板标记语言,用于使用嵌入式 C# 代码定义视图。 Razor 用于在服务器上动态生成 Web 内容。 可以完全混合服务器代码与客户端内容和代码。例如下面嵌入 C#代码,循环输出5组li标记
<ul>@for (int i = 0; i < 5; i++) {<li>List item @i</li>} </ul>
(10) 强类型视图
可以基于模型强类型化 MVC 中的 Razor 视图。 控制器可以将强类型化的模型传递给视图,使视图具备类型检查和 IntelliSense 支持。例如,以下视图呈现类型为 IEnumerable<Product>
的模型:
@model IEnumerable<Product> <ul>@foreach (Product p in Model){<li>@p.Name</li>} </ul>
(11) 标记帮助程序
标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。 例如,内置 LinkTagHelper 可以用来创建指向 AccountsController
控制器中
Login
的方法链接
<p>Thank you for confirming your email.Please <a asp-controller="Account" asp-action="Login">Click here to Log in</a>.</p>
(12) 视图组件
通过视图组件可以包装呈现逻辑并在整个应用程序中重用它。 这些组件类似于分部视图,但具有关联逻辑。
二. 完整示例介绍(项目StudyMVCDemo)
2.1 安装EF数据提供程序
这里使用内存数据库Microsoft.EntityFrameworkCore.InMemory,Entity Framework Core 和内存数据库一起使用, 这对测试非常有用。
PM> Install-Package Microsoft.EntityFrameworkCore.InMemory
2.2 新建数据模型类(POCO )和EF上下文类
public class MvcMovieContext : DbContext{public MvcMovieContext(DbContextOptions options): base(options){}public DbSet<Movie> Movie { get; set; }}
public class Movie{public int Id { get; set; }public string Title { get; set; }[DataType(DataType.Date)]public DateTime ReleaseDate { get; set; }public string Genre { get; set; }public decimal Price { get; set; }}
2.3 初始化数据
public static void Main(string[] args){var host = CreateWebHostBuilder(args).Build();using (var scope = host.Services.CreateScope()){var services = scope.ServiceProvider;try{//var context = services.GetRequiredService<MvcMovieContext>();//程序运行时,使用EF迁移生成数据,用在关系型数据库//context.Database.Migrate(); SeedData.Initialize(services);}catch (Exception ex){var logger = services.GetRequiredService<ILogger<Program>>();logger.LogError(ex, "An error occurred seeding the DB.");}}host.Run();}
public static class SeedData{/// <summary>/// 初始化数据/// </summary>/// <param name="serviceProvider"></param>public static void Initialize(IServiceProvider serviceProvider){using (var context = new MvcMovieContext(serviceProvider.GetRequiredService<DbContextOptions<MvcMovieContext>>())){// 如果有数据返回if (context.Movie.Any()){return; // DB has been seeded }context.Movie.AddRange(new Movie{Title = "When Harry Met Sally",ReleaseDate = DateTime.Parse("1989-2-12"),Genre = "Romantic Comedy",Price = 7.99M},new Movie{Title = "Ghostbusters ",ReleaseDate = DateTime.Parse("1984-3-13"),Genre = "Comedy",Price = 8.99M},new Movie{Title = "Ghostbusters 2",ReleaseDate = DateTime.Parse("1986-2-23"),Genre = "Comedy",Price = 9.99M},new Movie{Title = "Rio Bravo",ReleaseDate = DateTime.Parse("1959-4-15"),Genre = "Western",Price = 3.99M});context.SaveChanges();}}}
View Code
2.4 添加控制器类(MoviesController)
public class MoviesController : Controller{private readonly MvcMovieContext _MvcMovieContext;public MoviesController(MvcMovieContext MvcMovieContext){this._MvcMovieContext = MvcMovieContext;}}
2.5 列表页Movies/index.cshtml
// GET: /<controller>/public IActionResult Index(){var movies = _MvcMovieContext.Movie.ToList();return View(movies);}
@model IEnumerable<StudyMVCDemo.Models.Movie>@{ViewData["Title"] = "Index"; }<h1>Index</h1><p><a asp-action="Create">Create New</a> </p> <table class="table"><thead><tr><th>@Html.DisplayNameFor(model => model.Title)</th><th>@Html.DisplayNameFor(model => model.ReleaseDate)</th><th>@Html.DisplayNameFor(model => model.Genre)</th><th>@Html.DisplayNameFor(model => model.Price)</th><th></th></tr></thead><tbody>@foreach (var item in Model){<tr><td>@Html.DisplayFor(modelItem => item.Title)</td><td>@Html.DisplayFor(modelItem => item.ReleaseDate)</td><td>@Html.DisplayFor(modelItem => item.Genre)</td><td>@Html.DisplayFor(modelItem => item.Price)</td><td><a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |<a asp-action="Details" asp-route-id="@item.Id">Details</a> |<a asp-action="Delete" asp-route-id="@item.Id">Delete</a></td></tr>}</tbody> </table>
启动程序,在浏览器中输入http://localhost:18084/Movies,如下图所示:
上图中菜单布局是在 Views/Shared/_Layout.cshtml 文件中实现的,该_Layout.cshtml页中@RenderBody()是视图页面的占位符。
Views/_ViewStart.cshtml 文件将 Views/Shared/_Layout.cshtml 文件引入到每个视图中。 可以使用 Layout
属性设置不同的布局视图,或将它设置为 null
,这样将不会使用任何布局文件。后面详细了解布局。
2.6 详细页Movies/ Details.cshtml
/// <summary>/// 详细页 /// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<IActionResult> Details(int? id){if (id == null){return NotFound();}var movie = await _MvcMovieContext.Movie.FirstOrDefaultAsync(m => m.Id == id);if (movie == null){return NotFound();}return View(movie);}
@model StudyMVCDemo.Models.Movie@{ViewData["Title"] = "Details"; }<h1>Details</h1><div><h4>Movie</h4><hr /><dl class="row"><dt class="col-sm-2">@Html.DisplayNameFor(model => model.Title)</dt><dd class="col-sm-10">@Html.DisplayFor(model => model.Title)</dd><dt class="col-sm-2">@Html.DisplayNameFor(model => model.ReleaseDate)</dt><dd class="col-sm-10">@Html.DisplayFor(model => model.ReleaseDate)</dd><dt class="col-sm-2">@Html.DisplayNameFor(model => model.Genre)</dt><dd class="col-sm-10">@Html.DisplayFor(model => model.Genre)</dd><dt class="col-sm-2">@Html.DisplayNameFor(model => model.Price)</dt><dd class="col-sm-10">@Html.DisplayFor(model => model.Price)</dd></dl> </div> <div><a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |<a asp-action="Index">Back to List</a> </div>
启动程序,从列表页的超连接Details点击进入,如下图所示:
2.7 编辑页Movies/ Edit.cshtml
对于编辑页有二个action, 一个是Get用来提取数据填充到表单,一个是Post用来提交修改的表单数据。
(1) post中的Bind特性是对需要的属性进行更新。
(2) ValidateAntiForgeryToken特性用于防止请求伪造, 生成的隐藏的 XSRF 标记 Input name="__RequestVerificationToken"。用在Post提交的比如修改和删除功能等。
(3) 模型验证asp-validation-for是指表单Post到服务器之前,客户端验证会检查字段上的任何验证规则。 如果有任何验证错误,则将显示错误消息,并且不会Post表单,内部是输入标记帮助程序使用 DataAnnotations 特性,并在客户端上生成 jQuery 验证所需的 HTML 特性。
public async Task<IActionResult> Edit(int? id){if (id == null){return NotFound();}var movie = await _MvcMovieContext.Movie.FindAsync(id);if (movie == null){return NotFound();}return View(movie);}[HttpPost][ValidateAntiForgeryToken]public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price")] Movie movie){if (id != movie.Id){return NotFound();}if (ModelState.IsValid){try{_MvcMovieContext.Update(movie);await _MvcMovieContext.SaveChangesAsync();}catch (DbUpdateConcurrencyException){throw;}return RedirectToAction("Index");}return View(movie);}
@model StudyMVCDemo.Models.Movie@{ViewData["Title"] = "Edit"; }<h1>Edit</h1><h4>Movie</h4> <hr /> <div class="row"><div class="col-md-4"><form asp-action="Edit"><div asp-validation-summary="ModelOnly" class="text-danger"></div><input type="hidden" asp-for="Id" /><div class="form-group"><label asp-for="Title" class="control-label"></label><input asp-for="Title" class="form-control" /><span asp-validation-for="Title" class="text-danger"></span></div><div class="form-group"><label asp-for="ReleaseDate" class="control-label"></label><input asp-for="ReleaseDate" class="form-control" /><span asp-validation-for="ReleaseDate" class="text-danger"></span></div><div class="form-group"><label asp-for="Genre" class="control-label"></label><input asp-for="Genre" class="form-control" /><span asp-validation-for="Genre" class="text-danger"></span></div><div class="form-group"><label asp-for="Price" class="control-label"></label><input asp-for="Price" class="form-control" /><span asp-validation-for="Price" class="text-danger"></span></div><div class="form-group"><input type="submit" value="Save" class="btn btn-primary" /></div></form></div> </div><div><a asp-action="Index">Back to List</a> </div>@section Scripts {@{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
启动程序,从列表页的Edit点击进入,如下图所示:
2.8 删除
// 删除没有对应的页面,从列表页的Delete点击进入,下面是删除的关键代码 public async Task<IActionResult> DeleteConfirmed(int id) {var movie = await _context.Movie.FindAsync(id);_context.Movie.Remove(movie);await _context.SaveChangesAsync();return RedirectToAction(nameof(Index)); }
参考文献
MVC教程
转载于:https://www.cnblogs.com/MrHSR/p/10509469.html
asp.net core系列 40 Web 应用MVC 介绍与详细示例相关推荐
- asp.net core系列 67 Web压力测试工具WCAT
asp.net core系列 67 Web压力测试工具WCAT 原文:asp.net core系列 67 Web压力测试工具WCAT 一.介绍 最近搭建了一套CQRS框架,需要在投入开发前,进行必要的 ...
- asp.net core 系列 18 web服务器实现
一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...
- asp.net core系列 71 Web架构分层指南
一.概述 本章Web架构分层指南,参考了"Microsoft应用程序体系结构指南"(该书是在2009年出版的,当时出版是为了帮助开发人员和架构师更快速,更低风险地使用Microso ...
- asp.net core系列 38 WebAPI 返回类型与响应格式--必备
一.返回类型 ASP.NET Core 提供以下 Web API Action方法返回类型选项,以及说明每种返回类型的最佳适用情况: (1) 固定类型 (2) IActionResult (3) Ac ...
- 5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」
原文:5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,码字辛苦,如果你吃了蛋觉得味道不错,希望点个赞,谢 ...
- ASP.NET CORE系列【一】搭建ASP.NET CORE项目
原文:ASP.NET CORE系列[一]搭建ASP.NET CORE项目 为什么要使用 ASP.NET Core? NET Core 刚发布的时候根据介绍就有点心里痒痒,微软的尿性都懂的,新东西bug ...
- 【转载】从头编写 asp.net core 2.0 web api 基础框架 (1)
工具: 1.Visual Studio 2017 V15.3.5+ 2.Postman (Chrome的App) 3.Chrome (最好是) 关于.net core或者.net core 2.0的相 ...
- 从头编写 asp.net core 2.0 web api 基础框架 (1)
工具: 1.Visual Studio 2017 V15.3.5+ 2.Postman (Chrome的App) 3.Chrome (最好是) 关于.net core或者.net core 2.0的相 ...
- 4.1ASP.NET Core请求过程「深入浅出ASP.NET Core系列」
原文:4.1ASP.NET Core请求过程「深入浅出ASP.NET Core系列」 希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. HTTP请求过程 这里展示整 ...
最新文章
- 如何在notebook中的markdown中插入截取的图片_96编辑器教你如何在文章中插入图片、视频、音频!...
- 美国和中国将成数据中心建设首选之地
- 在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试
- 复述-软考网规--云计算专题
- File类之常用方法
- Oracle 数据类型,表空间、用户与权限,DDL、DML 语句、约束,exp/imp
- 单片机的c语言程序设计显示12,12手把手教你学单片机的C语言程序设计中断服务函数.pdf...
- android设置adb环境变量,如何配置android的adb环境变量
- Android 消息机制之深入学习MessageQueue
- 货币兑换java程序,Spring Cloud货币换算及货币兑换服务介绍
- ion-infinite-scroll上拉加载 ion-refresher下拉刷新
- 函数重载与参数缺省值共用可能引起两意性
- Win10桌面壁纸、锁屏壁纸保存位置
- H5 -- 自定义微信分享第三方页面链接的标题和小缩略图
- PHp猴子偷,这些猴子成精了!偷东西偷得很萌很认真……
- 推断统计学 假设检验 分布
- biubiu加速器下载安装包路径
- mldonkey安装
- 安卓手机开不了机_手机开不了机的原因 _手机开不了机如何解决
- 'static_cast': cannot convert from 'double' to 'pcl::visualization::LookUpTableRepresentationPropert
热门文章
- 随办——真正懂企业痛点的移动协作软件
- PowerDesigner 15生成数据字典
- 斑马打印机客户端GET和POST,以及后端两种打印方式。
- 关于设计模式的感悟2
- 微软认证学习资料大集合(软件+资料)
- 自定义标签之 带Body的标签库
- RocketMQ源码解析-Broker的HA实现
- 网页防篡改技术_阿里云云安全中心和web应用防火墙的网页防篡改功能有什么不同...
- zookeeper springboot_摊牌了!我要手写一个“Spring Boot”
- Mybatis的动态拼接条件