Część 6, metody kontrolera i widoki w ASP.NET Core

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Autor: Rick Anderson

Mamy dobry początek aplikacji filmowej, ale prezentacja nie jest idealna, na przykład ReleaseDate powinna być dwoma słowami.

Widok indeksu: Data wydania to jedno słowo (bez spacji), a każda data wydania filmu pokazuje godzinę 12:00

Models/Movie.cs Otwórz plik i dodaj wyróżnione wiersze pokazane poniżej:

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

DataAnnotations objaśniono w następnym samouczku. Atrybut Wyświetlania określa, co ma być wyświetlane dla nazwy pola (w tym przypadku "Data wydania" zamiast "Data wydania"). Atrybut DataType określa typ danych (Data), więc informacje o czasie przechowywane w polu nie są wyświetlane.

Adnotacja danych jest wymagana [Column(TypeName = "decimal(18, 2)")] , aby program Entity Framework Core mógł poprawnie mapować Price na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych.

Przejdź do Movies kontrolera i przytrzymaj wskaźnik myszy nad linkiem Edytuj , aby wyświetlić docelowy adres URL.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:5001/Movies/Edit/5

Łącza Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Views/Movies/Index.cshtml zakotwiczenia podstawowego MVC w pliku.

        <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 umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor. W powyższym AnchorTagHelper kodzie dynamicznie generuje wartość atrybutu HTML href z metody akcji kontrolera i identyfikator trasy. Użyjesz pozycji Wyświetl źródło z ulubionej przeglądarki lub użyjesz narzędzi deweloperskich do zbadania wygenerowanego znacznika. Poniżej przedstawiono część wygenerowanego kodu HTML:

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

Przypomnij sobie format zestawu routinguProgram.cs w pliku:

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

ASP.NET Core przekłada się https://localhost:5001/Movies/Edit/4 na żądanie do Edit metody Movies akcji kontrolera z parametrem Id 4. (Metody kontrolera są również nazywane metodami akcji).

Pomocnicy tagów są jedną z najpopularniejszych nowych funkcji w ASP.NET Core. Aby uzyskać więcej informacji, zobacz Dodatkowe zasoby.

Movies Otwórz kontroler i sprawdź dwie Edit metody akcji. Poniższy kod przedstawia metodę HTTP GET Edit , która pobiera film i wypełnia formularz edycji Edit.cshtmlRazor wygenerowany przez plik.

// 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 metodę HTTP POST Edit , która przetwarza opublikowane wartości filmu:

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [Bind] jest jednym ze sposobów ochrony przed nadmiernym publikowaniem. Właściwości należy uwzględnić tylko w atrybucie [Bind] , który chcesz zmienić. Aby uzyskać więcej informacji, zobacz Ochrona kontrolera przed nadmiernym publikowaniem. Modely ViewModel stanowią alternatywną metodę zapobiegania nadmiernemu delegowaniu.

Zwróć uwagę, że druga Edit metoda akcji jest poprzedzona atrybutem [HttpPost] .

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut HttpPost określa, że ta Edit metoda może być wywoływana tylko dla POST żądań. Atrybut można zastosować [HttpGet] do pierwszej metody edycji, ale nie jest to konieczne, ponieważ [HttpGet] jest to ustawienie domyślne.

Atrybut ValidateAntiForgeryToken służy do zapobiegania fałszerzowaniu żądania i jest sparowany z tokenem antyugeryjnym wygenerowanym w pliku widoku edycji (Views/Movies/Edit.cshtml). Plik widoku edycji generuje token ochrony przed fałszerzami za pomocą pomocnika tagów formularzy.

<form asp-action="Edit">

Pomocnik tagów formularzy generuje ukryty token ochrony przed fałszerzowania, który musi być zgodny [ValidateAntiForgeryToken] z wygenerowanym tokenem ochrony przed fałszercją w Edit metodzie kontrolera Filmy. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Metoda HttpGet Edit pobiera parametr filmu ID , wyszukuje film przy użyciu metody Entity Framework FindAsync i zwraca wybrany film do widoku Edytuj. Jeśli nie można odnaleźć filmu, NotFound zostanie zwrócony (HTTP 404).

