ASP.NET Core 中文文档 第二章 指南(4.6)Controller 方法与视图
原文:Controller methods and views
作者:Rick Anderson
翻译:谢炀(Kiler)
校对:孟帅洋(书缘) 、张仁建(第二年.夏) 、许登洋(Seay) 、姚阿勇(Dr.Yao) 、娄宇(Lyrics)
我们已经初步的创建了一个 movie 应用程序,但是展示并不理想。我们不希望看到 release date 字段显示时间并且 ReleaseDate 应该是两个单词。
打开 Models/Movie.cs 文件并添加下面高亮的代码行:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")] //手动高亮
[DataType(DataType.Date)] //手动高亮
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
右键点击红色波浪线代码行 > Quick Actions。
点击
using System.ComponentModel.DataAnnotations;
Visual studio 会自动添加 using System.ComponentModel.DataAnnotations;
引用代码。
让我们移除多余的 using
引用代码。它们默认以灰色字体出现。右键点击 Movie.cs 文件 点击 > Organize Usings > Remove Unnecessary Usings 菜单。
更新后的代码:
using System;
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
我们会在下一篇文章中继续发掘 DataAnnotations 的内容。Display 特性用来指定字段的显示名 (在本示例中 “Release Date” 会替代 “ReleaseDate”)。DataType 特性指定数据类型,在本示例是日期类型,所以字段中存储的时间信息不会被显示。
浏览 Movies
控制器并把鼠标悬停于 Edit 链接上可以看到目标 URL。
Edit、Details 以及 Delete 链接是由 Views/Movies/Index.cshtml 文件中的 MVC Core Anchor Tag Helper 自动生成的。
<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>
Tag Helpers 允许服务器端代码在 Razor 文件中创建和生成 HTML 元素。在上面的代码中,AnchorTagHelper通过 controller 方法以及路由ID 动态生成 HTML href
属性值。你可以在你熟悉的浏览器中使用 View Source 菜单或者使用 F12 工具来检查你生成的 HTML 标签。 F12 工具如下图。
在 Startup.cs 文件中设置回调路由格式。
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"); //手动高亮
});
ASP.NET Core 会把 http://localhost:1234/Movies/Edit/4
转化成发送到 Movies
controller 的 Edit
方法的请求并带上值为 4 的 ID
参数。(Controller 方法其实就是指代 action 方法。)
Tag Helpers 是 ASP.NET Core 中最受欢迎的新功能之一。 参考 附录资源 获取更多信息。
打开 Movies
controller 并查看两个 Edit
方法:
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
// POST: Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(movie);
}
[Bind]
特性是防止 over-posting (过度提交,客户端可能发送比期望还多的数据,比如只需要2个属性但是发送了3个属性)的一种方法。你应该只把需要改变的属性包含到 [Bind]
特性中。请参阅 Protect your controller from over-posting 获取更多信息,ViewModels 提供了另一种防止 over-posting 的方法。
请注意带第二个 Edit
方法被 [HttpPost]
特性所修饰。
[HttpPost] //手动高亮
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(movie);
}
[HttpPost]特性指定这个 Edit
方法 只能 被 POST
请求调用。你可以把 [HttpGet]
特性应用到第一个 edit 方法,但是,不是必须的,因为 [HttpGet]
是被默认使用的。
[ValidateAntiForgeryToken] 特性是用来防止伪造请求的,会在(Views/Movies/Edit.cshtml)视图最终呈现文件中加入反伪造标记和服务器进行配对。edit 视图生成反伪造标记请参考 Form Tag Helper。
<form asp-action="Edit">
Form Tag Helper 生成一个隐藏域的防伪标记必须和 Movies controller 的 Edit
方法的 [ValidateAntiForgeryToken]
产生的防伪标记相匹配。更多信息请参考 Anti-Request Forgery。
HttpGet Edit
方法获取 movie 的 ID
参数,通过使用 Entity Framework 的 SingleOrDefaultAsync
方法查找 movie,并将选中的 movie 填充到 Edit 视图。如果 movie 没有找到,返回 NotFound
(HTTP 404) 响应。
// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.SingleOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
在基架系统创建 Edit 视图的时候,会检查 Movie 类并为它的每个属性生成代码以呈现 <label>
和 <input>
元素。下面的例子展示了 Visual Studio 基架系统生成的 Edit 视图:
@model MvcMovie.Models.Movie //手动高亮
@{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Genre" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Price" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
你会注意到为什么视图模版文件的顶部会有一行 @model MvcMovie.Models.Movie
声明呢?— 因为这个声明指定这个视图模版的模型期待的类型是 Movie
。
基架生成的代码使用几个 Tag Helper 方法来简化 HTML 标记。 Label Tag Helper 用来显示字段名(“Title”、”ReleaseDate”、”Genre” 或者 “Price”)。Input Tag Helper 用来呈现 HTML <input>
元素。Validation Tag Helper 显示关联到属性的错误信息。
运行应用程序并导航到 /Movies
URL。单击 编辑 链接。在浏览器中查看该页面的源代码。为 <form>
元素生成的 HTML 如下所示。
<form action="/Movies/Edit/7" method="post"> //手动高亮
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div class="text-danger" />
<input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" /> //手动高亮
<div class="form-group">
<label class="control-label col-md-2" for="Genre" />
<div class="col-md-10">
<input class="form-control" type="text" id="Genre" name="Genre" value="Western" /> //手动高亮
<span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true" />
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Price" />
<div class="col-md-10">
<input class="form-control" type="text" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" value="3.99" /> //手动高亮
<span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true" />
</div>
</div>
<!-- Markup removed for brevity -->
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" /> //手动高亮
</div>
</div>
</div>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" /> //手动高亮
</form>
HTML <form>
中的 <input>
元素的 action
属性用于设置请求发送到 /Movies/Edit/id
URL。当点击 Save
按钮时表单数据会被发送到服务器。在 </form>
元素关闭前最后一行 </form>
展示了 XSRF 生成的隐藏域标识。
处理 POST 请求
下面的列表显示了 [HttpPost]
不同版本的 Edit
方法。
[HttpPost] //手动高亮
[ValidateAntiForgeryToken] //手动高亮
public async Task<IActionResult> Edit(int id, [Bind("ID,Genre,Price,ReleaseDate,Title")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid) //手动高亮
{
try
{
_context.Update(movie); //手动高亮
await _context.SaveChangesAsync(); //手动高亮
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index"); //手动高亮
}
return View(movie);
}
[ValidateAntiForgeryToken]
特性验证 Form Tag Helper 生成的存放在隐藏域中的 XSRF 反伪造标记。
模型绑定机制以发送表单数据创建 Movie
对象并作为 movie
参数。ModelState.IsValid
方法验证表单提交的数据可以用来修改(编辑或更新)一个 Movie
对象。如果数据有效,就可以保存。更新(编辑) movie 数据会被存到数据库通过 database context 的 SaveChangesAsync
方法。数据保存完毕以后,这段代码将用户重定向到 MoviesController
类的 Index
方法,这个页面显示了改动后最新的Movie集合。
表单数据被发布到服务器之前,客户端校验会检查所有字段上的验证规则。如果有任何验证错误,则显示错误消息,并且表单数据不会被发送。如果禁用了 JavaScript,将不会有客户端验证,但服务器端将检测出发送数据是无效的,表单依旧会显示出错误信息。在稍后的教程中,我们会探讨 Model Validation 模型验证 更多关于验证的细节。Views/Book/Edit.cshtml 视图模版中的 Validation Tag Helper 负责显示错误信息。
movie controller 的所有 HttpGet
方法都遵循类似的模式。它们获取一个对象(或者对象列表,比如 Index
),把对象(模型)传递到视图。Create
方法创建一个空的对象到 Create
视图。诸如 Create、Edit、Delete 等之类的会修改数据的方法都会在 [HttpPost]
版本的重载方法中这样做(译者注:执行类似于前文所述的这些操作)。在 HTTP GET
方法中修改数据有安全风险,参考 ASP.NET MVC 提示 #46 – 不要使用删除链接,因为他们制造安全漏洞 。在 HTTP GET 方法中修改数据同样也违反 HTTP 最佳实践以及 REST 架构模式,其中规定 GET 请求不应该更改应用程序的状态。换句话说,执行 GET 操作应该是没有任何副作用,不会修改您的持久化的数据。
附录资源
- 全球化与本地化
- Introduction to Tag Helpers
- Authoring Tag Helpers
- Anti-Request Forgery
- 防止 controller 过度提交
- ViewModels
- Form Tag Helper
- Input Tag Helper
- Label Tag Helper
- Select Tag Helper
- Validation Tag Helper
ASP.NET Core 中文文档 第二章 指南(4.6)Controller 方法与视图的更多相关文章
- ASP.NET Core 中文文档 第二章 指南(4.4)添加 Model
原文:Adding a model 作者:Rick Anderson 翻译:娄宇(Lyrics) 校对:许登洋(Seay).孟帅洋(书缘).姚阿勇(Mr.Yao).夏申斌 在这一节里,你将添加一些类来 ...
- ASP.NET Core 中文文档 第二章 指南(4.9)添加验证
原文:Adding Validation 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘).娄宇(Lyrics).许登洋(Seay) 在本章节中你将为 Movie 模型 ...
- ASP.NET Core 中文文档 第二章 指南 (09) 使用 Swagger 生成 ASP.NET Web API 在线帮助测试文档
原文:ASP.NET Web API Help Pages using Swagger 作者:Shayne Boyer 翻译:谢炀(kiler) 翻译:许登洋(Seay) 对于开发人员来说,构建一个消 ...
- ASP.NET Core 中文文档 第二章 指南(1)用 Visual Studio Code 在 macOS 上创建首个 ASP.NET Core 应用程序
原文:Your First ASP.NET Core Application on a Mac Using Visual Studio Code 作者:Daniel Roth.Steve Smith ...
- ASP.NET Core 中文文档 第二章 指南(2)用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API
原文:Building Your First Web API with ASP.NET Core MVC and Visual Studio 作者:Mike Wasson 和 Rick Anderso ...
- ASP.NET Core 中文文档 第二章 指南(3)用 Visual Studio 发布一个 Azure 云 Web 应用程序
原文:Getting Started 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘).刘怡(AlexLEWIS).何镇汐 设置开发环境 安装最新版本的 Azure S ...
- ASP.NET Core 中文文档 第二章 指南(4.1)ASP.NET Core MVC 与 Visual Studio 入门
原文:Getting started with ASP.NET Core MVC and Visual Studio 作者:Rick Anderson 翻译:娄宇(Lyrics) 校对:刘怡(Alex ...
- ASP.NET Core 中文文档 第二章 指南(4.5)使用 SQL Server LocalDB
原文:Working with SQL Server LocalDB 作者:Rick Anderson 翻译: 魏美娟(初见) 校对: 孟帅洋(书缘).张硕(Apple).许登洋(Seay) Appl ...
- ASP.NET Core 中文文档 第二章 指南(5) 在 Nano Server 上运行ASP.NET Core
原文 ASP.NET Core on Nano Server 作者 Sourabh Shirhatti 翻译 娄宇(Lyrics) 校对 刘怡(AlexLEWIS).许登洋(Seay).谢炀(kile ...
随机推荐
- ASP.NET Core HTTP 管道中的那些事儿
前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...
- Linux 江湖系列阶段性总结
引言 我使用 Linux 已经有很多年了,最开始接触 Linux 的时候是从 RedHat 9(没有 Enterprise),中途换过 N 个不同的发行版.多年前,我在 BlogJava 上面分享 J ...
- C# 正则表达式大全
文章导读 正则表达式的本质是使用一系列特殊字符模式,来表示某一类字符串.正则表达式无疑是处理文本最有力的工具,而.NET提供的Regex类实现了验证正则表达式的方法.Regex 类表示不可变(只读)的 ...
- Xamarin+Prism开发详解三:Visual studio 2017 RC初体验
Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...
- JAVA程序员常用软件整理下载
********为了大家学习方便,特意整理软件下载如下:*************Java类软件:-------------------------------JDK7.0:http://pan.ba ...
- android Notification介绍
如果要添加一个Notification,可以按照以下几个步骤 1:获取NotificationManager: NotificationManager m_NotificationManager=(N ...
- WINDOWS系统下MYSQL安装过程中的注意事项
1.首先MySQL的安装方式有两种:一种是MSI安装方式,很简单就像安装Windows软件一样.另外一种就是ZIP安装方式.这种相对而言比较麻烦.新手推荐MSI安装方式. 安装方式有以下两种: MSI ...
- 端盘子的服务生到月薪一万五的IT精英,你能相信吗
一直以来,我都觉得自己不是一个有故事的人. 以前的我,是个乖宝宝,对父母言听计从,特别内向,甚至一度感觉到自卑.不上学之后,我干过送货员,去工地除泥搬砖,当过油漆工,去过工厂,还去饭店当过端盘子的服务 ...
- SAE+WordPress快速搭建个人博客
前些天一时冲动,买了个域名,我想总不能放着不用吧,干脆就搭建了一个个人博客.下面我把搭建的过程分享给大家.注意,此文并不是攻略,只是为了记录下这个从无到有的过程,当然,假如解决了你的疑惑,那当然是极好 ...
- 虚拟机体验之 QEMU 篇
引言 说起虚拟机,大家都不陌生.需要使用虚拟机的场景也非常的多,比如有志于写操作系统的同志,往往需要一个虚拟机来运行和调试他写的系统:再比如喜欢研究网络体系结构的朋友,需要在自己的电脑上虚拟出 N 个 ...