zoukankan      html  css  js  c++  java
  • ASP.NET Core 中文文档 第二章 指南(4.6)Controller 方法与视图

    原文:Controller methods and views
    作者:Rick Anderson
    翻译:谢炀(Kiler)
    校对:孟帅洋(书缘) 、张仁建(第二年.夏) 、许登洋(Seay) 、姚阿勇(Dr.Yao) 、娄宇(Lyrics)

    我们已经初步的创建了一个 movie 应用程序,但是展示并不理想。我们不希望看到 release date 字段显示时间并且 ReleaseDate 应该是两个单词。
    movies-Index

    打开 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
      Quick Actions

    • 点击 using System.ComponentModel.DataAnnotations;
      using System.ComponentModel.DataAnnotations;

    Visual studio 会自动添加 using System.ComponentModel.DataAnnotations; 引用代码。

    让我们移除多余的 using 引用代码。它们默认以灰色字体出现。右键点击 Movie.cs 文件 点击 > Organize Usings > Remove Unnecessary 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。
    Movies-Edit

    EditDetails 以及 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 工具如下图。
    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 方法:
    两个 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 负责显示错误信息。
    Model Validation 模型验证

    movie controller 的所有 HttpGet 方法都遵循类似的模式。它们获取一个对象(或者对象列表,比如 Index),把对象(模型)传递到视图。Create 方法创建一个空的对象到 Create 视图。诸如 Create、Edit、Delete 等之类的会修改数据的方法都会在 [HttpPost] 版本的重载方法中这样做(译者注:执行类似于前文所述的这些操作)。在 HTTP GET 方法中修改数据有安全风险,参考 ASP.NET MVC 提示 #46 – 不要使用删除链接,因为他们制造安全漏洞 。在 HTTP GET 方法中修改数据同样也违反 HTTP 最佳实践以及 REST 架构模式,其中规定 GET 请求不应该更改应用程序的状态。换句话说,执行 GET 操作应该是没有任何副作用,不会修改您的持久化的数据。

    附录资源

    返回目录

  • 相关阅读:
    react 把时间戳用new Date改为日期
    react-格式化日期
    react-2种方法写法
    React-router4简约教程
    react-addons-css-transition-group
    vue-一些易错点
    Js apply方法详解,及其apply()方法的妙用
    C++中的继承(3)作用域与重定义,赋值兼容规则
    C++中的继承(1) 继承方式
    vim中文帮助文档安装
  • 原文地址:https://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-2_4_6-controller-methods-views.html
Copyright © 2011-2022 走看看