Teil 6: Controllermethoden und Ansichten in ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: Veröffentlichungsdatum ist ein Wort (kein Leerzeichen).

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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 werden im nächsten Tutorial erklärt. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten an (Date).

Führen Sie die Anwendung mithilfe der obigen Datenanmerkungen aus, und aktualisieren Sie die /Movies Seite. Da das Ansichtsmark-up die Methoden @Html.DisplayNameFor und @Html.DisplayFor verwendet, um den Eigenschaftennamen und -wert zu rendern, zeigt die aktualisierte Index-Ansicht alle Felder ordnungsgemäß formatiert an. Beispielsweise ist das Veröffentlichungsdatum jetzt zwei Wörter.

Indexansicht: Veröffentlichungsdatum ist zwei Wörter lang.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit der Maus über dem Bearbeiten-Link, und eine Link-URL wird im Fenster angezeigt.

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Program.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Taghilfsprogramme sind eines der am häufigsten verwendeten neuen Features in ASP.NET Core. Weitere Informationen finden Sie unter "Verwandte Inhalte".

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht: Eine Ausnahme für einen falschen Preiswert von abc gibt an, dass das Feld

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: „ReleaseDate“ ist ein Wort (ohne Leerzeichen), und bei jedem Veröffentlichungsdatum wird die Uhrzeit 12: 00 Uhr angezeigt

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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 werden im nächsten Tutorial erklärt. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten (Datum) an, deshalb werden die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt.

Führen Sie die Anwendung mithilfe der obigen Datenanmerkungen aus, und aktualisieren Sie die /Movies Seite. Da das Ansichtsmark-up die Methoden @Html.DisplayNameFor und @Html.DisplayFor verwendet, um den Eigenschaftennamen und -wert zu rendern, zeigt die aktualisierte Index-Ansicht alle Felder ordnungsgemäß formatiert an. Beispielsweise ist das Veröffentlichungsdatum jetzt zwei Wörter, und die Uhrzeitinformationen werden nicht mehr angezeigt.

Indexansicht: Veröffentlichungsdatum ist zwei Wörter, und jedes Filmveröffentlichungsdatum wird ohne die Uhrzeit angezeigt.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit Maus über dem Link „Bearbeiten“ und der angezeigten Link-URL von https://localhost:5001/Movies/Edit/5

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Program.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Taghilfsprogramme sind eines der am häufigsten verwendeten neuen Features in ASP.NET Core. Weitere Informationen finden Sie unter "Verwandte Inhalte".

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht bearbeiten: Bei einem falschen Wert für „Price“ von abc wird angezeigt, dass das Feld „Price“ eine Zahl sein muss. Bei einem falschen Wert des Veröffentlichungsdatums von xyz wird „Bitte geben Sie ein gültiges Datum ein“ angezeigt.

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: „ReleaseDate“ ist ein Wort (ohne Leerzeichen), und bei jedem Veröffentlichungsdatum wird die Uhrzeit 12: 00 Uhr angezeigt

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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 werden im nächsten Tutorial erklärt. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten (Datum) an, deshalb werden die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit Maus über dem Link „Bearbeiten“ und der angezeigten Link-URL von https://localhost:5001/Movies/Edit/5

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Program.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Taghilfsprogramme sind eines der am häufigsten verwendeten neuen Features in ASP.NET Core. Weitere Informationen finden Sie unter "Verwandte Inhalte".

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht bearbeiten: Bei einem falschen Wert für „Price“ von abc wird angezeigt, dass das Feld „Price“ eine Zahl sein muss. Bei einem falschen Wert des Veröffentlichungsdatums von xyz wird „Bitte geben Sie ein gültiges Datum ein“ angezeigt.

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: „ReleaseDate“ ist ein Wort (ohne Leerzeichen), und bei jedem Veröffentlichungsdatum wird die Uhrzeit 12: 00 Uhr angezeigt

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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 werden im nächsten Tutorial erklärt. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten (Datum) an, deshalb werden die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit Maus über dem Link „Bearbeiten“ und der angezeigten Link-URL von https://localhost:5001/Movies/Edit/5

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Program.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Taghilfsprogramme sind eines der am häufigsten verwendeten neuen Features in ASP.NET Core. Weitere Informationen finden Sie unter "Verwandte Inhalte".

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht bearbeiten: Bei einem falschen Wert für „Price“ von abc wird angezeigt, dass das Feld „Price“ eine Zahl sein muss. Bei einem falschen Wert des Veröffentlichungsdatums von xyz wird „Bitte geben Sie ein gültiges Datum ein“ angezeigt.

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: „ReleaseDate“ ist ein Wort (ohne Leerzeichen), und bei jedem Veröffentlichungsdatum wird die Uhrzeit 12: 00 Uhr angezeigt

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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 werden im nächsten Tutorial erklärt. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten (Datum) an, deshalb werden die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit Maus über dem Link „Bearbeiten“ und der angezeigten Link-URL von https://localhost:5001/Movies/Edit/5

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Program.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Tag Helpers sind ein beliebtes Feature in ASP.NET Core. Weitere Informationen zu ihnen finden Sie unter Verwandte Inhalte.

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht bearbeiten: Bei einem falschen Wert für „Price“ von abc wird angezeigt, dass das Feld „Price“ eine Zahl sein muss. Bei einem falschen Wert des Veröffentlichungsdatums von xyz wird „Bitte geben Sie ein gültiges Datum ein“ angezeigt.

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.