// 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 tworzenia szkieletów utworzył widok Edycji, przeanalizował klasę Movie i utworzył kod renderujący <label> i <input> elementy dla każdej właściwości klasy. W poniższym przykładzie pokazano widok Edycji wygenerowany przez system tworzenia szkieletów programu Visual Studio:

@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ę, że szablon widoku zawiera instrukcję @model MvcMovie.Models.Movie w górnej części pliku. @model MvcMovie.Models.Movie określa, że widok oczekuje, że model szablonu widoku będzie typu Movie.

Kod szkieletowy używa kilku metod pomocnika tagów w celu usprawnienia znaczników HTML. Pomocnik tagów etykiet wyświetla nazwę pola ("Title", "ReleaseDate", "Gatunek" lub "Price"). Pomocnik tagów wejściowych renderuje element HTML<input>. Pomocnik tagu weryfikacji wyświetla wszystkie komunikaty sprawdzania poprawności skojarzone z tą właściwością.

Uruchom aplikację i przejdź do /Movies adresu URL. Kliknij link Edytuj. W przeglądarce wyświetl źródło strony. Wygenerowany kod HTML dla <form> elementu jest pokazany poniżej.

<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 elemecieHTML <form>, którego action atrybut jest ustawiony na publikowanie w adresie /Movies/Edit/id URL. Dane formularza zostaną opublikowane na serwerze po kliknięciu Save przycisku. Ostatni wiersz przed elementem zamykającym </form> pokazuje ukryty token XSRF wygenerowany przez pomocnika tagów formularzy.

Przetwarzanie żądania POST

Na poniższej [HttpPost] liście przedstawiono wersję Edit metody akcji.

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [ValidateAntiForgeryToken] sprawdza poprawność ukrytego tokenu XSRF wygenerowanego przez generator tokenów chroniących przed fałszerzami w pomocniku tagów formularzy

System powiązania modelu przyjmuje opublikowane wartości formularza i tworzy Movie obiekt, który jest przekazywany jako movie parametr. Właściwość ModelState.IsValid sprawdza, czy dane przesłane w formularzu mogą służyć do modyfikowania (edytowania lub aktualizowania) Movie obiektu. Jeśli dane są prawidłowe, są zapisywane. Zaktualizowane (edytowane) dane filmu są zapisywane w bazie danych przez wywołanie SaveChangesAsync metody kontekstu bazy danych. Po zapisaniu danych kod przekierowuje użytkownika do Index metody MoviesController akcji klasy , która wyświetla kolekcję filmów, w tym wprowadzone zmiany.

Przed opublikowaniem formularza na serwerze walidacja po stronie klienta sprawdza wszystkie reguły walidacji w polach. Jeśli wystąpią jakiekolwiek błędy walidacji, zostanie wyświetlony komunikat o błędzie i formularz nie zostanie opublikowany. Jeśli język JavaScript jest wyłączony, nie będziesz mieć weryfikacji po stronie klienta, ale serwer wykryje nieprawidłowe wartości opublikowane, a wartości formularza będą odtwarzane ponownie z komunikatami o błędach. W dalszej części samouczka bardziej szczegółowo sprawdzimy weryfikację modelu. Pomocnik tagów weryfikacji w szablonie Views/Movies/Edit.cshtml widoku zajmuje się wyświetlaniem odpowiednich komunikatów o błędach.

Widok edycji: wyjątek nieprawidłowej wartości price abc wskazuje, że pole Cena musi być liczbą. Wyjątek dla nieprawidłowej wartości daty wydania stanów xyz Wprowadź prawidłową datę.

HttpGet Wszystkie metody w kontrolerze filmu są zgodne z podobnym wzorcem. Pobierają obiekt filmu (lub listę obiektów, w przypadku Index), i przekazują obiekt (model) do widoku. Metoda Create przekazuje pusty obiekt filmu do Create widoku. Wszystkie metody, które tworzą, edytują, usuwają lub w inny sposób modyfikują dane, robią to w [HttpPost] przeciążeniu metody . Modyfikowanie danych w metodzie HTTP GET jest zagrożeniem bezpieczeństwa. Modyfikowanie danych w metodzie HTTP GET narusza również najlepsze rozwiązania HTTP i wzorzec architektury REST , który określa, że żądania GET nie powinny zmieniać stanu aplikacji. Innymi słowy wykonanie operacji GET powinno być bezpieczną operacją, która nie ma skutków ubocznych i nie modyfikuje utrwalone dane.

