原文:【译】ASP.NET MVC 5 教程 - 7:Edit方法和Edit视图详解

在本节中,我们继续研究生成的Edit方法和视图。但在研究之前,我们先将 release date 弄得好看一点。打开 Models\Movie.cs 文件,添加下面黄色背景的行:

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity; 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; }
} public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
}

我们将在下一节中介绍 DataAnnotations。Display 特性指定了显示的字段名(本例中“Release Date”替换了“ReleaseDate”)。DataType 特性指定了数据类型,在本例中它是日期类型,因此存储在该字段的时间信息将不会显示出来。

运行应用程序,在浏览器地址栏中追加/movies 来访问 Movies 控制器。将鼠标放在Edit 链接上面,查看它链接到的地址:

Edit 链接是通过Html.ActionLink 方法生成的,在Views\Movies\Index.cshtml 视图中,代码如下:

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

Html 对象是System.Web.Mvc.WebViewPage 基类中的一个属性,ActionLink 方法使动态生成 控制器中方法的 HTML 超链接更为简单。ActionLink 方法的第一个参数是要显示的链接文字(例如<a>Edit Me</a>);第二个参数是要调用的控制器方法(在这里是 Edit 方法);最后一个参数是生成路由数据用的匿名对象(在这里是id,值为1)。

在图1中显示的生成地址是http://localhost:2264/Movies/Edit/1 。默认的路由匹配 {controller}/{action}/{id} URL模式 。因此,ASP.NET 将请求地址http://localhost:2264/Movies/Edit/1 翻译为MoviesController 的Edit 方法,参数id为1。查看App_Start\RouteConfig.cs 文件中的代码:

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

你还可以使用查询字符串传递参数,例如 http://localhost:xxxxx/Movies/Edit?ID=1 也可以将参数id为1的值传递给Movies控制器。

打开Movies控制器,它包含了两个Edit 方法,代码如下:

//
// GET: /Movies/Edit/5
public ActionResult Edit(Int32 id)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
} //
// POST: /Movies/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}

注意第二个 Edit 方法,它被HttpPost特性修饰(代码中黄色背景标注),这个特性指定只有POST请求才能调用这个重载的Edit方法。我们可以为第一个Edit方法添加HttpGet特性,但这不是必须的,因为它是默认值(我们将未标记的方法认定为HttpGet方法)。第二个Edit 方法还有一个ValidateAntiForgeryToken 特性,这个特性用来阻止伪造的请求,它和视图(Views\Movies\Edit.cshtml)中的 @Html.AntiForgeryToken() 是成对出现的。

@model MvcMovie.Models.Movie

@{
ViewBag.Title = "Edit";
} <h2>Edit</h2> @using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true) <fieldset class="form-horizontal">
<legend>Movie</legend> @Html.HiddenFor(model => model.ID) <div class="control-group">
@Html.LabelFor(model => model.Title, new { @class = "control-label" })
<div class="controls">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title, null, new { @class = "help-inline" })
</div>
</div>

@Html.AntiForgeryToken() 生成一个表单防伪标记,必须与 MoviesController 中的 Edit 方法匹配。

HttpGet标记的Edit方法接收一个ID的参数,通过Entity Framework Find方法查找电影,并将找到的结果返回个Edit 视图。如果调用Edit方法的时候没有参数,ID参数的值将会是默认的0。如果未找到电影信息,控制器将返回一个HttpNotFound。当支架系统创建Edit 视图的时候,它会检查Movie类,为它的每一个属性创建绘制<label><input> 元素的代码。下面的示例展示了Visual Studio 支架系统创建的Edit视图代码:

@model MvcMovie.Models.Movie