Wir haben einen guten Anfang für die Movie-App, aber die Präsentation ist nicht ideal, zum Beispiel sollte ReleaseDate aus zwei Wörtern bestehen.

Indexansicht: „ReleaseDate“ ist ein Wort (ohne Leerzeichen), und bei jedem Veröffentlichungsdatum wird die Uhrzeit 12: 00 Uhr angezeigt

Öffnen Sie die Datei Models/Movie.cs, und fügen Sie die nachfolgend gezeigten markierten Zeilen hinzu:

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

Im nächsten Tutorial behandeln wir DataAnnotations. Das Display-Attribut gibt an, was für den Namen eines Felds angezeigt werden soll (in diesem Fall „Release Date“ anstatt „ReleaseDate“). Das DataType-Attribut gibt den Typ der Daten (Datum) an, deshalb werden die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt.

Die Datenanmerkung [Column(TypeName = "decimal(18, 2)")] ist erforderlich, damit Entity Framework Core Price ordnungsgemäß einer Währung in der Datenbank zuordnen kann. Weitere Informationen finden Sie unter Datentypen.

Navigieren Sie zum Movies-Controller, und halten Sie den Mauszeiger über einen Bearbeiten-Link, um die Ziel-URL zu sehen.

Browserfenster mit Maus über dem Link „Bearbeiten“ und der angezeigten Link-URL von https://localhost:5001/Movies/Edit/5

Die Links Bearbeiten, Details und Löschen werden mithilfe des Core MVC Anchor Tag Helpers in der Datei Views/Movies/Index.cshtml generiert.

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

Tag Helpers ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien. Im obigen Code generiert AnchorTagHelper dynamisch den HTML-href-Attributwert aus der Controlleraktionsmethode und der Routen-ID. Verwenden Sie in Ihrem bevorzugten Browser Quelltext anzeigen oder die Entwicklertools, um das generierte Markup zu untersuchen. Ein Teil des generierten HTML-Codes wird unten gezeigt:

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

Rufen Sie sich das Format für das in der Datei Startup.cs festgelegte Routing in Erinnerung:

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

ASP.NET Core übersetzt https://localhost:5001/Movies/Edit/4 in eine Anforderung an die Edit-Aktionsmethode des Movies-Controllers mit dem Id-Parameter 4. (Controllermethoden werden auch als Aktionsmethoden bezeichnet.)

Weitere Informationen zu Tag Helpers finden Sie unter Verwandte Inhalte.

Öffnen Sie den Movies-Controller, und untersuchen Sie die beiden Edit-Aktionsmethoden. Der folgende Code zeigt die HTTP GET Edit-Methode, die den Film abruft und das Bearbeitungsformular ausfüllt, das von der Edit.cshtmlRazor-Datei generiert wurde.

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

Der folgende Code zeigt die HTTP POST Edit-Methode, die die übermittelten Filmdaten verarbeitet:

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

Das [Bind]-Attribut ist eine Möglichkeit zum Schutz vor zu vielen Angaben. Sie sollten nur Eigenschaften in das [Bind]-Attribut aufnehmen, die Sie ändern möchten. Weitere Informationen finden Sie unter Protect your controller from over-posting (Schützen Ihres Controllers vor zu vielen Angaben). ViewModels bieten einen alternativen Ansatz, um Over-Posting zu verhindern.

Beachten Sie, dass der zweiten Edit-Aktionsmethode das [HttpPost]-Attribut vorangestellt ist.

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

Das HttpPost-Attribut gibt an, dass diese Edit-Methode nur für POST-Anfragen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Editiermethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] die Standardeinstellung ist.

Das Attribut ValidateAntiForgeryToken wird verwendet, um die Fälschung einer Anfrage zu verhindern, und wird mit einem Antiforgery-Token gepaart, das in der Datei der Bearbeitungsansicht (Views/Movies/Edit.cshtml) erzeugt wird. Die Datei für die Bearbeitungsansicht generiert das Fälschungssicherheitstoken mit dem Hilfsprogramm für Formulartags.

<form asp-action="Edit">

Das Hilfsprogramm für Formulartags generiert ein ausgeblendetes Fälschungssicherheitstoken, das mit dem von [ValidateAntiForgeryToken] generierten Fälschungssicherheitstoken in der Edit-Methode des Movies-Controllers übereinstimmen muss. Weitere Informationen finden Sie unter Prevent Cross-Site Request Forgery (XSRF/CSRF) Attacks in ASP.NET Core (Verhindern von websiteübergreifenden Anforderungsfälschungen (XSRF/CSRF) in ASP.NET Core).