Dodatkowe zasoby

Mamy dobry początek aplikacji filmowej, ale prezentacja nie jest idealna, na przykład ReleaseDate powinna być dwoma słowami.

Widok indeksu: Data wydania to jedno słowo (bez spacji), a każda data wydania filmu pokazuje godzinę 12:00

Models/Movie.cs Otwórz plik i dodaj wyróżnione wiersze pokazane poniżej:

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

DataAnnotations objaśniono w następnym samouczku. Atrybut Wyświetlania określa, co ma być wyświetlane dla nazwy pola (w tym przypadku "Data wydania" zamiast "Data wydania"). Atrybut DataType określa typ danych (Data), więc informacje o czasie przechowywane w polu nie są wyświetlane.

Adnotacja danych jest wymagana [Column(TypeName = "decimal(18, 2)")] , aby program Entity Framework Core mógł poprawnie mapować Price na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych.

Przejdź do Movies kontrolera i przytrzymaj wskaźnik myszy nad linkiem Edytuj , aby wyświetlić docelowy adres URL.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:5001/Movies/Edit/5

Łącza Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Views/Movies/Index.cshtml zakotwiczenia podstawowego MVC w pliku.

        <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 umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor. W powyższym AnchorTagHelper kodzie dynamicznie generuje wartość atrybutu HTML href z metody akcji kontrolera i identyfikator trasy. Użyjesz pozycji Wyświetl źródło z ulubionej przeglądarki lub użyjesz narzędzi deweloperskich do zbadania wygenerowanego znacznika. Poniżej przedstawiono część wygenerowanego kodu HTML:

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

Przypomnij sobie format zestawu routinguProgram.cs w pliku:

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

ASP.NET Core przekłada się https://localhost:5001/Movies/Edit/4 na żądanie do Edit metody Movies akcji kontrolera z parametrem Id 4. (Metody kontrolera są również nazywane metodami akcji).

Pomocnicy tagów są jedną z najpopularniejszych nowych funkcji w ASP.NET Core. Aby uzyskać więcej informacji, zobacz Dodatkowe zasoby.

Movies Otwórz kontroler i sprawdź dwie Edit metody akcji. Poniższy kod przedstawia metodę HTTP GET Edit , która pobiera film i wypełnia formularz edycji Edit.cshtmlRazor wygenerowany przez plik.

// 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 metodę HTTP POST Edit , która przetwarza opublikowane wartości filmu:

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [Bind] jest jednym ze sposobów ochrony przed nadmiernym publikowaniem. Właściwości należy uwzględnić tylko w atrybucie [Bind] , który chcesz zmienić. Aby uzyskać więcej informacji, zobacz Ochrona kontrolera przed nadmiernym publikowaniem. Modely ViewModel stanowią alternatywną metodę zapobiegania nadmiernemu delegowaniu.

Zwróć uwagę, że druga Edit metoda akcji jest poprzedzona atrybutem [HttpPost] .

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut HttpPost określa, że ta Edit metoda może być wywoływana tylko dla POST żądań. Atrybut można zastosować [HttpGet] do pierwszej metody edycji, ale nie jest to konieczne, ponieważ [HttpGet] jest to ustawienie domyślne.

Atrybut ValidateAntiForgeryToken służy do zapobiegania fałszerzowaniu żądania i jest sparowany z tokenem antyugeryjnym wygenerowanym w pliku widoku edycji (Views/Movies/Edit.cshtml). Plik widoku edycji generuje token ochrony przed fałszerzami za pomocą pomocnika tagów formularzy.

<form asp-action="Edit">

Pomocnik tagów formularzy generuje ukryty token ochrony przed fałszerzowania, który musi być zgodny [ValidateAntiForgeryToken] z wygenerowanym tokenem ochrony przed fałszercją w Edit metodzie kontrolera Filmy. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Metoda HttpGet Edit pobiera parametr filmu ID , wyszukuje film przy użyciu metody Entity Framework FindAsync i zwraca wybrany film do widoku Edytuj. Jeśli nie można odnaleźć filmu, NotFound zostanie zwrócony (HTTP 404).

// 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 tworzenia szkieletów utworzył widok Edycji, przeanalizował klasę Movie i utworzył kod renderujący <label> i <input> elementy dla każdej właściwości klasy. W poniższym przykładzie pokazano widok Edycji wygenerowany przez system tworzenia szkieletów programu Visual Studio:

@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ę, że szablon widoku zawiera instrukcję @model MvcMovie.Models.Movie w górnej części pliku. @model MvcMovie.Models.Movie określa, że widok oczekuje, że model szablonu widoku będzie typu Movie.

Kod szkieletowy używa kilku metod pomocnika tagów w celu usprawnienia znaczników HTML. Pomocnik tagów etykiet wyświetla nazwę pola ("Title", "ReleaseDate", "Gatunek" lub "Price"). Pomocnik tagów wejściowych renderuje element HTML<input>. Pomocnik tagu weryfikacji wyświetla wszystkie komunikaty sprawdzania poprawności skojarzone z tą właściwością.

Uruchom aplikację i przejdź do /Movies adresu URL. Kliknij link Edytuj. W przeglądarce wyświetl źródło strony. Wygenerowany kod HTML dla <form> elementu jest pokazany poniżej.

<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 elemecieHTML <form>, którego action atrybut jest ustawiony na publikowanie w adresie /Movies/Edit/id URL. Dane formularza zostaną opublikowane na serwerze po kliknięciu Save przycisku. Ostatni wiersz przed elementem zamykającym </form> pokazuje ukryty token XSRF wygenerowany przez pomocnika tagów formularzy.

Przetwarzanie żądania POST

Na poniższej [HttpPost] liście przedstawiono wersję Edit metody akcji.

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [ValidateAntiForgeryToken] sprawdza poprawność ukrytego tokenu XSRF wygenerowanego przez generator tokenów chroniących przed fałszerzami w pomocniku tagów formularzy

System powiązania modelu przyjmuje opublikowane wartości formularza i tworzy Movie obiekt, który jest przekazywany jako movie parametr. Właściwość ModelState.IsValid sprawdza, czy dane przesłane w formularzu mogą służyć do modyfikowania (edytowania lub aktualizowania) Movie obiektu. Jeśli dane są prawidłowe, są zapisywane. Zaktualizowane (edytowane) dane filmu są zapisywane w bazie danych przez wywołanie SaveChangesAsync metody kontekstu bazy danych. Po zapisaniu danych kod przekierowuje użytkownika do Index metody MoviesController akcji klasy , która wyświetla kolekcję filmów, w tym wprowadzone zmiany.

Przed opublikowaniem formularza na serwerze walidacja po stronie klienta sprawdza wszystkie reguły walidacji w polach. Jeśli wystąpią jakiekolwiek błędy walidacji, zostanie wyświetlony komunikat o błędzie i formularz nie zostanie opublikowany. Jeśli język JavaScript jest wyłączony, nie będziesz mieć weryfikacji po stronie klienta, ale serwer wykryje nieprawidłowe wartości opublikowane, a wartości formularza będą odtwarzane ponownie z komunikatami o błędach. W dalszej części samouczka bardziej szczegółowo sprawdzimy weryfikację modelu. Pomocnik tagów weryfikacji w szablonie Views/Movies/Edit.cshtml widoku zajmuje się wyświetlaniem odpowiednich komunikatów o błędach.

Widok edycji: wyjątek nieprawidłowej wartości price abc wskazuje, że pole Cena musi być liczbą. Wyjątek dla nieprawidłowej wartości daty wydania stanów xyz Wprowadź prawidłową datę.

HttpGet Wszystkie metody w kontrolerze filmu są zgodne z podobnym wzorcem. Pobierają obiekt filmu (lub listę obiektów, w przypadku Index), i przekazują obiekt (model) do widoku. Metoda Create przekazuje pusty obiekt filmu do Create widoku. Wszystkie metody, które tworzą, edytują, usuwają lub w inny sposób modyfikują dane, robią to w [HttpPost] przeciążeniu metody . Modyfikowanie danych w metodzie HTTP GET jest zagrożeniem bezpieczeństwa. Modyfikowanie danych w metodzie HTTP GET narusza również najlepsze rozwiązania HTTP i wzorzec architektury REST , który określa, że żądania GET nie powinny zmieniać stanu aplikacji. Innymi słowy wykonanie operacji GET powinno być bezpieczną operacją, która nie ma skutków ubocznych i nie modyfikuje utrwalone dane.