@{
ViewBag.Title = "Edit";
} <h2>Edit</h2> @using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true) <fieldset class="form-horizontal">
<legend>Movie</legend> @Html.HiddenFor(model => model.ID) <div class="control-group">
@Html.LabelFor(model => model.Title, new { @class = "control-label" })
<div class="controls">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title, null, new { @class = "help-inline" })
</div>
</div> <div class="control-group">
@Html.LabelFor(model => model.ReleaseDate, new { @class = "control-label" })
<div class="controls">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate, null, new { @class = "help-inline" })
</div>
</div> <div class="control-group">
@Html.LabelFor(model => model.Genre, new { @class = "control-label" })
<div class="controls">
@Html.EditorFor(model => model.Genre)
@Html.ValidationMessageFor(model => model.Genre, null, new { @class = "help-inline" })
</div>
</div> <div class="control-group">
@Html.LabelFor(model => model.Price, new { @class = "control-label" })
<div class="controls">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price, null, new { @class = "help-inline" })
</div>
</div> <div class="form-actions no-color">
<input type="submit" value="Save" class="btn" />
</div>
</fieldset>
} <div>
@Html.ActionLink("Back to List", "Index")
</div> @section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

在视图文件顶部的语句 @model MvcMovie.Models.Movie 指明该视图需要一个 Movie 型的模型。

支架生成的代码使用了很多精简HTML标记的帮助方法,Html.LabelFor 方法用来显示字段名( "Title", "ReleaseDate", "Genre", 和 "Price"),Html.EditorFor 方法生成一个HTML <input> 元素,Html.ValidationMessageFor 方法结合属性显示一些验证信息。

运行应用程序,导航到 /movies 地址,点击 Edit 链接,在浏览器中,查看页面源代码,form标签的HTML代码如下:

<form action="/Movies/Edit/1" method="post">
<input name="__RequestVerificationToken" type="hidden" value="vM6-yBtYJY5uRgKm3ivFw9gLMjauE9i2-Wi6Y--2Swm4-BlucfrG71zQV3n773tT9i8ytaG5WSanfBBd54qIhAKAkTJO_Z0UhRaGPDhMJ9M1" /> <fieldset class="form-horizontal">
<legend>Movie</legend> <input data-val="true" data-val-number="The field ID must be a number." data-val-required="ID 字段是必需的。" id="ID" name="ID" type="hidden" value="1" /> <div class="control-group">
<label class="control-label" for="Title">Title</label>
<div class="controls">
<input class="text-box single-line" id="Title" name="Title" type="text" value="中国合伙人" />
<span class="field-validation-valid help-inline" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
</div> <div class="control-group">
<label class="control-label" for="ReleaseDate">Release Date</label>
<div class="controls">
<input class="text-box single-line" data-val="true" data-val-date="The field Release Date must be a date." data-val-required="Release Date 字段是必需的。" id="ReleaseDate" name="ReleaseDate" type="date" value="2013/6/18" />
<span class="field-validation-valid help-inline" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
</div> <div class="control-group">
<label class="control-label" for="Genre">Genre</label>
<div class="controls">
<input class="text-box single-line" id="Genre" name="Genre" type="text" value="励志" />
<span class="field-validation-valid help-inline" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
</div> <div class="control-group">
<label class="control-label" for="Price">Price</label>
<div class="controls">
<input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="Price 字段是必需的。" id="Price" name="Price" type="text" value="70.00" />
<span class="field-validation-valid help-inline" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div> <div class="form-actions no-color">
<input type="submit" value="Save" class="btn" />
</div>
</fieldset>
</form>

看一下这个form标签,它的action属性是/movies/edit/1,说明form将提交到这个地址,而它里面包含的<input>标签则对应每一个Movie类的字段。当点击“Save”按钮时,form中的数据将提交到服务器。第二行代码中,我使用高亮提示,这行代码是使用@Html.AntiForgeryToken() 生成的隐藏域,用来阻止伪造的请求。

处理POST请求

下面是接收POST请求的Edit方法:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}

ASP.NET MVC model binder 使用提交的表单数据创建一个 Movie 对象,作为 movie 参数传递给 Edit 方法。ModelState.IsValid  验证 form 提交的数据是否能够用来修改(编辑或更新)Movie对象,如果验证通过,电影数据就会保存在 db.Movies 列表中(db 是 MovieDBContext 的实例),通过调用 db.SaveChanges 方法将修改后的电影数据保存在数据库中。在数据保存之后,RedirectToAction 将跳转到 Index 方法,用来显示电影列表,此时就能看到更新后的电影信息。

