Część 6, metody kontrolera i widoki w ASP.NET CorePart 6, controller methods and views in ASP.NET Core

Autor: Rick AndersonBy Rick Anderson

Dobrze zaczynasz korzystać z aplikacji filmowej, ale prezentacja nie jest idealna, na przykład ReleaseDate powinny być dwa słowa.We have a good start to the movie app, but the presentation isn't ideal, for example, ReleaseDate should be two words.

Widok indeksu: Data wydania to jeden wyraz (bez spacji), a każda Data wydania filmu przedstawia godzinę 12 AM

Otwórz plik models/Movie. cs i Dodaj wyróżnione wiersze poniżej:Open the Models/Movie.cs file and add the highlighted lines shown below:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

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

        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }
    }
}

W następnym samouczku omówiono wszystkie Adnotacje .We cover DataAnnotations in the next tutorial. Atrybut Display określa, co ma być wyświetlane dla nazwy pola (w tym przypadku "Data wydania" zamiast "ReleaseDate").The Display attribute specifies what to display for the name of a field (in this case "Release Date" instead of "ReleaseDate"). Atrybut DataType określa typ danych (Data), więc informacje o czasie przechowywane w polu nie są wyświetlane.The DataType attribute specifies the type of the data (Date), so the time information stored in the field isn't displayed.

[Column(TypeName = "decimal(18, 2)")]Adnotacja danych jest wymagana, aby Entity Framework Core prawidłowo mapować Price do waluty w bazie danych.The [Column(TypeName = "decimal(18, 2)")] data annotation is required so Entity Framework Core can correctly map Price to currency in the database. Aby uzyskać więcej informacji, zobacz typy danych.For more information, see Data Types.

Przejdź do Movies kontrolera i przytrzymaj wskaźnik myszy nad linkiem edycji , aby zobaczyć docelowy adres URL.Browse to the Movies controller and hold the mouse pointer over an Edit link to see the target URL.

Okno przeglądarki z myszą nad linkiem edycji i pokazanym adresem URL linku https://localhost:5001/Movies/Edit/5

Linki Edytuj, szczegóły i Usuń są generowane przez pomocnika podstawowego tagu zakotwiczenia MVC w pliku views/filmy/index. cshtml .The Edit, Details, and Delete links are generated by the Core MVC Anchor Tag Helper in the Views/Movies/Index.cshtml file.

        <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>
</tr>

Pomocnicy tagów włączają kod po stronie serwera, aby uczestniczyć w tworzeniu i RENDEROWANIU elementów HTML w Razor plikach.Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files. W powyższym kodzie, AnchorTagHelper dynamicznie generuje href wartość atrybutu HTML z metody akcji kontrolera i identyfikatora trasy. Możesz użyć widoku źródła z ulubionej przeglądarki lub użyć narzędzi programistycznych do sprawdzenia wygenerowanego znacznika.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. Poniżej przedstawiono część wygenerowanego kodu HTML:A portion of the generated HTML is shown below:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Odwołaj format zestawu routingu w pliku Startup.cs :Recall the format for routing set in the Startup.cs file:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

ASP.NET Core przetłumaczy https://localhost:5001/Movies/Edit/4 żądanie na Edit metodę akcji Movies kontrolera z parametrem Id 4.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. (Metody kontrolera są również nazywane metodami akcji).(Controller methods are also known as action methods.)

Pomocnicy tagów to jedna z najpopularniejszych nowych funkcji w programie ASP.NET Core.Tag Helpers are one of the most popular new features in ASP.NET Core. Aby uzyskać więcej informacji, zobacz dodatkowe zasoby.For more information, see Additional resources.

Otwórz Movies kontroler i Przeanalizuj dwie Edit metody akcji.Open the Movies controller and examine the two Edit action methods. Poniższy kod przedstawia HTTP GET Edit metodę, która pobiera film i wypełnia formularz edycji wygenerowany przez plik Edit. cshtml Razor .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.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Poniższy kod przedstawia HTTP POST Edit metodę, która przetwarza ogłoszone wartości filmu:The following code shows the HTTP POST Edit method, which processes the posted movie values:

// 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,Title,ReleaseDate,Genre,Price")] 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);
}
// 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);
}

Poniższy kod przedstawia HTTP POST Edit metodę, która przetwarza ogłoszone wartości filmu:The following code shows the HTTP POST Edit method, which processes the posted movie values:

// 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,Title,ReleaseDate,Genre,Price")] 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);
}