Dodatkowe zasoby

Mamy dobry początek aplikacji filmowej, ale prezentacja nie jest idealna, na przykład ReleaseDate powinna być dwoma słowami.

Widok indeksu: Data wydania to jedno słowo (bez spacji), a każda data wydania filmu pokazuje godzinę 12:00

Models/Movie.cs Otwórz plik i dodaj wyróżnione wiersze pokazane poniżej:

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

DataAnnotations objaśniono w następnym samouczku. Atrybut Wyświetlania określa, co ma być wyświetlane dla nazwy pola (w tym przypadku "Data wydania" zamiast "Data wydania"). Atrybut DataType określa typ danych (Data), więc informacje o czasie przechowywane w polu nie są wyświetlane.

Adnotacja danych jest wymagana [Column(TypeName = "decimal(18, 2)")] , aby program Entity Framework Core mógł poprawnie mapować Price na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych.

Przejdź do Movies kontrolera i przytrzymaj wskaźnik myszy nad linkiem Edytuj , aby wyświetlić docelowy adres URL.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:5001/Movies/Edit/5

Łącza Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Views/Movies/Index.cshtml zakotwiczenia podstawowego MVC w pliku.

        <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 umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor. W powyższym AnchorTagHelper kodzie dynamicznie generuje wartość atrybutu HTML href z metody akcji kontrolera i identyfikator trasy. Użyjesz pozycji Wyświetl źródło z ulubionej przeglądarki lub użyjesz narzędzi deweloperskich do zbadania wygenerowanego znacznika. Poniżej przedstawiono część wygenerowanego kodu HTML:

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

Przypomnij sobie format zestawu routinguProgram.cs w pliku:

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

ASP.NET Core przekłada się https://localhost:5001/Movies/Edit/4 na żądanie do Edit metody Movies akcji kontrolera z parametrem Id 4. (Metody kontrolera są również nazywane metodami akcji).

Pomocnicy tagów są popularną funkcją w ASP.NET Core. Aby uzyskać więcej informacji na ich temat, zobacz Dodatkowe zasoby.

Movies Otwórz kontroler i sprawdź dwie Edit metody akcji. Poniższy kod przedstawia metodę HTTP GET Edit , która pobiera film i wypełnia formularz edycji Edit.cshtmlRazor wygenerowany przez plik.

// 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 metodę HTTP POST Edit , która przetwarza opublikowane wartości filmu:

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [Bind] jest jednym ze sposobów ochrony przed nadmiernym publikowaniem. Właściwości należy uwzględnić tylko w atrybucie [Bind] , który chcesz zmienić. Aby uzyskać więcej informacji, zobacz Ochrona kontrolera przed nadmiernym publikowaniem. Modely ViewModel stanowią alternatywną metodę zapobiegania nadmiernemu delegowaniu.

Zwróć uwagę, że druga Edit metoda akcji jest poprzedzona atrybutem [HttpPost] .

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut HttpPost określa, że ta Edit metoda może być wywoływana tylko dla POST żądań. Atrybut można zastosować [HttpGet] do pierwszej metody edycji, ale nie jest to konieczne, ponieważ [HttpGet] jest to ustawienie domyślne.

Atrybut ValidateAntiForgeryToken służy do zapobiegania fałszerzowaniu żądania i jest sparowany z tokenem antyugeryjnym wygenerowanym w pliku widoku edycji (Views/Movies/Edit.cshtml). Plik widoku edycji generuje token ochrony przed fałszerzami za pomocą pomocnika tagów formularzy.

<form asp-action="Edit">

Pomocnik tagów formularzy generuje ukryty token ochrony przed fałszerzowania, który musi być zgodny [ValidateAntiForgeryToken] z wygenerowanym tokenem ochrony przed fałszercją w Edit metodzie kontrolera Filmy. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Metoda HttpGet Edit pobiera parametr filmu ID , wyszukuje film przy użyciu metody Entity Framework FindAsync i zwraca wybrany film do widoku Edytuj. Jeśli nie można odnaleźć filmu, NotFound zostanie zwrócony (HTTP 404).

// 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 tworzenia szkieletów utworzył widok Edycji, przeanalizował klasę Movie i utworzył kod renderujący <label> i <input> elementy dla każdej właściwości klasy. W poniższym przykładzie pokazano widok Edycji wygenerowany przez system tworzenia szkieletów programu Visual Studio:

