Teil 6: Controllermethoden und Ansichten in ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Von Rick Anderson

Für den Anfang ist die Movie-App schon recht ansprechend, doch es gibt Raum für Verbesserungen. Zum Beispiel sollte ReleaseDate zwei Wörter sein.

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, damit die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt werden.

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 MVC Core-Hilfsprogramms für Ankertags 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>

Taghilfsprogramme 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 Zusätzliche Ressourcen.

Ö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 bereitgestellten Filmwerte 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 bietet eine alternative Methode, um zu viele Angaben 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-Anforderungen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Bearbeitungsmethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] der Standardwert ist.

Das ValidateAntiForgeryToken-Attribut wird verwendet, um die Fälschung einer Anforderung zu verhindern. Es wird einem Fälschungssicherheitstoken zugeordnet, das in der Datei für die Bearbeitungsansicht (Views/Movies/Edit.cshtml) generiert 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 verwendet den ID-Parameter eines Films, 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 Hilfsprogramm für Bezeichnungstags zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Das Hilfsprogramm für Validierungstags zeigt Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf einen 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 Modellbindungssystem verwendet die übermittelten Formularwerte und erstellt 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: Eine Ausnahme für einen falschen Wert für „Price“ von abc besagt, dass das Feld „Price“ eine Zahl sein muss. Eine Ausnahme für einen falschen Wert des Veröffentlichungsdatums von xyz besagt „Please enter a valid date“.

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 Movie-Objekt 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. Die Durchführung eines GET-Vorgangs sollte also eine sichere Operation sein, die keine Nebenwirkungen hat und die permanenten Daten nicht ändert.

Zusätzliche Ressourcen

Für den Anfang ist die Movie-App schon recht ansprechend, doch es gibt Raum für Verbesserungen. Zum Beispiel sollte ReleaseDate zwei Wörter sein.

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, damit die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt werden.

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 MVC Core-Hilfsprogramms für Ankertags 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>

Taghilfsprogramme 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 Zusätzliche Ressourcen.

Ö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 bereitgestellten Filmwerte 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 bietet eine alternative Methode, um zu viele Angaben 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-Anforderungen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Bearbeitungsmethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] der Standardwert ist.

Das ValidateAntiForgeryToken-Attribut wird verwendet, um die Fälschung einer Anforderung zu verhindern. Es wird einem Fälschungssicherheitstoken zugeordnet, das in der Datei für die Bearbeitungsansicht (Views/Movies/Edit.cshtml) generiert 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 verwendet den ID-Parameter eines Films, 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 Hilfsprogramm für Bezeichnungstags zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Das Hilfsprogramm für Validierungstags zeigt Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf einen 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 Modellbindungssystem verwendet die übermittelten Formularwerte und erstellt 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: Eine Ausnahme für einen falschen Wert für „Price“ von abc besagt, dass das Feld „Price“ eine Zahl sein muss. Eine Ausnahme für einen falschen Wert des Veröffentlichungsdatums von xyz besagt „Please enter a valid date“.

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 Movie-Objekt 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. Die Durchführung eines GET-Vorgangs sollte also eine sichere Operation sein, die keine Nebenwirkungen hat und die permanenten Daten nicht ändert.

Zusätzliche Ressourcen

Für den Anfang ist die Movie-App schon recht ansprechend, doch es gibt Raum für Verbesserungen. Zum Beispiel sollte ReleaseDate zwei Wörter sein.

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, damit die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt werden.

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 MVC Core-Hilfsprogramms für Ankertags 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>

Taghilfsprogramme 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 ein beliebtes Feature in ASP.NET Core. Weitere Informationen hierzu finden Sie unter Zusätzliche Ressourcen.

Ö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 bereitgestellten Filmwerte 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 bietet eine alternative Methode, um zu viele Angaben 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-Anforderungen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Bearbeitungsmethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] der Standardwert ist.

Das ValidateAntiForgeryToken-Attribut wird verwendet, um die Fälschung einer Anforderung zu verhindern. Es wird einem Fälschungssicherheitstoken zugeordnet, das in der Datei für die Bearbeitungsansicht (Views/Movies/Edit.cshtml) generiert 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 verwendet den ID-Parameter eines Films, 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 Hilfsprogramm für Bezeichnungstags zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Das Hilfsprogramm für Validierungstags zeigt Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf einen 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 Modellbindungssystem verwendet die übermittelten Formularwerte und erstellt 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: Eine Ausnahme für einen falschen Wert für „Price“ von abc besagt, dass das Feld „Price“ eine Zahl sein muss. Eine Ausnahme für einen falschen Wert des Veröffentlichungsdatums von xyz besagt „Please enter a valid date“.

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 Movie-Objekt 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. Die Durchführung eines GET-Vorgangs sollte also eine sichere Operation sein, die keine Nebenwirkungen hat und die permanenten Daten nicht ändert.

Zusätzliche Ressourcen

Für den Anfang ist die Movie-App schon recht ansprechend, doch es gibt Raum für Verbesserungen. Zum Beispiel sollte ReleaseDate zwei Wörter sein.

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 Datenanmerkungen. 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, damit die im Feld gespeicherten Informationen zur Uhrzeit nicht angezeigt werden.

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 MVC Core-Hilfsprogramms für Ankertags 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>

Taghilfsprogramme 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 Taghilfsprogrammen finden Sie unter Zusätzliche Ressourcen.

Ö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 bereitgestellten Filmwerte 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 bietet eine alternative Methode, um zu viele Angaben 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-Anforderungen aufgerufen werden kann. Sie könnten das [HttpGet]-Attribut auf die erste Bearbeitungsmethode anwenden, aber dies ist nicht erforderlich, da [HttpGet] der Standardwert ist.

Das ValidateAntiForgeryToken-Attribut wird verwendet, um die Fälschung einer Anforderung zu verhindern. Es wird einem Fälschungssicherheitstoken zugeordnet, das in der Datei für die Bearbeitungsansicht (Views/Movies/Edit.cshtml) generiert 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 verwendet den ID-Parameter eines Films, 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 Hilfsprogramm für Bezeichnungstags zeigt den Namen des Felds an („Title“, „ReleaseDate“, „Genre“ oder „Price“). Das Hilfsprogramm für Eingabetags rendert ein HTML-<input>-Element. Das Hilfsprogramm für Validierungstags zeigt Validierungsmeldungen an, die dieser Eigenschaft zugeordnet sind.

Führen Sie die Anwendung aus, und navigieren Sie zur /Movies-URL. Klicken Sie auf einen 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 Modellbindungssystem verwendet die übermittelten Formularwerte und erstellt 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: Eine Ausnahme für einen falschen Wert für „Price“ von abc besagt, dass das Feld „Price“ eine Zahl sein muss. Eine Ausnahme für einen falschen Wert des Veröffentlichungsdatums von xyz besagt „Please enter a valid date“.

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 Movie-Objekt 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. Die Durchführung eines GET-Vorgangs sollte also eine sichere Operation sein, die keine Nebenwirkungen hat und die permanenten Daten nicht ändert.

Zusätzliche Ressourcen