Die HttpGet Edit-Methode akzeptiert den Filmparameter ID, sucht den Film mit der FindAsync-Methode von Entity Framework und gibt den ausgewählten Film an die Bearbeitungsansicht zurück. Wenn ein Film nicht gefunden werden kann, wird NotFound (HTTP 404) zurückgegeben.

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

Als das Gerüstsystem die Bearbeitungsansicht erstellt hat, wurde die Movie-Klasse überprüft und Code zum Rendern der <label>- und <input>-Elemente für jede Eigenschaft der Klasse erstellt. Das folgende Beispiel zeigt die Bearbeitungsansicht, die vom Visual Studio-Gerüstsystem generiert wurde:

@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");}
}

Beachten Sie, dass die Ansichtsvorlage eine @model MvcMovie.Models.Movie-Anweisung am Anfang der Datei aufweist. @model MvcMovie.Models.Movie gibt an, dass die Ansicht erwartet, dass das Modell für die Ansichtsvorlage den Typ Movie hat.

Der Gerüstcode verwendet mehrere Methoden von Taghilfsprogrammen, um das HTML-Markup zu optimieren. Das Label-Tag-Helper zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Der Validation-Tag-Helper zeigt alle Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf den Link Bearbeiten. Zeigen Sie im Browser den Quelltext für die Seite an. Der generierte HTML-Code für das <form>-Element wird unten dargestellt.

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

Die <input>-Elemente sind in einem HTML <form>-Element enthalten, dessen action-Attribut so festgelegt ist, dass Beiträge an die URL /Movies/Edit/id gesendet werden. Die Formulardaten werden an den Server gesendet, wenn auf die Schaltfläche Save geklickt wird. Die letzte Zeile vor dem schließenden </form>-Element zeigt das ausgeblendete XSRF-Token, das durch das Hilfsprogramm für Formulartags generiert wurde.

Verarbeiten der POST-Anforderung

Die folgende Liste zeigt die [HttpPost]-Version der Edit-Aktionsmethode.

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

Das [ValidateAntiForgeryToken]-Attribut überprüft das ausgeblendete XSRF-Token, das vom Generator für Fälschungssicherheitstoken im Hilfsprogramm für Formulartags generiert wurde.

Das Model-Binding-System nimmt die übermittelten Formularwerte entgegen und erstellt daraus ein Movie-Objekt, das als movie-Parameter übergeben wird. Die ModelState.IsValid-Eigenschaft stellt sicher, dass die im Formular übermittelten Daten verwendet werden können, um ein Movie-Objekt zu ändern (zu bearbeiten oder zu aktualisieren). Wenn die Daten gültig sind, werden sie gespeichert. Die aktualisierten (bearbeiteten) Filmdaten werden in der Datenbank gespeichert, indem die SaveChangesAsync-Methode des Datenbankkontexts aufgerufen wird. Nach dem Speichern der Daten leitet der Code den Benutzer an die Index-Aktionsmethode der MoviesController-Klasse weiter, die die Filmsammlung einschließlich der gerade vorgenommenen Änderungen anzeigt.

Bevor das Formular an den Server gesendet wird, überprüft die clientseitige Validierung alle Validierungsregeln der Felder. Sind Validierungsfehler vorhanden, wird eine Fehlermeldung angezeigt, und das Formular wird nicht gesendet. Wenn JavaScript deaktiviert ist, erfolgt keine clientseitige Validierung. Der Server erkennt jedoch, dass die bereitgestellten Werte nicht gültig sind, und die Formularwerte werden wieder mit Fehlermeldungen angezeigt. Später in diesem Tutorial untersuchen wir die Modellvalidierung im Detail. Das Hilfsprogramm für Validierungstags in der Ansichtsvorlage Views/Movies/Edit.cshtml übernimmt die Anzeige der entsprechenden Fehlermeldungen.

Bearbeitungsansicht bearbeiten: Bei einem falschen Wert für „Price“ von abc wird angezeigt, dass das Feld „Price“ eine Zahl sein muss. Bei einem falschen Wert des Veröffentlichungsdatums von xyz wird „Bitte geben Sie ein gültiges Datum ein“ angezeigt.

Alle HttpGet-Methoden im Movie-Controller folgen einem ähnlichen Muster. Sie erhalten ein Movie-Objekt (oder eine Liste von Objekten bei Index), und übergeben das Objekt (Modell) an die Ansicht. Die Create-Methode übergibt ein leeres Filmobjekt an die Ansicht Create. Alle Methoden, die Daten erstellen, bearbeiten, löschen oder in beliebiger Weise ändern, nutzen dazu die [HttpPost]-Überladung der Methode. Das Ändern von Daten in einer HTTP GET-Methode ist ein Sicherheitsrisiko. Das Ändern von Daten in einer HTTP GET-Methode verstößt auch gegen bewährte HTTP-Methoden und das architektonische REST-Muster, das angibt, dass die GET-Anforderungen nicht den Status Ihrer Anwendung ändern dürfen. Mit anderen Worten: Die Durchführung eines GET-Vorgangs sollte ein sicherer Vorgang sein, der keine Nebenwirkungen hat und Ihre persistent gespeicherten Daten nicht verändert.