@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ę, że szablon widoku zawiera instrukcję @model MvcMovie.Models.Movie w górnej części pliku. @model MvcMovie.Models.Movie określa, że widok oczekuje, że model szablonu widoku będzie typu Movie.

Kod szkieletowy używa kilku metod pomocnika tagów w celu usprawnienia znaczników HTML. Pomocnik tagów etykiet wyświetla nazwę pola ("Title", "ReleaseDate", "Gatunek" lub "Price"). Pomocnik tagów wejściowych renderuje element HTML<input>. Pomocnik tagu weryfikacji wyświetla wszystkie komunikaty sprawdzania poprawności skojarzone z tą właściwością.

Uruchom aplikację i przejdź do /Movies adresu URL. Kliknij link Edytuj. W przeglądarce wyświetl źródło strony. Wygenerowany kod HTML dla <form> elementu jest pokazany poniżej.

<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 elemecieHTML <form>, którego action atrybut jest ustawiony na publikowanie w adresie /Movies/Edit/id URL. Dane formularza zostaną opublikowane na serwerze po kliknięciu Save przycisku. Ostatni wiersz przed elementem zamykającym </form> pokazuje ukryty token XSRF wygenerowany przez pomocnika tagów formularzy.

Przetwarzanie żądania POST

Na poniższej [HttpPost] liście przedstawiono wersję Edit metody akcji.

// POST: Movies/Edit/5
// To protect from overposting attacks, 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,Rating")] 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);
}

Atrybut [ValidateAntiForgeryToken] sprawdza poprawność ukrytego tokenu XSRF wygenerowanego przez generator tokenów chroniących przed fałszerzami w pomocniku tagów formularzy

System powiązania modelu przyjmuje opublikowane wartości formularza i tworzy Movie obiekt, który jest przekazywany jako movie parametr. Właściwość ModelState.IsValid sprawdza, czy dane przesłane w formularzu mogą służyć do modyfikowania (edytowania lub aktualizowania) Movie obiektu. Jeśli dane są prawidłowe, są zapisywane. Zaktualizowane (edytowane) dane filmu są zapisywane w bazie danych przez wywołanie SaveChangesAsync metody kontekstu bazy danych. Po zapisaniu danych kod przekierowuje użytkownika do Index metody MoviesController akcji klasy , która wyświetla kolekcję filmów, w tym wprowadzone zmiany.

Przed opublikowaniem formularza na serwerze walidacja po stronie klienta sprawdza wszystkie reguły walidacji w polach. Jeśli wystąpią jakiekolwiek błędy walidacji, zostanie wyświetlony komunikat o błędzie i formularz nie zostanie opublikowany. Jeśli język JavaScript jest wyłączony, nie będziesz mieć weryfikacji po stronie klienta, ale serwer wykryje nieprawidłowe wartości opublikowane, a wartości formularza będą odtwarzane ponownie z komunikatami o błędach. W dalszej części samouczka bardziej szczegółowo sprawdzimy weryfikację modelu. Pomocnik tagów weryfikacji w szablonie Views/Movies/Edit.cshtml widoku zajmuje się wyświetlaniem odpowiednich komunikatów o błędach.

Widok edycji: wyjątek nieprawidłowej wartości price abc wskazuje, że pole Cena musi być liczbą. Wyjątek dla nieprawidłowej wartości daty wydania stanów xyz Wprowadź prawidłową datę.

HttpGet Wszystkie metody w kontrolerze filmu są zgodne z podobnym wzorcem. Pobierają obiekt filmu (lub listę obiektów, w przypadku Index), i przekazują obiekt (model) do widoku. Metoda Create przekazuje pusty obiekt filmu do Create widoku. Wszystkie metody, które tworzą, edytują, usuwają lub w inny sposób modyfikują dane, robią to w [HttpPost] przeciążeniu metody . Modyfikowanie danych w metodzie HTTP GET jest zagrożeniem bezpieczeństwa. Modyfikowanie danych w metodzie HTTP GET narusza również najlepsze rozwiązania HTTP i wzorzec architektury REST , który określa, że żądania GET nie powinny zmieniać stanu aplikacji. Innymi słowy wykonanie operacji GET powinno być bezpieczną operacją, która nie ma skutków ubocznych i nie modyfikuje utrwalone dane.

