Controller methods and views
https://docs.asp.net/en/latest/tutorials/first-mvc-app/controller-methods-views.html
We have a good start to the movie app, but the presentation is not ideal. We don’t want to see the time (12:00:00 AM in the image below) and ReleaseDate should be two words.
Open the Models/Movie.cs file and add the highlighted lines shown below:
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; } }
using System.ComponentModel.DataAnnotations;
We’ll cover DataAnnotations in the next tutorial.
The Display attribute specifies what to display for the name of a field (in this case “Release Date” instead of “ReleaseDate”).
The DataType attribute specifies the type of the data, in this case it’s a date, so the time information stored in the field is not displayed.
Browse to the Movies
controller and hold the mouse pointer over an Edit link to see the target URL.
The Edit, Details, and Delete links are generated by the MVC Core Anchor Tag Helper in theViews/Movies/Index.cshtml file.
<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 enable server-side code to participate in creating and rendering HTML elements in Razor files.
In the code above, the AnchorTagHelper
dynamically generates the HTML href
attribute value from the controller action method and route id.
You use View Source from your favorite browser or use the F12 tools to examine the generated markup.
The F12 tools are shown below.
Chrome浏览器,在界面上右键,检查:然后会弹出一个浏览界面,左上角的指针图标,单击之后,移动到页面上,会随着移动而展开html
Recall the format for routing set in the Startup.cs file.
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
ASP.NET Core translates http://localhost:1234/Movies/Edit/4
into
a request to the Edit
action method of the Movies
controller with the parameter ID
of 4.
(Controller methods are also known as action methods.)
Tag Helpers are one of the most popular new features in ASP.NET Core. See Additional resources for more information.
Open the Movies
controller and examine the two Edit
action methods:
// 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); }
The [Bind]
attribute is one way to protect against over-posting.
You should only include properties in the [Bind]
attribute that you want to change.
See Protect your controller from over-posting for more information.
ViewModels provide an alternative approach to prevent over-posting.
Notice the second Edit
action method is preceded by the [HttpPost]
attribute.
[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); }
The HttpPostAttribute
attribute specifies that this Edit
method can be invoked only for POST
requests.
You could apply the [HttpGet]
attribute to the first edit method, but that’s not necessary because [HttpGet]
is the default.
The ValidateAntiForgeryTokenAttribute
attribute is used to prevent forgery of a request and is paired up with an anti-forgery token generated in the edit view file (Views/Movies/Edit.cshtml).
The edit view file generates the anti-forgery token with the Form Tag Helper.
<form asp-action="Edit">
The Form Tag Helper generates a hidden anti-forgery token that must match the[ValidateAntiForgeryToken]
generated anti-forgery token in the Edit
method of the Movies controller.
For more information, see 🔧 Anti-Request Forgery.
The HttpGet Edit
method takes the movie ID
parameter, looks up the movie using the Entity Framework SingleOrDefaultAsync
method, and returns the selected movie to the Edit view.
If a movie cannot be found, NotFound
(HTTP 404) is returned.
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); }
When the scaffolding system created the Edit view, it examined the Movie
class and created code to render <label>
and <input>
elements for each property of the class.
The following example shows the Edit view that was generated by the visual studio scaffolding system:
@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");} }
Notice how the view template has a @model MvcMovie.Models.Movie
statement at the top of the file — this specifies that the view expects the model for the view template to be of type Movie
.
The scaffolded code uses several Tag Helper methods to streamline the HTML markup.
The - Label Tag Helper displays the name of the field (“Title”, “ReleaseDate”, “Genre”, or “Price”).
The Input Tag Helper renders an HTML <input>
element.
The Validation Tag Helper displays any validation messages associated with that property.
Run the application and navigate to the /Movies
URL. Click an Edit link.
In the browser, view the source for the page.
The generated HTML for the <form>
element is shown below.
<form action="/Movies/Edit/3" method="post" novalidate="novalidate"> <div class="form-horizontal"> <h4>Movie</h4> <hr> <div class="text-danger"></div> <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="3"> <div class="form-group"> <label class="col-md-2 control-label" for="Genre">Genre</label> <div class="col-md-10"> <input class="form-control valid" type="text" id="Genre" name="Genre" value="Romantic Comedy" aria-invalid="false"> <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"> </span></div> </div> <div class="form-group"> <label class="col-md-2 control-label" for="Price">Price</label> <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="7.99"> <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"> </span></div> </div> <div class="form-group"> <label class="col-md-2 control-label" for="ReleaseDate">Release Date</label> <div class="col-md-10"> <input class="form-control" type="date" data-val="true" data-val-required="The Release Date field is required." id="ReleaseDate" name="ReleaseDate" value="1989-01-11"> <span class="text-danger field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"> </span></div> </div> <div class="form-group"> <label class="col-md-2 control-label" for="Title">Title</label> <div class="col-md-10"> <input class="form-control" type="text" id="Title" name="Title" value="When Harry Met Sally"> <span class="text-danger field-validation-valid" data-valmsg-for="Title" 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> <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Anx_Z5byTVDqx_qfim84NdRi0DWbSfs1SuFZ50Xrtu7JUCri2nMtEJZ8yoKznVqVXflWA5XXccb1vFBLFdyY5fgUVPciYr77oNx9NQHOwhrIvUwJi9PaQ8ldKIR5BWaFhqDb_bwYF7H7rmTR_byvXk"></form>
The <input>
elements are in an HTML <form>
element whose action
attribute is set to post to the/Movies/Edit/id
URL.
The form data will be posted to the server when the Save
button is clicked.
The last line before the closing </form>
element shows the hidden XSRF token generated by theForm Tag Helper.
Processing the POST Request
The following listing shows the [HttpPost]
version of the Edit
action method.
[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); }
The [ValidateAntiForgeryToken]
attribute validates the hidden XSRF token generated by the anti-forgery token generator in the Form Tag Helper
The model binding system takes the posted form values and creates a Movie
object that’s passed as the movie
parameter.
The ModelState.IsValid
method verifies that the data submitted in the form can be used to modify (edit or update) a Movie
object.
If the data is valid it’s saved.
The updated (edited) movie data is saved to the database by calling the SaveChangesAsync
method of database context.
After saving the data, the code redirects the user to the Index
action method of the MoviesController
class, which displays the movie collection, including the changes just made.
Before the form is posted to the server, client side validation checks any validation rules on the fields.
If there are any validation errors, an error message is displayed and the form is not posted.
If JavaScript is disabled, you won’t have client side validation but the server will detect the posted values that are not valid, and the form values will be redisplayed with error messages.
Later in the tutorial we examine Model Validation validation in more detail.
The Validation Tag Helper in theViews/Book/Edit.cshtml view template takes care of displaying appropriate error messages.
All the HttpGet
methods in the movie controller follow a similar pattern.
They get a movie object (or list of objects, in the case of Index
), and pass the object (model) to the view.
The Create
method passes an empty movie object to the Create
view.
All the methods that create, edit, delete, or otherwise modify data do so in the [HttpPost]
overload of the method.
Modifying data in an HTTP GET method is a security risk, as in ASP.NET MVC Tip #46 – Don’t use Delete Links because they create Security Holes.
Modifying data in a HTTP GET
method also violates HTTP best practices and the architectural REST pattern, which specifies that GET requests should not change the state of your application.
In other words, performing a GET operation should be a safe operation that has no side effects and doesn’t modify your persisted data.
Additional resources
- Globalization and localization
- Introduction to Tag Helpers
- Authoring Tag Helpers
- 🔧 Anti-Request Forgery
- Protect your controller from over-posting
- ViewModels
- Form Tag Helper
- Input Tag Helper
- Label Tag Helper
- Select Tag Helper
- Validation Tag Helper
作者:Chuck Lu GitHub |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2015-07-01 Complete The Pattern #1
2015-07-01 Complete The Pattern #2
2015-07-01 Complete The Pattern #6 - Odd Ladder
2015-07-01 C#中保留2位小数
2014-07-01 博客园上值得一看的文章