Ten [Bind] atrybut jest jednym ze sposobów ochrony przed nadmiernym publikowaniem.The [Bind] attribute is one way to protect against over-posting. Należy uwzględnić tylko właściwości w [Bind] atrybucie, który ma zostać zmieniony.You should only include properties in the [Bind] attribute that you want to change. Aby uzyskać więcej informacji, zobacz Ochrona kontrolera przed nadmiernym publikowaniem.For more information, see Protect your controller from over-posting. Modele widoków zapewniają alternatywne podejście do zapobiegania nadmiernemu księgowaniu.ViewModels provide an alternative approach to prevent over-posting.

Zwróć uwagę, że druga Edit Metoda działania jest poprzedzona [HttpPost] atrybutem.Notice the second Edit action method is preceded by the [HttpPost] attribute.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] 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(nameof(Index));
    }
    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,Title,ReleaseDate,Genre,Price")] 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);
}

HttpPostTen atrybut określa, że ta Edit Metoda może być wywoływana tylko w przypadku POST żądań.The HttpPost attribute specifies that this Edit method can be invoked only for POST requests. Można zastosować [HttpGet] atrybut do pierwszej metody edycji, ale nie jest to konieczne, ponieważ jest to [HttpGet] wartość domyślna.You could apply the [HttpGet] attribute to the first edit method, but that's not necessary because [HttpGet] is the default.

Ten ValidateAntiForgeryToken atrybut służy do zapobiegania fałszerstwu żądania i jest sparowany z tokenem chroniącym przed fałszerstwem wygenerowanym w pliku widoku edycji (widoki/filmy/Edit. cshtml).The ValidateAntiForgeryToken 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). Plik widoku edycji generuje token chroniący przed fałszerstwem za pomocą pomocnika tagu formularza.The edit view file generates the anti-forgery token with the Form Tag Helper.

<form asp-action="Edit">

Pomocnik tagu formularza generuje ukryty token chroniący przed fałszerstwem, który musi być zgodny z [ValidateAntiForgeryToken] wygenerowanym tokenem chroniącym przed fałszerstwem w Edit metodzie kontrolera filmów.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. Aby uzyskać więcej informacji, zobacz Uniemożliwiaj ataki między witrynami (XSRF/CSRF) w ASP.NET Core.For more information, see Uniemożliwiaj ataki między witrynami (XSRF/CSRF) w ASP.NET Core.

HttpGet EditMetoda przyjmuje ID parametr filmu, wyszukuje film przy użyciu metody Entity Framework FindAsync i zwraca wybrany film do widoku edycji.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. Jeśli nie można znaleźć filmu, NotFound zwracany jest (HTTP 404).If a movie cannot be found, NotFound (HTTP 404) is returned.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Gdy system szkieletu utworzył widok edycji, zbadał Movie klasę i utworzony kod w celu renderowania <label> i <input> elementów dla każdej właściwości klasy.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. W poniższym przykładzie przedstawiono widok edycji, który został wygenerowany przez system szkieletu programu Visual Studio:The following example shows the Edit view that was generated by the Visual Studio scaffolding system:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Zwróć uwagę, jak szablon widoku zawiera @model MvcMovie.Models.Movie instrukcję w górnej części pliku.Notice how the view template has a @model MvcMovie.Models.Movie statement at the top of the file. @model MvcMovie.Models.Movie Określa, że widok oczekuje modelu dla szablonu widoku, który ma być typu Movie .@model MvcMovie.Models.Movie specifies that the view expects the model for the view template to be of type Movie.

Kod szkieletowy używa kilku metod pomocnika tagów do uproszczenia znacznika HTML.The scaffolded code uses several Tag Helper methods to streamline the HTML markup. Pomocnik tagu etykiety wyświetla nazwę pola ("title", "ReleaseDate", "gatunek" lub "price").The Label Tag Helper displays the name of the field ("Title", "ReleaseDate", "Genre", or "Price"). Pomocnik tagu wejściowego renderuje <input> element HTML.The Input Tag Helper renders an HTML <input> element. Pomocnik tagów walidacji wyświetla wszystkie komunikaty weryfikacyjne skojarzone z tą właściwością.The Validation Tag Helper displays any validation messages associated with that property.

Uruchom aplikację i przejdź do /Movies adresu URL.Run the application and navigate to the /Movies URL. Kliknij link Edytuj .Click an Edit link. Sprawdź Źródło strony w przeglądarce.In the browser, view the source for the page. Wygenerowany kod HTML dla <form> elementu jest przedstawiony poniżej.The generated HTML for the <form> element is shown below.

<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"></span>
            </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"></span>
            </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>

<input>Elementy znajdują się w HTML <form> elemencie, którego action atrybut ma ustawioną wartość post na /Movies/Edit/id adres URL.The <input> elements are in an HTML <form> element whose action attribute is set to post to the /Movies/Edit/id URL. Dane formularza zostaną opublikowane na serwerze po Save kliknięciu przycisku.The form data will be posted to the server when the Save button is clicked. Ostatni wiersz przed zamykającym </form> elementu pokazuje ukryty token XSRF wygenerowany przez pomocnika tagów formularza.The last line before the closing </form> element shows the hidden XSRF token generated by the Form Tag Helper.

