学习ASP .NET MVC5官方教程总结(七)Edit方法和Edit视图详解

            学习ASP .NET MVC5官方教程总结(七)Edit方法和Edit视图详解

       在本章中,我们研究生成的Edit方法和视图。但在研究之前,我们先将 release date 弄得好看一点。打开Models\Movie.cs 文件。先添加一个引用:

<span style="font-size:14px;">using System.ComponentModel.DataAnnotations;</span>
然后在Movie类中添加以下代码:
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
Movie类全部代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
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; }
    }
    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
}

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

运行项目。效果:

技术分享


将鼠标放在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:15032/Movies/Edit/1 。默认的路由匹配 {controller}/{action}/{id} URL模式 。因此,ASP.NET 将请求地址:http://localhost:15032/Movies/Edit/1 翻译为MoviesController Edit 方法,参数id1你还可以使用查询字符串传递参数,例如 http://localhost:xxxxx/Movies/Edit?ID=1 也可以将参数id1的值传递给Movies控制器。

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

public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Movie movie = db.Movies.Find(id);
            if (movie == null)
            {
                return HttpNotFound();
            }
            return View(movie);
        }

        // POST: /Movies/Edit/5
        // 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关 
        // 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] 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() 是成对出现的。

@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()
    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.ReleaseDate, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ReleaseDate)
                @Html.ValidationMessageFor(model => model.ReleaseDate)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Genre, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Genre)
                @Html.ValidationMessageFor(model => model.Genre)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Price, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Price)
                @Html.ValidationMessageFor(model => model.Price)
            </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>
}

<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"><span style="color:#ffff66;background-color: rgb(255, 0, 0);"><input name="__RequestVerificationToken" type="hidden" value="iiF70EeCUAcjeB4NTxTVBrKhEXBP2APa0srKNdmyLK7ck6Z82etcAZRNzjt2guTeSA38DadyfhRM57PlbqOX8-d_r4nVD2vrNufvo8R1Vb01" /> </span>   <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        
        <input data-val="true" data-val-number="字段 ID 必须是一个数字。" data-val-required="ID 字段是必需的。" id="ID" name="ID" type="hidden" value="1" />
 
        <div class="form-group">
            <label class="control-label col-md-2" for="Title">Title</label>
            <div class="col-md-10">
                <input class="text-box single-line" id="Title" name="Title" type="text" value="When Harry Meet Sally" />
                <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-md-2" for="ReleaseDate">Release Date</label>
            <div class="col-md-10">
                <input class="text-box single-line" data-val="true" data-val-date="字段 Release Date 必须是日期。" data-val-required="Release Date 字段是必需的。" id="ReleaseDate" name="ReleaseDate" type="date" value="1997/1/11" />
                <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre">Genre</label>
            <div class="col-md-10">
                <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Comedy" />
                <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
 
        <div class="form-group">
            <label class="control-label col-md-2" for="Price">Price</label>
            <div class="col-md-10">
                <input class="text-box single-line" data-val="true" data-val-number="字段 Price 必须是一个数字。" data-val-required="Price 字段是必需的。" id="Price" name="Price" type="text" value="6.99" />
                <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </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>

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

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中是电影列表),然后传递给ViewCreate 方法传递一个空的电影数据给视图。使用HTTP GET 方法修改数据存在安全风险,也同样违背了HTTP最佳实践,并且 REST 模式中明确指出,GET 请求不应该改变应用程序的状态,换句话说,GET 操作应该是一个安全的操作,没有副作用,也不会修改你的持久化数据。

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

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

技术分享

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

技术分享

在Edit视图最下方做如下修改:

@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>}

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

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













郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。