在用户输入过程中,一旦输入了未通过验证的值,客户端就会显示一个错误信息,这些功能是通过Javascript 完成的,如果你禁用了Javascript,客户端验证功能将会失效,但服务器会检查到不合法的数据,然后将包含错误信息的表单重新显示在客户端。在后面的章节中我们将介绍更多验证的细节。

Edit.cshtml 视图中的 Html.ValidationMessageFor 方法会显示相应的错误信息。如下图:

所有处理Get请求的方法都有相似的模式:他们获取一个电影对象(在Index中是电影列表),然后传递给View。Create 方法传递一个空的电影数据给视图。使用HTTP GET 方法修改数据存在安全风险,也同样违背了HTTP最佳实践,并且 REST 模式中明确指出,GET 请求不应该改变应用程序的状态,换句话说,GET 操作应该是一个安全的操作,没有副作用,也不会修改你的持久化数据。

本地化验证

如果你是用英语,这部分可以跳过。作为汉语国家用户,我还是把这部分翻译一下吧。

对于非英语地区的用户来说,小数点使用逗号(","),还有非美语的日期格式,为了支持Jquery 验证,你必须包含 globalize.js 和特定的cultures/globalize.cultures.js 文件,在Javascript中使用 Globalize.parseFloat。你可以通过NuGet获得 jQuery non-English validation (英语地区不要安装Globalize)。

From the Tools menu click Library Package Manager, and then click Manage NuGet Packages for Solution.

在菜单栏中单击“工具”>“库程序包管理器”>“管理解决方案的 NuGet程序包”。

在左侧单击“联机”,在查询框中输入“Globalize”:

点击“安装”按钮,Scripts\jquery.globalize\globalize.js 文件将会添加到项目中,文件夹 Scripts\jquery.globalize\cultures\ 中包含了很多Javascript文件。

下面的代码显示了将文件 Views\Movies\Edit.cshtml 使用汉语区域所作的修改(原文作者使用法语作为演示):

@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/jquery.globalize/globalize.js"></script>
<script src="~/Scripts/jquery.globalize/cultures/globalize.culture.fr-FR.js"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$(document).ready(function () {
Globalize.culture('fr-FR');
});
</script>
<script>
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Use the Globalization plugin to parse the value
var val = $.global.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
</script>
<script>
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
!isDate(Globalize.parseDate(value));
}
</script>
}

为了避免在每个编辑视图都写上这么一大段代码,你可以将他们写在布局页。

在下一节中我们将为程序实现查找功能。

【译】ASP.NET MVC 5 教程 - 7:Edit方法和Edit视图详解的更多相关文章

  1. 【译】ASP.NET MVC 5 教程 - 11:Details 和 Delete 方法详解

    原文:[译]ASP.NET MVC 5 教程 - 11:Details 和 Delete 方法详解 在教程的这一部分,我们将研究一下自动生成的 Details 和Delete 方法. Details ...

  2. ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解

    原文 ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 ...

  3. 【译】ASP.NET MVC 5 教程 - 9:添加新字段

    原文:[译]ASP.NET MVC 5 教程 - 9:添加新字段 在本节中,我们将使用Entity Framework Code First 数据迁移功能将模型类的改变应用到数据库中. 默认情况下,当 ...

  4. 【译】ASP.NET MVC 5 教程 - 6:通过控制器访问模型的数据

    原文:[译]ASP.NET MVC 5 教程 - 6:通过控制器访问模型的数据 在本节中,你将新建一个MoviesController 类,并编写获取电影数据的代码,使用视图模板将数据展示在浏览器中. ...

  5. 【译】ASP.NET MVC 5 教程 - 10:添加验证

    原文:[译]ASP.NET MVC 5 教程 - 10:添加验证 在本节中,我们将为Movie模型添加验证逻辑,并确认验证规则在用户试图使用程序创建和编辑电影时有效. DRY 原则 ASP.NET M ...

  6. 【译】ASP.NET MVC 5 教程 - 8:搜索查询

    原文:[译]ASP.NET MVC 5 教程 - 8:搜索查询 添加一个搜索的方法和搜索的视图 在本节中,我们为 Index 方法添加查询功能,使我们能够根据电影的题材或名称进行查找. 修改 Inde ...

  7. 【译】ASP.NET MVC 5 教程 - 5:使用 SQL 服务器 LocalDB 创建连接字符串

    原文:[译]ASP.NET MVC 5 教程 - 5:使用 SQL 服务器 LocalDB 创建连接字符串 在上一节中,我们创建了MovieDBContext 类来连接数据库.处理Movie 对象和数 ...

  8. 【译】ASP.NET MVC 5 教程 - 4:添加模型

    原文:[译]ASP.NET MVC 5 教程 - 4:添加模型 在本节中,我们将添加一些管理电影数据库的类,这些类在ASP.NET MVC 应用程序中扮演“Model”的角色. 我们将使用.NET F ...

  9. 【译】ASP.NET MVC 5 教程 - 3:添加视图

    原文:[译]ASP.NET MVC 5 教程 - 3:添加视图 在本节内容中,我们将修改HelloWorldController类,使用视图模板来干净利索的封装生成HTML响应客户端的过程. 您将创建 ...