Przetwarzanie żądania POSTProcessing the POST Request

Na poniższej liście przedstawiono [HttpPost] wersję Edit metody akcji.The following listing shows the [HttpPost] version of the Edit action method.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] 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(nameof(Index));
    }
    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,Title,ReleaseDate,Genre,Price")] 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);
}

Ten [ValidateAntiForgeryToken] atrybut sprawdza poprawność ukrytego tokenu XSRF wygenerowanego przez generatora tokenów chroniących przed fałszowaniem w Pomocniku tagów formularzaThe [ValidateAntiForgeryToken] attribute validates the hidden XSRF token generated by the anti-forgery token generator in the Form Tag Helper

System powiązań modelu przyjmuje wartości ogłoszonych formularzy i tworzy Movie obiekt, który jest przesyłany jako movie parametr.The model binding system takes the posted form values and creates a Movie object that's passed as the movie parameter. ModelState.IsValidWłaściwość weryfikuje, że dane przesłane w formularzu mogą służyć do modyfikowania (edycji lub aktualizowania) Movie obiektu.The ModelState.IsValid property verifies that the data submitted in the form can be used to modify (edit or update) a Movie object. Jeśli dane są prawidłowe, zostaną zapisane.If the data is valid, it's saved. Zaktualizowane (edytowane) dane filmu są zapisywane w bazie danych przez wywołanie SaveChangesAsync metody kontekstu bazy danych.The updated (edited) movie data is saved to the database by calling the SaveChangesAsync method of database context. Po zapisaniu danych kod przekierowuje użytkownika do Index metody akcji MoviesController klasy, która wyświetla kolekcję filmów, włącznie z wprowadzonymi zmianami.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.

Przed opublikowaniem formularza na serwerze sprawdzanie poprawności po stronie klienta sprawdza wszystkie reguły sprawdzania poprawności w polach.Before the form is posted to the server, client-side validation checks any validation rules on the fields. Jeśli wystąpią jakieś błędy sprawdzania poprawności, zostanie wyświetlony komunikat o błędzie z informacją, że formularz nie zostanie opublikowany.If there are any validation errors, an error message is displayed and the form isn't posted. Jeśli język JavaScript jest wyłączony, nie będzie można sprawdzić poprawności po stronie klienta, ale serwer wykryje ogłoszone wartości, które nie są prawidłowe, a wartości formularza zostaną wyświetlone ponownie przy użyciu komunikatów o błędach.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. W dalszej części samouczka sprawdzimy Sprawdzanie poprawności modelu w bardziej szczegółowy sposób.Later in the tutorial we examine Model Validation in more detail. Pomocnik tagów walidacji w szablonie widoki/filmy/edytowanie. cshtml ma zadbać o wyświetlenie odpowiednich komunikatów o błędach.The Validation Tag Helper in the Views/Movies/Edit.cshtml view template takes care of displaying appropriate error messages.

Widok edycji: wyjątek dla nieprawidłowej wartości ceny ABC wskazuje, że cena pola musi być liczbą.

Wszystkie HttpGet metody w kontrolerze filmu są zgodne z podobnym wzorcem.All the HttpGet methods in the movie controller follow a similar pattern. Uzyskują one obiekt filmu (lub listę obiektów w przypadku Index ) i przekazują obiekt (model) do widoku.They get a movie object (or list of objects, in the case of Index), and pass the object (model) to the view. CreateMetoda przekazuje pusty obiekt filmu do Create widoku.The Create method passes an empty movie object to the Create view. Wszystkie metody, które tworzą, edytują, usuwają lub w inny sposób modyfikują dane, to w ramach [HttpPost] przeciążenia metody.All the methods that create, edit, delete, or otherwise modify data do so in the [HttpPost] overload of the method. Modyfikowanie danych w HTTP GET metodzie stanowi zagrożenie bezpieczeństwa.Modifying data in an HTTP GET method is a security risk. Modyfikowanie danych w HTTP GET metodzie również narusza najlepsze rozwiązania protokołu HTTP i wzorzec rest architektury, który określa, że żądania GET nie powinny zmieniać stanu aplikacji.Modifying data in an HTTP GET method also violates HTTP best practices and the architectural REST pattern, which specifies that GET requests shouldn't change the state of your application. Innymi słowy wykonanie operacji GET powinno być operacją bezpieczną, która nie ma efektów ubocznych i nie modyfikuje utrwalonych danych.In other words, performing a GET operation should be a safe operation that has no side effects and doesn't modify your persisted data.

Dodatkowe zasobyAdditional resources