Dodatkowe zasoby

Mamy dobry początek aplikacji filmowej, ale prezentacja nie jest idealna, na przykład ReleaseDate powinna być dwoma słowami.

Widok indeksu: Data wydania to jedno słowo (bez spacji), a każda data wydania filmu pokazuje godzinę 12:00

Models/Movie.cs Otwórz plik i dodaj wyróżnione wiersze pokazane poniżej:

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 adnotacje danych. Atrybut Wyświetlania określa, co ma być wyświetlane dla nazwy pola (w tym przypadku "Data wydania" zamiast "Data wydania"). Atrybut DataType określa typ danych (Data), więc informacje o czasie przechowywane w polu nie są wyświetlane.

Adnotacja danych jest wymagana [Column(TypeName = "decimal(18, 2)")] , aby program Entity Framework Core mógł poprawnie mapować Price na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych.

Przejdź do Movies kontrolera i przytrzymaj wskaźnik myszy nad linkiem Edytuj , aby wyświetlić docelowy adres URL.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:5001/Movies/Edit/5

Łącza Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Views/Movies/Index.cshtml zakotwiczenia podstawowego MVC w pliku.

        <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 umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor. W powyższym AnchorTagHelper kodzie dynamicznie generuje wartość atrybutu HTML href z metody akcji kontrolera i identyfikator trasy. Użyjesz pozycji Wyświetl źródło z ulubionej przeglądarki lub użyjesz narzędzi deweloperskich do zbadania wygenerowanego znacznika. Poniżej przedstawiono część wygenerowanego kodu HTML:

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

Przypomnij sobie format zestawu routinguStartup.cs w pliku:

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

ASP.NET Core przekłada się https://localhost:5001/Movies/Edit/4 na żądanie do Edit metody Movies akcji kontrolera z parametrem Id 4. (Metody kontrolera są również nazywane metodami akcji).

Aby uzyskać więcej informacji na temat pomocników tagów, zobacz Dodatkowe zasoby.

Movies Otwórz kontroler i sprawdź dwie Edit metody akcji. Poniższy kod przedstawia metodę HTTP GET Edit , która pobiera film i wypełnia formularz edycji Edit.cshtmlRazor wygenerowany przez plik.

// 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 metodę HTTP POST Edit , która przetwarza opublikowane wartości filmu:

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

Atrybut [Bind] jest jednym ze sposobów ochrony przed nadmiernym publikowaniem. Właściwości należy uwzględnić tylko w atrybucie [Bind] , który chcesz zmienić. Aby uzyskać więcej informacji, zobacz Ochrona kontrolera przed nadmiernym publikowaniem. Modely ViewModel stanowią alternatywną metodę zapobiegania nadmiernemu delegowaniu.

Zwróć uwagę, że druga Edit metoda akcji jest poprzedzona atrybutem [HttpPost] .

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

Atrybut HttpPost określa, że ta Edit metoda może być wywoływana tylko dla POST żądań. Atrybut można zastosować [HttpGet] do pierwszej metody edycji, ale nie jest to konieczne, ponieważ [HttpGet] jest to ustawienie domyślne.

Atrybut ValidateAntiForgeryToken służy do zapobiegania fałszerzowaniu żądania i jest sparowany z tokenem antyugeryjnym wygenerowanym w pliku widoku edycji (Views/Movies/Edit.cshtml). Plik widoku edycji generuje token ochrony przed fałszerzami za pomocą pomocnika tagów formularzy.

<form asp-action="Edit">

Pomocnik tagów formularzy generuje ukryty token ochrony przed fałszerzowania, który musi być zgodny [ValidateAntiForgeryToken] z wygenerowanym tokenem ochrony przed fałszercją w Edit metodzie kontrolera Filmy. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Metoda HttpGet Edit pobiera parametr filmu ID , wyszukuje film przy użyciu metody Entity Framework FindAsync i zwraca wybrany film do widoku Edytuj. Jeśli nie można odnaleźć filmu, NotFound zostanie zwrócony (HTTP 404).

// 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 tworzenia szkieletów utworzył widok Edycji, przeanalizował klasę Movie i utworzył kod renderujący <label> i <input> elementy dla każdej właściwości klasy. W poniższym przykładzie pokazano widok Edycji wygenerowany przez system tworzenia szkieletów programu Visual Studio:

@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ę, że szablon widoku zawiera instrukcję @model MvcMovie.Models.Movie w górnej części pliku. @model MvcMovie.Models.Movie określa, że widok oczekuje, że model szablonu widoku będzie typu Movie.

Kod szkieletowy używa kilku metod pomocnika tagów w celu usprawnienia znaczników HTML. Pomocnik tagów etykiet wyświetla nazwę pola ("Title", "ReleaseDate", "Gatunek" lub "Price"). Pomocnik tagów wejściowych renderuje element HTML<input>. Pomocnik tagu weryfikacji wyświetla wszystkie komunikaty sprawdzania poprawności skojarzone z tą właściwością.

Uruchom aplikację i przejdź do /Movies adresu URL. Kliknij link Edytuj. W przeglądarce wyświetl źródło strony. Wygenerowany kod HTML dla <form> elementu jest pokazany poniżej.

<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 elemecieHTML <form>, którego action atrybut jest ustawiony na publikowanie w adresie /Movies/Edit/id URL. Dane formularza zostaną opublikowane na serwerze po kliknięciu Save przycisku. Ostatni wiersz przed elementem zamykającym </form> pokazuje ukryty token XSRF wygenerowany przez pomocnika tagów formularzy.

Przetwarzanie żądania POST

Na poniższej [HttpPost] liście przedstawiono wersję Edit metody akcji.

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

Atrybut [ValidateAntiForgeryToken] sprawdza poprawność ukrytego tokenu XSRF wygenerowanego przez generator tokenów chroniących przed fałszerzami w pomocniku tagów formularzy

System powiązania modelu przyjmuje opublikowane wartości formularza i tworzy Movie obiekt, który jest przekazywany jako movie parametr. Właściwość ModelState.IsValid sprawdza, czy dane przesłane w formularzu mogą służyć do modyfikowania (edytowania lub aktualizowania) Movie obiektu. Jeśli dane są prawidłowe, są zapisywane. Zaktualizowane (edytowane) dane filmu są zapisywane w bazie danych przez wywołanie SaveChangesAsync metody kontekstu bazy danych. Po zapisaniu danych kod przekierowuje użytkownika do Index metody MoviesController akcji klasy , która wyświetla kolekcję filmów, w tym wprowadzone zmiany.

Przed opublikowaniem formularza na serwerze walidacja po stronie klienta sprawdza wszystkie reguły walidacji w polach. Jeśli wystąpią jakiekolwiek błędy walidacji, zostanie wyświetlony komunikat o błędzie i formularz nie zostanie opublikowany. Jeśli język JavaScript jest wyłączony, nie będziesz mieć weryfikacji po stronie klienta, ale serwer wykryje nieprawidłowe wartości opublikowane, a wartości formularza będą odtwarzane ponownie z komunikatami o błędach. W dalszej części samouczka bardziej szczegółowo sprawdzimy weryfikację modelu. Pomocnik tagów weryfikacji w szablonie Views/Movies/Edit.cshtml widoku zajmuje się wyświetlaniem odpowiednich komunikatów o błędach.

Widok edycji: wyjątek nieprawidłowej wartości price abc wskazuje, że pole Cena musi być liczbą. Wyjątek dla nieprawidłowej wartości daty wydania stanów xyz Wprowadź prawidłową datę.

HttpGet Wszystkie metody w kontrolerze filmu są zgodne z podobnym wzorcem. Pobierają obiekt filmu (lub listę obiektów, w przypadku Index), i przekazują obiekt (model) do widoku. Metoda Create przekazuje pusty obiekt filmu do Create widoku. Wszystkie metody, które tworzą, edytują, usuwają lub w inny sposób modyfikują dane, robią to w [HttpPost] przeciążeniu metody . Modyfikowanie danych w metodzie HTTP GET jest zagrożeniem bezpieczeństwa. Modyfikowanie danych w metodzie HTTP GET narusza również najlepsze rozwiązania HTTP i wzorzec architektury REST , który określa, że żądania GET nie powinny zmieniać stanu aplikacji. Innymi słowy wykonanie operacji GET powinno być bezpieczną operacją, która nie ma skutków ubocznych i nie modyfikuje utrwalone dane.

Dodatkowe zasoby