随机推荐

  1. 矩形、占位符组件——axure线框图部件库介绍

    矩形组件和占位符没有太多的区别,这里我们主要讲解矩形组件的操作和使用,占位符的操作各位可以按照矩形的操作方法进行练习一下. 矩形组件是一个矩形,它可以用来做很多的工作,比如页面上需要一块蓝色的背景,就 ...

  2. strip 命令的使用方法

    用途 通过除去绑定程序和符号调试程序使用的信息,降低扩展公共对象文件格式(XCOFF)的对象文件的大小. 语法 strip [ -V ] [ -r [ -l ] | -x [ -l ] | -t | ...

  3. 碰撞回避算法(一) Velocity Obstacle

    碰撞回避是机器人导航,游戏AI等领域的基础课题.几十年来,有很多算法被提出.注意这里主要指的是局部的碰撞回避算法.尽管和全局的路径规划算法(A*算法等)有千丝万缕的联系.可是还是有所不同的(局部的碰撞 ...

  4. 解决android应用程序适用新老android系统版本方法

    老的android系统不能运行高版本系统的新方法,为了解决这个问题:  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ...

  5. Monkey 命令使用说明

    1.  命令使用 Monkey是一个命令列工具 ,可以运行在仿真器里或实际设备中.它向系统发送伪随机的使用者事件流,实现对正在开发的应用程序进行压力测试.Monkey包括许多选项,它们大致分为四大类: ...

  6. boost锁的概述

    ●     boost锁的概述 boost库中提供了mutex类与lock类,通过组合可以轻易的构建读写锁与互斥锁. ▲     mutex对象类 mutex类主要有两种:boost::mutex,b ...

  7. Net Core子应用由于配置引起IIS错误500.19

    Asp.Net Core子应用由于配置中重复添加模块会引起IIS错误500.19 ASP.NET Core已经从IIS中解耦,可以作为自宿主程序运行,不再依赖IIS. 但我们还是需要强大的IIS作为前 ...

  8. QT5静态编译教程,主要针对vs2012(渡世白玉)

    QT5,VS2012静态编译,所有的库准备充分的话qwebkit也可以静态编译通过,但是我编译的版本使用中如果用了QWEBVIEW控件在连接时会出错. 注:我自己编译的环境是:win server 2 ...

  9. oracle 之数据字典屣履造门。

     oracle 之数据字典屣履造门.(更新中)       今天是2013-06-20,哎,写这篇笔记的时候,我发现我是一个非常懒惰的人,这篇文章本该昨天就完成的,想起了钱鹤滩的<明日歌> ...

  10. js 常用正则表达式分析详解

    1.整数或者小数:/^((0{1}|[1-9]{1}[0-9]+)\.{1}[0-9]+|[1-9]{1}[0-9]*|0)$/ 分析:分类讨论,如果是小数,则有两种形式   0.111对应的是 0{ ...