AspNetCore.Docs/aspnetcore/tutorials/first-mvc-app/controller-methods-views.md

162 lines
12 KiB
Markdown
Raw Normal View History

2016-10-29 01:35:15 +08:00
---
title: Part 6, controller methods and views in ASP.NET Core
2016-10-29 01:35:15 +08:00
author: rick-anderson
description: Part 6, add a model to an ASP.NET Core MVC app
2018-01-29 23:21:31 +08:00
ms.author: riande
2018-12-21 08:42:01 +08:00
ms.date: 12/13/2018
no-loc: [Home, Privacy, Kestrel, appsettings.json, "ASP.NET Core Identity", cookie, Cookie, Blazor, "Blazor Server", "Blazor WebAssembly", "Identity", "Let's Encrypt", Razor, SignalR]
2016-10-29 01:35:15 +08:00
uid: tutorials/first-mvc-app/controller-methods-views
---
# Part 6, controller methods and views in ASP.NET Core
2016-10-29 01:35:15 +08:00
By [Rick Anderson](https://twitter.com/RickAndMSFT)
We have a good start to the movie app, but the presentation isn't ideal, for example, **ReleaseDate** should be two words.
2016-10-29 01:35:15 +08:00
![Index view: Release Date is one word (no space) and every movie release date shows a time of 12 AM](working-with-sql/_static/m55.png)
2016-10-29 01:35:15 +08:00
Open the *Models/Movie.cs* file and add the highlighted lines shown below:
[!code-csharp[](start-mvc/sample/MvcMovie22/Models/MovieDateFixed.cs?name=snippet_1&highlight=2,3,12-13,17)]
We cover [DataAnnotations](/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-6) in the next tutorial. The [Display](/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.metadata.displaymetadata) attribute specifies what to display for the name of a field (in this case "Release Date" instead of "ReleaseDate"). The [DataType](/dotnet/api/microsoft.aspnetcore.mvc.dataannotations.internal.datatypeattributeadapter) attribute specifies the type of the data (Date), so the time information stored in the field isn't displayed.
The `[Column(TypeName = "decimal(18, 2)")]` data annotation is required so Entity Framework Core can correctly map `Price` to currency in the database. For more information, see [Data Types](/ef/core/modeling/relational/data-types).
Browse to the `Movies` controller and hold the mouse pointer over an **Edit** link to see the target URL.
![Browser window with mouse over the Edit link and a link Url of https://localhost:5001/Movies/Edit/5 is shown](~/tutorials/first-mvc-app/controller-methods-views/_static/edit7.png)
The **Edit**, **Details**, and **Delete** links are generated by the Core MVC Anchor Tag Helper in the *Views/Movies/Index.cshtml* file.
2020-07-10 06:22:19 +08:00
[!code-cshtml[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Views/Movies/IndexOriginal.cshtml?highlight=1-3&range=46-50)]
[Tag Helpers](xref:mvc/views/tag-helpers/intro) 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 developer tools to examine the generated markup. A portion of the generated HTML is shown below:
```html
<td>
<a href="/Movies/Edit/4"> Edit </a> |
<a href="/Movies/Details/4"> Details </a> |
<a href="/Movies/Delete/4"> Delete </a>
</td>
```
Recall the format for [routing](xref:mvc/controllers/routing) set in the *Startup.cs* file:
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie3/Startup.cs?name=snippet_1&highlight=5)]
ASP.NET Core translates `https://localhost:5001/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.)
2018-12-21 08:42:01 +08:00
[Tag Helpers](xref:mvc/views/tag-helpers/intro) are one of the most popular new features in ASP.NET Core. For more information, see [Additional resources](#additional-resources).
Routing 3.X MAJOR rewrite/update (#16335) * Pivot Controller routing content based on version * Update routing content for 3.0 Significant rewrite of 3.0 focused on: - Making routing concepts more prominent - Decrease conflation of routing and MVC in docs - Banish mentions of old features to the land of wind and ghosts - Avoid the term MVC when we're actually referring to Controllers and Razor Pages * Update routing.md (#16336) * Update routing.md * Update routing.md * Update routing.md * Update routing.md * Update routing.md * Update routing.md * Rick's PR of Ryans routing PR (#16456) * Rick's PR of Ryans routing PR * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * almost done * almost done * almost done * almost done * almost done * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * more work * Apply suggestions from code review Co-Authored-By: Ryan Nowak <nowakra@gmail.com> Co-Authored-By: James Newton-King <james@newtonking.com> * react to feedback part 1 * react to feedback part 1 * react to feedback part 1 * react to feedback part 1 * react to feedback part 1 * react to feedback * react to feedback * react to feedback * clean up * clean up * clean up * clean up * clean up * clean up * clean up * clean up * clean up * clean up * Apply suggestions from code review Accepting all these fantastic suggestions. Co-Authored-By: Kirk Larkin <6025110+serpent5@users.noreply.github.com> * Apply suggestions from code review Co-Authored-By: Kirk Larkin <6025110+serpent5@users.noreply.github.com> * Update aspnetcore/fundamentals/routing/samples/3.x/RoutingSample/EndpointInspectorStartup.cs Co-Authored-By: Kirk Larkin <6025110+serpent5@users.noreply.github.com> * work * Apply suggestions from code review Co-Authored-By: Kirk Larkin <6025110+serpent5@users.noreply.github.com> * work * work * Update aspnetcore/mvc/controllers/routing.md Co-Authored-By: Ryan Nowak <nowakra@gmail.com> * Update aspnetcore/mvc/controllers/routing.md Co-Authored-By: Ryan Nowak <nowakra@gmail.com> * Update aspnetcore/fundamentals/routing.md Co-Authored-By: Ryan Nowak <nowakra@gmail.com> * react to feedback * Apply suggestions from code review Co-Authored-By: Ryan Nowak <nowakra@gmail.com> * react to feedback * react to feedback * react to feedback * react to feedback * react to feedback * clean up * work * work * simplify MyDisplayRouteInfo return * simplify MyDisplayRouteInfo return * simplify MyDisplayRouteInfo return * simplify MyDisplayRouteInfo return Co-authored-by: Ryan Nowak <nowakra@gmail.com> Co-authored-by: James Newton-King <james@newtonking.com> Co-authored-by: Kirk Larkin <6025110+serpent5@users.noreply.github.com> * Update publish-to-azure-webapp-using-vscode.md Co-authored-by: Rick Anderson <3605364+Rick-Anderson@users.noreply.github.com> Co-authored-by: James Newton-King <james@newtonking.com> Co-authored-by: Kirk Larkin <6025110+serpent5@users.noreply.github.com>
2020-03-25 12:41:44 +08:00
<a name="get-post"></a>
Open the `Movies` controller and examine the two `Edit` action methods. The following code shows the `HTTP GET Edit` method, which fetches the movie and populates the edit form generated by the *Edit.cshtml* Razor file.
::: moniker range=">= aspnetcore-2.1"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie21/Controllers/MC1.cs?name=snippet_edit1)]
The following code shows the `HTTP POST Edit` method, which processes the posted movie values:
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Controllers/MC1.cs?name=snippet_edit2)]
::: moniker-end
::: moniker range="<= aspnetcore-2.0"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Controllers/MC1.cs?name=snippet_edit1)]
The following code shows the `HTTP POST Edit` method, which processes the posted movie values:
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Controllers/MC1.cs?name=snippet_edit2)]
::: moniker-end
2016-10-29 01:35:15 +08:00
2019-07-11 10:36:05 +08:00
The `[Bind]` attribute is one way to protect against [over-posting](/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application#overpost). You should only include properties in the `[Bind]` attribute that you want to change. For more information, see [Protect your controller from over-posting](/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application). [ViewModels](https://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp-net-mvc-applications/) provide an alternative approach to prevent over-posting.
Notice the second `Edit` action method is preceded by the `[HttpPost]` attribute.
::: moniker range=">= aspnetcore-2.1"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie21/Controllers/MC1.cs?name=snippet_edit2&highlight=1)]
::: moniker-end
::: moniker range="<= aspnetcore-2.0"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Controllers/MC1.cs?name=snippet_edit2&highlight=4)]
::: moniker-end
The `HttpPost` 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 `ValidateAntiForgeryToken` attribute is used to [prevent forgery of a request](xref:security/anti-request-forgery) 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](xref:mvc/views/working-with-forms).
2020-07-10 06:22:19 +08:00
[!code-cshtml[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Views/Movies/Edit.cshtml?range=9)]
The [Form Tag Helper](xref:mvc/views/working-with-forms) 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 <xref:security/anti-request-forgery>.
The `HttpGet Edit` method takes the movie `ID` parameter, looks up the movie using the Entity Framework `FindAsync` method, and returns the selected movie to the Edit view. If a movie cannot be found, `NotFound` (HTTP 404) is returned.
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie21/Controllers/MC1.cs?name=snippet_edit1)]
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:
2020-07-10 06:22:19 +08:00
[!code-cshtml[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie22/Views/Movies/EditOriginal.cshtml)]
Notice how the view template has a `@model MvcMovie.Models.Movie` statement at the top of the file. `@model MvcMovie.Models.Movie` specifies that the view expects the model for the view template to be of type `Movie`.
2020-06-09 21:25:16 +08:00
The scaffolded code uses several Tag Helper methods to streamline the HTML markup. The [Label Tag Helper](xref:mvc/views/working-with-forms) displays the name of the field ("Title", "ReleaseDate", "Genre", or "Price"). The [Input Tag Helper](xref:mvc/views/working-with-forms) renders an HTML `<input>` element. The [Validation Tag Helper](xref:mvc/views/working-with-forms) 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.
[!code-HTML[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Views/Shared/edit_view_source.html?highlight=1,6,10,17,24,28)]
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](xref:security/anti-request-forgery) token generated by the [Form Tag Helper](xref:mvc/views/working-with-forms).
## Processing the POST Request
The following listing shows the `[HttpPost]` version of the `Edit` action method.
::: moniker range=">= aspnetcore-2.1"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie21/Controllers/MC1.cs?name=snippet_edit2)]
::: moniker-end
::: moniker range="<= aspnetcore-2.0"
[!code-csharp[](~/tutorials/first-mvc-app/start-mvc/sample/MvcMovie/Controllers/MC1.cs?name=snippet_edit2)]
::: moniker-end
The `[ValidateAntiForgeryToken]` attribute validates the hidden [XSRF](xref:security/anti-request-forgery) token generated by the anti-forgery token generator in the [Form Tag Helper](xref:mvc/views/working-with-forms)
The [model binding](xref:mvc/models/model-binding) system takes the posted form values and creates a `Movie` object that's passed as the `movie` parameter. The `ModelState.IsValid` property 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.
2018-12-21 08:42:01 +08:00
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 isn't 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](xref:mvc/models/validation) in more detail. The [Validation Tag Helper](xref:mvc/views/working-with-forms) in the *Views/Movies/Edit.cshtml* view template takes care of displaying appropriate error messages.
![Edit view: An exception for an incorrect Price value of abc states that The field Price must be a number. An exception for an incorrect Release Date value of xyz states Please enter a valid date.](~/tutorials/first-mvc-app/controller-methods-views/_static/val.png)
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. Modifying data in an `HTTP GET` method also violates HTTP best practices and the architectural [REST](http://rest.elkstein.org/) pattern, which specifies that GET requests shouldn't 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](xref:fundamentals/localization)
* [Introduction to Tag Helpers](xref:mvc/views/tag-helpers/intro)
* [Author Tag Helpers](xref:mvc/views/tag-helpers/authoring)
* <xref:security/anti-request-forgery>
* Protect your controller from [over-posting](/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application)
2019-07-11 10:36:05 +08:00
* [ViewModels](https://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp-net-mvc-applications/)
* [Form Tag Helper](xref:mvc/views/working-with-forms)
* [Input Tag Helper](xref:mvc/views/working-with-forms)
* [Label Tag Helper](xref:mvc/views/working-with-forms)
* [Select Tag Helper](xref:mvc/views/working-with-forms)
* [Validation Tag Helper](xref:mvc/views/working-with-forms)
2016-10-29 01:35:15 +08:00
2018-04-05 07:51:35 +08:00
> [!div class="step-by-step"]
> [Previous](working-with-sql.md)
> [Next](search.md)