Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Von Rick Anderson, Dave Brock und Kirk Larkin
Note
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Warning
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie unter .NET und .NET Core Support Policy. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.
Razor Pages kann im Vergleich zu Controllern und Ansichten das Programmieren seitenbasierter Anwendungen vereinfachen und die Produktivität erhöhen.
Wenn Sie nach einem Tutorial suchen, das den Ansatz "Model-View-Controller" verwendet, lesen Sie Erste Schritte mit ASP.NET Core MVC.
In diesem Artikel werden die Architektur, Konzepte und Muster behandelt, die Razor Pages effektiv für die Erstellung von seitenorientierten Webanwendungen machen. Es wird erläutert, wie Razor Seiten funktionieren, ihre wichtigsten Komponenten und bewährten Methoden für die Implementierung. Wenn Sie praktisches Lernen mit schrittweisen Anleitungen bevorzugen, lesen Sie Tutorial: Erstellen einer Razor Pages-Web-App mit ASP.NET Core. Eine Übersicht über ASP.NET Core finden Sie unter Introduction to ASP.NET Core.
Prerequisites
- Visual Studio 2022 mit dem ASP.NET und Webentwicklung-Workload.
- .NET 6 SDK
Erstellen eines Razor Pages-Projekts
Detaillierte Anweisungen zum Erstellen eines Pages-Projekts finden Sie unter "Erste Schritte mit Pages."
Razor Seiten
Razor Pages ist in Program.cs aktiviert:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Im vorhergehenden Code:
- AddRazorPages fügt Dienste für Razor Pages zur App hinzu.
- MapRazorPages fügt Endpunkte für Razor Seiten zu IEndpointRouteBuilder hinzu.
Sehen Sie sich diese einfache Seite an:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
Der vorangehende Code sieht ähnlich wie eine Razor Ansichtsdatei aus, die in einer ASP.NET Core App mit Controllern und Ansichten verwendet wird. Der Unterschied besteht in der @page-Anweisung.
@page macht die Datei zu einer MVC-Aktion, d. h. dass Anforderungen direkt ohne Durchlaufen eines Controllers verarbeitet werden.
@page muss die erste Razor-Anweisung auf einer Seite sein.
@page wirkt sich auf das Verhalten aller anderen Razor-Konstrukte aus.
Razor Pages-Dateinamen haben das Suffix .cshtml.
Eine ähnliche Seite, die die PageModel-Klasse verwendet, wird in den folgenden zwei Dateien angezeigt. Die datei Pages/Index2.cshtml:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Das Pages/Index2.cshtml.cs Seitenmodell:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Standardmäßig hat die PageModel-Klassendatei denselben Namen wie die Razor-Seiten-Datei, wobei .cs angehängt wird. Die vorherige Razor Seite ist Pages/Index2.cshtml. Die Datei mit der PageModel-Klasse heißt Pages/Index2.cshtml.cs.
Die Zuordnungen von URL-Pfaden zu Seiten werden durch den Speicherort der Seite im Dateisystem bestimmt. Die folgende Tabelle zeigt einen Razor Seitenpfad und die entsprechende URL:
| Dateiname und Pfad | übereinstimmende URL |
|---|---|
/Pages/Index.cshtml |
/ oder /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store oder /Store/Index |
Notes:
- Die Laufzeitumgebung sucht standardmäßig im Ordner Pages nach Razor Pages-Dateien.
- Wenn eine Seite nicht in einer URL enthalten ist, ist
Indexdie Standardseite.
Schreiben eines einfachen Formulars
Razor Pages ist darauf ausgelegt, allgemeine Muster, die mit Webbrowsern verwendet werden können, beim Erstellen einer App leichter implementieren zu können. Die Modellbindung, Taghilfsprogramme und HTML-Hilfsprogramme funktionieren mit den Eigenschaften, die in einer Razor Page-Klasse definiert sind. Nehmen wir z.B. eine Seite, die ein allgemeines Kontaktformular für das Contact-Modell implementiert:
Für die Beispiele in diesem Dokument wird die DbContext in der Datei Program.cs initialisiert.
Für die In-Memory-Datenbank ist das NuGet-Paket Microsoft.EntityFrameworkCore.InMemory erforderlich.
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Das Datenmodell:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
Der db-Kontext:
using Microsoft.EntityFrameworkCore;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext (DbContextOptions<CustomerDbContext> options)
: base(options)
{
}
public DbSet<RazorPagesContacts.Models.Customer> Customer => Set<RazorPagesContacts.Models.Customer>();
}
}
Die Ansichtsdatei Pages/Customers/Create.cshtml:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Das Pages/Customers/Create.cshtml.cs Seitenmodell:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Die PageModel -Klasse heißt standardmäßig <PageName>Model und befindet sich im selben Namespace wie die Seite.
Mit der Klasse PageModel kann die Logik einer Seite von deren Darstellung getrennt werden. Sie definiert Seitenhandler für Anforderungen, die an die Seite geschickt wurden, und für zum Rendern der Seite verwendete Daten. Diese Trennung ermöglicht Folgendes:
- Verwalten von Seitenabhängigkeiten mithilfe von Dependency Injection
- Einheitstests
Die Seite verfügt über eine OnPostAsync-Handlermethode, die bei POST-Anforderungen ausgeführt wird (wenn ein Benutzer das Formular sendet). Für alle HTTP-Verben können Handlermethoden hinzugefügt werden. Die am häufigsten verwendeten Handler sind:
-
OnGet, um den für eine Seite erforderlichen Status zu initialisieren. Im vorangehenden Code wird dieOnGetCreate.cshtml-Seite durch die Razor-Methode dargestellt. -
OnPost, um Formular-Einsendungen zu behandeln.
Das Namenssuffix Async ist optional. Es wird jedoch standardmäßig häufig für asynchrone Funktionen verwendet. Der vorhergehende Code ist typisch für Razor Pages.
Wenn Sie mit ASP.NET-Apps mit Controllern und Ansichten vertraut sind:
- Der
OnPostAsync-Code im vorangehenden Beispiel ähnelt dem typischen Controllercode. - Die meisten primitiven MVC-Typen wie solche für Modellbindungen, Validierungen und Aktionsergebnisse werden in Controllern und Razor Pages auf dieselbe Weise eingesetzt.
Die vorherige OnPostAsync-Methode:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Der grundlegende Ablauf von OnPostAsync:
Überprüfen Sie auf Validierungsfehler
- Wenn keine Fehler vorliegen, werden die Daten gespeichert und weitergeleitet.
- Wenn es Fehler gibt, zeigen Sie die Seite erneut mit den Validierungsmeldungen an. denn Validierungsfehler werden oftmals auf dem Client erkannt und nie an den Server übermittelt.
Die Ansichtsdatei Pages/Customers/Create.cshtml:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Der von Pages/Customers/Create.cshtml gerenderte HTML-Code sieht wie folgt aus:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
Im vorherigen Code gilt für die Formularübermittlung mittels POST Folgendes:
Bei gültigen Daten:
Die
OnPostAsync-Handlermethode ruft die RedirectToPage-Hilfsmethode auf. Eine Instanz vonRedirectToPagewird von RedirectToPageResult zurückgegeben.RedirectToPage:- Ist ein Aktionsergebnis.
- ähnelt
RedirectToActionoderRedirectToRoute(wird in Controllern und Ansichten verwendet). - Ist für Seiten angepasst. Im vorhergehenden Beispiel leitet es an die Stammindexseite (
/Index) weiter. Informationen zuRedirectToPagefinden Sie im Abschnitt URL-Generierung für Seiten.
Bei Validierungsfehlern, die an den Server übermittelt werden:
- Die
OnPostAsync-Handlermethode ruft die Page-Hilfsmethode auf. Eine Instanz vonPagewird von PageResult zurückgegeben. Der Vorgang, bei demPagezurückgegeben wird, ähnelt dem Vorgang, bei dem Aktionen im ControllerViewzurückgeben.PageResultist der Standardrückgabetyp für eine Handlermethode. Eine Handlermethode, dievoidzurückgibt, rendert die Seite. - Im vorangehenden Beispiel gibt ModelState.IsValid „false“ zurück, wenn das Formular via POST übermittelt wird und dabei kein Wert angegeben ist. In diesem Beispiel werden keine Validierungsfehler auf dem Client angezeigt. Die Verarbeitung von Validierungsfehlern wird weiter unten in diesem Artikel behandelt.
[BindProperty] public Customer? Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } if (Customer != null) _context.Customer.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }- Die
Bei Validierungsfehlern, die durch eine clientseitige Validierung erkannt werden:
- Die Daten werden nicht an den Server gesendet.
- Die clientseitige Validierung wird weiter unten in diesem Artikel erläutert.
Die Eigenschaft Customer verwendet das [BindProperty]-Attribut, um die Modellbindung zu aktivieren:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
[BindProperty] sollte nicht in Modellen mit Eigenschaften verwendet werden, die vom Client nicht geändert werden dürfen. Weitere Informationen finden Sie unter Overposting.
Razor Pages binden Eigenschaften standardmäßig nur an Nicht-GET-Verben. Durch die Bindung an Eigenschaften entfällt das Schreiben von Code, mit dem HTTP-Daten in den Modelltyp konvertiert werden. Die Bindung reduziert den Code, indem sie die gleiche Eigenschaft verwendet, um Formularfelder zu rendern und die Eingabe zu akzeptieren.
Warning
Aus Sicherheitsgründen müssen Sie Daten von GET-Anforderungen in die Seitenmodelleigenschaften einbinden. Überprüfen Sie die Benutzereingaben, bevor Sie sie den Eigenschaften zuordnen. Die Verwendung der GET-Bindung ist von Vorteil, wenn Sie Szenarios behandeln, die von Abfragezeichenfolgen oder Routenwerten abhängig sind.
Legen Sie die GET-Eigenschaft des [BindProperty]-Attributs auf SupportsGet fest, um eine Eigenschaft an true-Anforderungen zu binden:
[BindProperty(SupportsGet = true)]
Weitere Informationen finden Sie in der ASP.NET Core Community-Standup: Diskussion über Bindungen bei GET (YouTube).
Sehen Sie sich die Ansichtsdatei Pages/Customers/Create.cshtml an:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
- Im vorangehenden Code bindet das Eingabetag-Hilfsprogramm
<input asp-for="Customer.Name" />das HTML-Element<input>an den ModellausdruckCustomer.Name. -
@addTagHelperstellt Tag-Hilfsprogramme zur Verfügung.
Die Startseite
Index.cshtml ist die Startseite:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@if (Model.Customers != null)
{
foreach (var contact in Model.Customers)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
Die zugeordnete PageModel-Klasse (Index.cshtml.cs):
public class IndexModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public IndexModel(Data.CustomerDbContext context)
{
_context = context;
}
public IList<Customer>? Customers { get; set; }
public async Task OnGetAsync()
{
Customers = await _context.Customer.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
Die Datei Index.cshtml enthält das folgende Markup:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
Das <a /a>Anchor Tag Helper verwendet das asp-route-{value}-Attribut, um einen Link zur Bearbeiten-Seite zu generieren. Der Link enthält die Routendaten mit der Kontakt-ID. Beispiel: https://localhost:5001/Edit/1.
Taghilfsprogramme ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien.
Die Datei Index.cshtml enthält das Markup zum Erstellen der Schaltfläche „delete“ (Löschen) für jeden Kundenkontakt:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
Der gerenderte HTML-Code sieht wie folgt aus:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Wenn die „delete“-Schaltfläche in HTML gerendert wird, enthält das zugehörige formaction-Element Parameter für Folgendes:
- die Kundenkontakt-ID, die durch das
asp-route-id-Attribut angegeben wird - den
handler, der durch dasasp-page-handler-Attribut angegeben wird
Wenn die Schaltfläche ausgewählt wird, wird eine POST-Anforderung an den Server gesendet. Durch Konvention wird der Name der Handlermethode auf Grundlage des Werts des handler-Parameters gemäß dem Schema OnPost[handler]Async ausgewählt.
Da der handler in diesem Beispiel delete ist, wird die Handlermethode OnPostDeleteAsync verwendet, um die POST-Anforderung zu verarbeiten. Wenn asp-page-handler auf einen anderen Wert (z. B. remove) festgelegt wird, wird eine Handlermethode namens OnPostRemoveAsync ausgewählt.
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
Die OnPostDeleteAsync-Methode:
- Holt
idaus der Query-String. - Fragt mit
FindAsyncdie Datenbank nach dem Kundenkontakt ab. - Wenn der Kundenkontakt gefunden wird, wird er entfernt, und die Datenbank wird aktualisiert.
- Ruft RedirectToPage auf, um zur Stammindexseite (
/Index) umzuleiten.
Die Datei „Edit.cshtml“
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Customer</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer!.Id" />
<div class="form-group">
<label asp-for="Customer!.Name" class="control-label"></label>
<input asp-for="Customer!.Name" class="form-control" />
<span asp-validation-for="Customer!.Name" 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-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Die erste Zeile enthält die @page "{id:int}"-Anweisung. Die Routingbeschränkung "{id:int}" weist die Seite an, Anfragen an die Seite zu akzeptieren, die int-Routendaten enthalten. Wenn eine Anforderung an die Seite bestimmte Routingdaten nicht enthält, die in einen int konvertiert werden können, gibt die Runtime einen Fehler vom Typ „HTTP 404: Nicht gefunden“ zurück. Um die ID optional zu machen, fügen Sie ? an die Routeneinschränkung an:
@page "{id:int?}"
Die datei Edit.cshtml.cs:
public class EditModel : PageModel
{
private readonly RazorPagesContacts.Data.CustomerDbContext _context;
public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
if (Customer == null)
{
return NotFound();
}
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null)
{
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.Id))
{
return NotFound();
}
else
{
throw;
}
}
}
return RedirectToPage("./Index");
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.Id == id);
}
}
Validation
Gültigkeitsprüfungsregeln:
- In der Modellklasse werden sie deklarativ angegeben.
- Sie werden überall in der App erzwungen.
Der Namespace System.ComponentModel.DataAnnotations stellt eine Gruppe integrierter Validierungsattribute bereit, die deklarativ auf eine Klasse oder Eigenschaft angewendet werden. „DataAnnotations“ enthält auch Formatierungsattribute wie [DataType], die bei der Formatierung helfen und keinerlei Validierung bereitstellen.
Sehen Sie sich das Customer-Modell an:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
Mit der folgenden Ansichtsdatei Create.cshtml:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Der vorangehende Code:
umfasst jQuery-Skripts einschließlich solcher zur Validierung.
Verwendet die
<div />- und<span />-Tag-Helfer, um Folgendes zu ermöglichen:- Clientseitige Überprüfung.
- Darstellung von Validierungsfehlern.
wird der folgende HTML-Code generiert:
<p>Enter a customer name:</p> <form method="post"> Name: <input type="text" data-val="true" data-val-length="The field Name must be a string with a maximum length of 10." data-val-length-max="10" data-val-required="The Name field is required." id="Customer_Name" maxlength="10" name="Customer.Name" value="" /> <input type="submit" /> <input name="__RequestVerificationToken" type="hidden" value="<Antiforgery token here>" /> </form> <script src="/lib/jquery/dist/jquery.js"></script> <script src="/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Wenn Sie das Formular „Erstellen“ ohne einen Wert für den Namen übermitteln, wird die Fehlermeldung „Das Namensfeld ist erforderlich“ auf dem Formular angezeigt. Wenn JavaScript auf dem Client aktiviert ist, zeigt der Browser den Fehler an, ohne dass Daten per POST an den Server gesendet werden.
Das [StringLength(10)]-Attribut generiert data-val-length-max="10" für den gerenderten HTML-Code.
data-val-length-max verhindert, dass im Browser ein Wert eingegeben wird, der die angegebene Maximallänge überschreitet. Wenn ein Tool wie Fiddler zum Bearbeiten und erneuten Senden eines Posts verwendet wird
- Mit einem Namen, der länger als 10 Zeichen ist.
- geschieht Folgendes: Die Fehlermeldung „The field Name must be a string with a maximum length of 10“ (Das Namensfeld muss eine Zeichenfolge mit maximal 10 Zeichen enthalten) wird zurückgegeben.
Sehen Sie sich das folgende Movie-Modell an:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Die Validierungsattribute geben das Verhalten an, das auf die Modelleigenschaften angewendet werden soll:
Die Attribute
RequiredundMinimumLengthgeben an, dass eine Eigenschaft einen Wert haben muss. Ein Benutzer kann allerdings ein Leerzeichen eingeben, um diese Anforderung zu erfüllen.Das Attribut
RegularExpressionwird verwendet, um einzuschränken, welche Zeichen eingegeben werden dürfen. Im vorhergehenden Code: "Genre":- Es dürfen nur Buchstaben enthalten sein.
- Der erste Buchstabe muss ein Großbuchstabe sein. Leerzeichen, Zahlen und Sonderzeichen sind nicht zulässig.
Das
RegularExpression-„Rating“ (Bewertung):- Das erste Zeichen muss ein Großbuchstabe sein.
- Erlaubt Sonderzeichen und Zahlen in nachfolgenden Leerzeichen. „PG-13“ ist als Bewertung („Rating“) gültig, nicht jedoch als „Genre“.
Das Attribut
Rangeschränkt einen Wert auf einen bestimmten Bereich ein.Mit dem Attribut
StringLengthkann die maximale Länge einer Zeichenfolgeneigenschaft und optional die minimale Länge festlegt werden.Werttypen (wie
decimal,int,float,DateTime) sind grundsätzlich erforderlich und benötigen nicht das Attribut[Required].
Auf der Seite „Erstellen“ für das Movie-Modell werden Anzeigeprobleme mit ungültigen Werten gezeigt.
Weitere Informationen finden Sie unter:
CSS-Isolation
Isolieren Sie CSS-Formatvorlagen auf einzelne Seiten, Ansichten und Komponenten, um Reduzierung oder Vermeidung zu erzielen.
- Abhängigkeiten von globalen Stilen, die eine Herausforderung darstellen können.
- Formatkonflikte in geschachtelten Inhalten
Um eine bereichsbezogene CSS-Datei für eine Seite oder Ansicht hinzuzufügen, platzieren Sie die CSS-Stile in der Begleitdatei .cshtml.css, die dem Namen der .cshtml-Datei entspricht. Im folgenden Beispiel liefert eine Index.cshtml.css-Datei CSS-Stile, die nur auf die Index.cshtml-Seite oder -Ansicht angewendet werden.
Pages/Index.cshtml.css (Razor-Seiten) oder Views/Index.cshtml.css (MVC):
h1 {
color: red;
}
Die CSS-Isolation erfolgt zum Zeitpunkt der Erstellung. Das Framework schreibt CSS-Selektoren so um, dass sie mit Markup übereinstimmen, das von den Seiten oder Ansichten der App gerendert wird. Die umgeschriebenen CSS-Stile werden gebündelt und als statisches Objekt ({APP ASSEMBLY}.styles.css) erzeugt. Der Platzhalter {APP ASSEMBLY} ist der Assembly-Name des Projekts. Ein Link zu den gebündelten CSS-Stilen wird im Layout der App platziert.
Im <head> Inhalt der App Pages/Shared/_Layout.cshtml (Razor Pages) oder Views/Shared/_Layout.cshtml (MVC) fügen Sie den Link zu den gebündelten CSS-Styles hinzu oder überprüfen Sie die Anwesenheit des Links.
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
Im folgenden Beispiel lautet der Assemblyname der App WebApp:
<link rel="stylesheet" href="WebApp.styles.css" />
Die in einer bereichsbezogenen CSS-Datei definierten Stile werden nur auf die gerenderte Ausgabe der entsprechenden Datei angewendet. Im vorherigen Beispiel stehen alle CSS-Deklarationen von h1, die an anderer Stelle in der App definiert sind, nicht in Konflikt mit dem Überschriftenstil von Index. Kaskadierende CSS- und Vererbungsregeln bleiben für CSS-Dateien mit Bereichsbezug gültig. Beispielsweise überschreiben Stile, die direkt auf ein <h1>-Element in der Index.cshtml-Datei angewendet werden, die Stile der bereichsbezogenen CSS-Datei in Index.cshtml.css.
Note
Um die Isolation des CSS-Stils beim Bündeln zu gewährleisten, wird der Import von CSS in Razor-Codeblöcken nicht unterstützt.
CSS-Isolation gilt nur für HTML-Elemente. CSS-Isolation wird für Taghilfsprogramme nicht unterstützt.
Innerhalb der gebündelten CSS-Datei ist jede Seite, Ansicht oder Razor-Komponente mit einem Bereichsbezeichner im Format b-{STRING} verknüpft, wobei der Platzhalter {STRING} eine zehnstellige Zeichenfolge ist, die vom Framework generiert wird. Das folgende Beispiel liefert den Stil für das vorherige <h1>-Element der Seite Index einer Razor Pages-App:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
Auf der Seite Index, auf der der CSS-Stil aus der gebündelten Datei angewendet wird, wird der Bereichsbezeichner als HTML-Attribut angefügt:
<h1 b-3xxtam6d07>
Der Bezeichner ist für eine App eindeutig. Zur Build-Zeit wird ein Projekt-Bundle mit der Namenskonvention {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css erstellt, wobei der Platzhalter {STATIC WEB ASSETS BASE PATH} der Basispfad für statische Webressourcen ist.
Wenn andere Projekte verwendet werden, z. B. NuGet-Pakete oder Razor-Klassenbibliotheken, gilt für die gebündelte Datei Folgendes:
- Verweise auf Stile erfolgen mithilfe von CSS-Importen.
- Wird nicht als statisches Web-Asset der App bereitgestellt, die die Stile nutzt.
Unterstützung für CSS-Präprozessoren
CSS-Präprozessoren tragen zur Verbesserung der CSS-Entwicklung bei, indem sie Features wie Variablen, Schachtelung, Module, Mischung und Vererbung nutzen. CSS-Isolation unterstützt CSS-Präprozessoren wie Sass oder Less zwar nicht nativ, dennoch können CSS-Präprozessoren nahtlos integriert werden, sofern die Kompilierung des Präprozessors erfolgt, bevor das Framework die CSS-Selektoren während des Buildprozesses umschreibt. Konfigurieren Sie die vorhandene Präprozessorkompilierung als Before Build-Aufgabe im Visual Studio Task Runner Explorer, indem Sie Visual Studio als Beispiel verwenden.
Viele NuGet-Pakete von Drittanbietern wie z. B. AspNetCore.SassCompiler, können die SASS-/SCSS-Dateien am Anfang des Buildprozesses – noch vor der CSS-Isolation – kompilieren, ohne dass eine Konfiguration erforderlich ist.
Konfiguration der CSS-Isolation
CSS-Isolation ermöglicht die Konfiguration einiger fortgeschrittener Szenarien, z. B. wenn Abhängigkeiten von vorhandenen Tools oder Workflows bestehen.
Anpassen des Formats von Bereichsbezeichnern
In diesem Abschnitt ist der Platzhalter {Pages|Views} entweder Pages für Razor Pages-Apps oder Views für MVC-Apps.
Standardmäßig verwenden Bereichsbezeichner das Format b-{STRING}, wobei der Platzhalter {STRING} eine vom Framework generierte Zeichenfolge mit zehn Zeichen ist. Um das Bereichsbezeichnerformat anzupassen, aktualisieren Sie die project Datei auf ein gewünschtes Muster:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Im vorherigen Beispiel ändert der für Index.cshtml.css generierte CSS-Code seinen Bereichsbezeichner von b-{STRING} in custom-scope-identifier.
Verwenden Sie Bereichsbezeichner, um eine Vererbung mit bereichsbezogenen CSS-Dateien zu erzielen. Im folgenden Projektdateibeispiel enthält eine Datei BaseView.cshtml.css allgemeine Formatvorlagen übergreifend für Ansichten. Eine DerivedView.cshtml.css-Datei erbt diese Stile.
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Verwenden Sie den Wildcard-Operator (*), um Gültigkeitsbereich-Identifikatoren über mehrere Dateien zu teilen:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Ändern des Basispfads für statische Webressourcen
Die bereichsbezogene CSS-Datei wird im Stammverzeichnis der App generiert. Verwenden Sie in der project datei die Eigenschaft StaticWebAssetBasePath, um den Standardpfad zu ändern. Im folgenden Beispiel wird die bereichsbezogene CSS-Datei und die restlichen Ressourcen der App am _content Pfad platziert:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Deaktivieren der automatischen Bündelung
Wenn Sie das Verhalten des Frameworks beim Veröffentlichen und Laden bereichsbezogener Dateien zur Laufzeit ändern möchten, verwenden Sie die DisableScopedCssBundling-Eigenschaft. Die Verwendung dieser Eigenschaft bedeutet, dass die isolierten CSS-Dateien durch andere Tools oder Prozesse aus dem Verzeichnis obj übernommen und zur Laufzeit veröffentlicht und geladen werden:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Unterstützung für Razor-Klassenbibliothek (RCL)
Wenn eine Razor-Klassenbibliothek (RCL) isolierte Stile bereitstellt, zeigt das <link>-Attribut des href-Tags auf {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, wobei die Platzhalter für Folgendes stehen:
-
{STATIC WEB ASSET BASE PATH}: Basispfad der statischen Webressource -
{PACKAGE ID}: Der Paketbezeichner der Bibliothek. Der Paketbezeichner wird standardmäßig auf den Assemblynamen des Projekts festgelegt, wenn der Paketbezeichner in der Projektdatei nicht angegeben ist.
Im folgenden Beispiel:
- Der Basispfad der statischen Webressource lautet
_content/ClassLib. - Der Assemblyname der Klassenbibliothek lautet
ClassLib.
Pages/Shared/_Layout.cshtml (Razor-Seiten) oder Views/Shared/_Layout.cshtml (MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Weitere Informationen zu RCLs finden Sie in den folgenden Artikeln:
- Reusable Razor UI in Klassenbibliotheken mit ASP.NET Core
- Verwenden von ASP.NET Core Razor Komponenten aus einer Razor Klassenbibliothek (RCL)
Informationen zur Blazor CSS-Isolation finden Sie unter ASP.NET Core Blazor CSS Isolation.
Verarbeiten von HEAD-Anforderungen mit einem OnGet-Handlerfallback
HEAD-Anforderungen ermöglichen das Abrufen der Header für eine bestimmte Ressource. Im Gegensatz zu GET-Anforderungen geben HEAD-Anforderungen keinen Antworttext zurück.
Normalerweise wird ein OnHead-Handler erstellt und für HEAD-Anforderungen aufgerufen:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Razor Pages fällt auf den OnGet-Handler zurück, wenn kein OnHead-Handler definiert ist.
XSRF/CSRF und Razor Seiten
Razor Seiten werden durch Antifälschungsvalidierung geschützt. Das FormTagHelper fügt Antifälschungstoken in HTML-Formularelemente hinzu.
Verwenden von Layouts, Teilansichten, Vorlagen und Tag-Helpern mit Razor Pages
Die Seiten funktionieren mit allen Funktionen der Razor-View-Engine. Layouts, Partials, Vorlagen, Tag Helpers, _ViewStart.cshtml und _ViewImports.cshtml funktionieren in gleicher Weise wie für herkömmliche Razor-Ansichten.
Lassen Sie uns diese Seite aufräumen, indem wir einige dieser praktischen Funktionen nutzen.
Fügen Sie eine Layoutseite zu Pages/Shared/_Layout.cshtml hinzu:
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
Das Layout:
- Steuert das Layout der einzelnen Seiten, es sei denn, das Layout wird für eine Seite deaktiviert.
- Importiert HTML-Strukturen, z.B. JavaScript und Stylesheets.
- Der Inhalt der Razor-Seite wird gerendert, wenn
@RenderBody()aufgerufen wird.
Weitere Informationen finden Sie unter Seite mit Layoutinformationen.
Die Eigenschaft Layout wird in Pages/_ViewStart.cshtml festgelegt:
@{
Layout = "_Layout";
}
Das Layout befindet sich im Ordner Pages/Shared. Seiten suchen hierarchisch nach anderen Ansichten (Layouts, Vorlagen oder Teilansichten) und beginnen im gleichen Ordner wie die aktuelle Seite. Ein Layout im Ordner Pages/Shared kann von jeder Razor-Seite aus unter dem Ordner Pages verwendet werden.
Die Layoutdatei sollte im Ordner Pages/Shared gespeichert werden.
Wir empfehlen Ihnen, die Layoutdatei nicht im Ordner Views/Shared (Ansichten/Freigegeben) zu platzieren. Views/Shared ist ein MVC-Ansichtsmuster. Razor Seiten stützen sich auf die Ordnerhierarchie und nicht auf Pfadkonventionen.
Die Suchansicht von einer Razor Seite umfasst den Ordner Pages. Die Layouts, Vorlagen und Teilansichten, die mit MVC-Controllern und herkömmlichen Razor-Ansichten verwendet werden, funktionieren problemlos.
Fügen Sie eine Pages/_ViewImports.cshtml-Datei hinzu:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace wird weiter unten im Tutorial erläutert. Die @addTagHelper-Anweisung importiert die integrierten Taghilfsprogramme in alle Seiten im Pages-Ordner.
Die @namespace-Anweisung wird auf einer Seite festgelegt.
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
Die @namespace-Anweisung legt den Namespace für die Seite fest. Die @model-Anweisung muss den Namespace nicht enthalten.
Wenn sich die @namespace-Anweisung in _ViewImports.cshtml befindet, stellt der angegebene Namespace das Präfix für den generierten Namespace auf der Seite bereit, die die @namespace-Anweisung importiert. Der rest des generierten Namespaces (der Suffixteil) ist der punkttrennte relative Pfad zwischen dem Ordner, der die Seite enthält _ViewImports.cshtml , und dem Ordner, der die Seite enthält.
Die PageModel-Klasse Pages/Customers/Edit.cshtml.cs legt den Namespace explizit fest:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Die Datei Pages/_ViewImports.cshtml legt den folgenden Namespace fest:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Der generierte Namespace für die Pages/Customers/Edit.cshtmlRazor-Seite ist identisch mit der PageModel-Klasse.
@namespace
funktioniert auch mit konventionellen Razor-Ansichten.
Sehen Sie sich die Ansichtsdatei Pages/Customers/Create.cshtml an:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Die aktualisierte Ansichtsdatei Pages/Customers/Create.cshtml mit _ViewImports.cshtml und der vorherigen Layoutdatei sieht wie folgt aus:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Im vorangehenden Code importierte _ViewImports.cshtml den Namespace und die Tag-Hilfsprogramme. Die JavaScript-Dateien werden von der Layoutdatei importiert.
Der Razor Pages starter project enthält die Pages/_ValidationScriptsPartial.cshtml, wodurch die clientseitige Überprüfung eingebunden wird.
Weitere Informationen zu Teilansichten finden Sie unter Partial views in ASP.NET Core.
URL-Generierung für Seiten
Die zuvor gezeigte Create-Seite verwendet RedirectToPage:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Die App hat die folgende Datei/Ordner-Struktur:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
Die Seiten Pages/Customers/Create.cshtml und Pages/Customers/Edit.cshtml führen bei Erfolg eine Umleitung zu Pages/Customers/Index.cshtml durch. Die Zeichenfolge ./Index ist ein relativer Seitenname, der zum Zugriff auf die vorherige Seite verwendet wird. Sie wird für das Generieren von URIs für die Seite Pages/Customers/Index.cshtml verwendet. Beispiel:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
Der absolute Seitenname /Index wird zum Generieren der URLs für die Seite Pages/Index.cshtml verwendet. Beispiel:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
Der Seitenname ist der Pfad zu der Seite vom Stammordner /Pages (einschließlich eines vorangestellten /, z.B. /Index). Die oben stehenden Beispiele für eine URL-Generierung bieten erweiterte Optionen und Funktionen, durch die Sie URLs nicht mehr hartcodieren müssen. Bei der URL-Generierung wird Routing verwendet. Außerdem können damit Parameter generiert und entsprechend der Definition der Route im Zielpfad codiert werden.
Die URL-Generierung für Seiten unterstützt relative Namen. In der folgenden Tabelle wird dargestellt, welche Indexseite durch verschiedene RedirectToPage-Parameter in Pages/Customers/Create.cshtml ausgewählt wird.
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index"), RedirectToPage("./Index") und RedirectToPage("../Index") sind relative Namen. Der RedirectToPage-Parameter wird mit dem Pfad der aktuellen Seite kombiniert, um den Namen der Zielseite zu berechnen.
Das Verknüpfen relativer Namen eignet sich beim Erstellen von Websites mit einer komplexen Struktur. Wenn durch relative Namen Seiten in einem Ordner verknüpft werden, hat das folgende Vorteile:
- Das Umbenennen eines Ordners bricht die relativen Verknüpfungen nicht.
- Links sind nicht defekt, da sie den Ordnernamen nicht enthalten.
Um auf eine Seite in einem anderen Bereich umzuleiten, geben Sie den Bereich an:
RedirectToPage("/Index", new { area = "Services" });
Weitere Informationen finden Sie unter Areas in ASP.NET Core und Razor Pages route and app conventions in ASP.NET Core.
ViewData-Attribut
Daten können mit ViewDataAttribute an eine Seite übermittelt werden. Für Eigenschaften mit dem [ViewData]-Attribut werden die Werte in ViewDataDictionary gespeichert und daraus geladen.
Im folgenden Beispiel wendet AboutModel das [ViewData]-Attribut auf die Title-Eigenschaft an:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Greifen Sie auf der 'Über'-Seite auf die Eigenschaft Title als Modelleigenschaft zu.
<h1>@Model.Title</h1>
Im Layout wird der Titel aus dem ViewData-Wörterbuch gelesen:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core macht den TempData verfügbar. Diese Eigenschaft speichert Daten, bis sie gelesen werden. Die Methoden Keep und Peek können verwendet werden, um die Daten zu überprüfen, ohne sie zu löschen.
TempData eignet sich für die Umleitung, wenn Daten für mehr als eine Anforderung benötigt werden.
Im folgenden Code wird der Wert von Message mit TempData festgelegt:
public class CreateDotModel : PageModel
{
private readonly AppDbContext _db;
public CreateDotModel(AppDbContext db)
{
_db = db;
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./Index");
}
}
Das folgende Markup in der Datei Pages/Customers/Index.cshtml zeigt den Wert von Message mit TempData an.
<h3>Msg: @Model.Message</h3>
Das Seitenmodell Pages/Customers/Index.cshtml.cs wendet das [TempData]-Attribut auf die Eigenschaft Message an.
[TempData]
public string Message { get; set; }
Weitere Informationen finden Sie unter TempData.
Mehrere Handler pro Seite
Die folgende Seite generiert Markup für zwei Handler mithilfe des asp-page-handler Tag Helpers:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
Das Formular im vorherigen Beispiel hat zwei Sendeschaltflächen, und jede verwendet FormActionTagHelper, um an eine andere URL zu übermitteln. Das asp-page-handler-Attribut ist eine Ergänzung für asp-page.
asp-page-handler generiert URLs, die an die von einer Seite definierten Handler-Methoden übermittelt werden.
asp-page wird nicht angegeben, weil das Beispiel mit der aktuellen Seite verknüpft.
Das Seitenmodell:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
Der vorherige Code verwendet benannte Handlermethoden. Benannte Handlermethoden werden aus dem Text im Namen nach On<HTTP Verb> und vor Async (falls vorhanden) erstellt. Im vorherigen Beispiel sind OnPostJoinListAsync und OnPostJoinListUCAsync die Seitenmethoden. Wenn Sie OnPost und Async entfernen, lauten die Handlernamen JoinList und JoinListUC.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Mit dem vorherigen Code lautet der URL-Pfad, der an OnPostJoinListAsync übermittelt, https://localhost:5001/Customers/CreateFATH?handler=JoinList. Der URL-Pfad, der an OnPostJoinListUCAsync übermittelt, lautet https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.
Benutzerdefinierte Routen
Verwenden Sie die @page-Anweisung für Folgendes:
- Geben Sie eine benutzerdefinierte Route zu einer Seite an. Die Route zur Seite „Info“ kann mit
/Some/Other/Pathbeispielsweise auf@page "/Some/Other/Path"festgelegt werden. - Das Anfügen von Segmenten an die Standardroute einer Seite. Mit
@page "item"kann beispielsweise ein item-Segment an die Standardroute der Seite angefügt werden. - Das Anfügen von Parametern an die Standardroute einer Seite. Beispielsweise kann ein ID-Parameter,
id, für eine Seite mit@page "{id}"erforderlich sein.
Ein pfad zum Stamm, der am Anfang mit einer Tilde (~) versehen ist, wird unterstützt. Beispielsweise ist @page "~/Some/Other/Path" mit @page "/Some/Other/Path"identisch.
Wenn Sie nicht möchten, dass die Abfragezeichenfolge ?handler=JoinList in der URL enthalten ist, ändern Sie die Route so, dass der Handlername im Pfadteil der URL eingefügt wird. Sie können die Route anpassen, indem Sie nach der @page-Anweisung eine Routenvorlage in doppelten Anführungszeichen hinzufügen.
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Mit dem vorherigen Code lautet der URL-Pfad, der an OnPostJoinListAsync übermittelt, https://localhost:5001/Customers/CreateFATH/JoinList. Der URL-Pfad, der an OnPostJoinListUCAsync übermittelt, lautet https://localhost:5001/Customers/CreateFATH/JoinListUC.
Das ? nach handler bedeutet, dass der Routenparameter optional ist.
Verteilung von JavaScript-Dateien (JS)
Die Kombination von JavaScript-Dateien (JS) für Seiten und Ansichten ist praktisch, um Skripts in einer App zu organisieren.
Stellen Sie JS-Dateien mithilfe der folgenden Konventionen für Dateinamenerweiterungen zusammen:
- Seiten von Razor Pages-Apps und Ansichten von MVC-Apps:
.cshtml.jsExamples:-
Pages/Index.cshtml.jsfür dieIndex-Seite einer Razor Pages-App unterPages/Index.cshtml -
Views/Home/Index.cshtml.jsfür dieIndex-Ansicht einer MVC-App unterViews/Home/Index.cshtml
-
Mit dem JS sind die Dateien öffentlich adressierbar.
Seiten und Ansichten aus einer zusammengelegten Skriptdatei in der App:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- Der
{PATH}-Platzhalter ist der Pfad zur Seite, der Ansicht oder der Komponente. - Der
{PAGE, VIEW, OR COMPONENT}-Platzhalter ist die Seite, die Ansicht oder die Komponente. - Der Platzhalter
{EXTENSION}stimmt mit der Erweiterung der Seite, Ansicht oder Komponente überein, entwederrazorodercshtml.
Razor Pages-Beispiel:
Eine JS-Datei für die Seite
Indexwird imPages-Ordner (Pages/Index.cshtml.js) neben der SeiteIndex(Pages/Index.cshtml) abgelegt. Auf der SeiteIndexwird auf das Skript unter dem Pfad im OrdnerPagesverwiesen:@section Scripts { <script src="~/Pages/Index.cshtml.js"></script> }- Der
Das Standardlayout Pages/Shared/_Layout.cshtml kann so konfiguriert werden, dass verbundene JS-Dateien enthalten sind, ohne dass jede Seite einzeln konfiguriert werden muss:
<script asp-src-include="@(ViewContext.View.Path).js"></script>
Der Download-Sample nutzt den vorherigen Codeausschnitt, um verbundene JS-Dateien in das Standardlayout einzuschließen.
Wenn die App veröffentlicht wird, verschiebt das Framework das Skript automatisch in den Webstamm. Im vorherigen Beispiel wird das Skript nach bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Pages\Index.cshtml.js verschoben, wobei der Platzhalter {TARGET FRAMEWORK MONIKER} der Ziel-Framework-Moniker ist. Die relative URL des Skripts auf der Seite Index muss nicht geändert werden.
Wenn die App veröffentlicht wird, verschiebt das Framework das Skript automatisch in den Webstamm. Im vorherigen Beispiel wird das Skript nach bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Components\Pages\Index.razor.js verschoben, wobei der Platzhalter {TARGET FRAMEWORK MONIKER} der Zielframeworkmoniker ist. Die relative URL des Skripts in der Komponente Index muss nicht geändert werden.
Für Skripts, die von einer Razor-Klassenbibliothek (RCL) bereitgestellt wurden:
_content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- Der Platzhalter
{PACKAGE ID}ist der Paketbezeichner der RCL (oder der Bibliotheksname für eine Klassenbibliothek, auf die von der App verwiesen wird). - Der
{PATH}-Platzhalter ist der Pfad zur Seite, der Ansicht oder der Komponente. Wenn sich eine Razor-Komponente im Stammverzeichnis der RCL befindet, wird das Pfadsegment nicht eingeschlossen. - Der
{PAGE, VIEW, OR COMPONENT}-Platzhalter ist die Seite, die Ansicht oder die Komponente. - Der Platzhalter
{EXTENSION}stimmt mit der Erweiterung der Seite, Ansicht oder Komponente überein, entwederrazorodercshtml.
- Der Platzhalter
Erweiterte Konfigurationen und Einstellungen
Die Konfigurationen und Einstellungen in den folgenden Abschnitten sind für die meisten Apps nicht erforderlich.
Um die erweiterten Optionen zu konfigurieren, verwenden Sie die Überladung AddRazorPages, die RazorPagesOptions konfiguriert:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Verwenden Sie RazorPagesOptions, um das Stammverzeichnis für Seiten festzulegen oder Anwendungsmodellkonventionen für Seiten hinzuzufügen. Weitere Informationen zu Konventionen finden Sie unter Razor Pages-Autorisierungskonventionen.
Informationen zum Vorkompilieren von Ansichten finden Sie unter Razor-Ansichtenkompilierung.
Festlegen, dass Razor Pages im Inhaltsstammverzeichnis sind
Standardmäßig lautet das Stammverzeichnis für Razor Pages /Pages. Fügen Sie WithRazorPagesAtContentRoot hinzu, um anzugeben, dass sich Ihre Razor-Seiten im Inhaltsstammverzeichnis (ContentRootPath) der App befinden:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Geben Sie an, dass Razor Pages sich in einem benutzerdefinierten Stammverzeichnis befinden.
Fügen Sie WithRazorPagesRoot hinzu, um anzugeben, dass sich Ihre Razor-Seiten in einem benutzerdefinierten Stammverzeichnis der App befinden. Geben Sie dabei einen relativen Pfad an:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Weitere Ressourcen
- Weitere Informationen finden Sie unter Erste Schritte mit Razor Pages, das auf dieser Einführung basiert.
-
[Authorize]Attribut in Razor Seiten-Apps - Download- oder Ansichtsbeispielcode
- Overview von ASP.NET Core
- Razor Syntaxreferenz für ASP.NET Core
- Bereiche in ASP.NET Core
- Tutorial: Einführung in die Razor Seiten in ASP.NET Core
- Razor Pages-Autorisierungskonventionen in ASP.NET Core
- Razor Seiten-, Routen- und App-Konventionen in ASP.NET Core
- Razor Pages Komponententests in ASP.NET Core
- Partialansichten in ASP.NET Core
- Visual Studio 2019 16.4 oder höher mit der Arbeitsauslastung ASP.NET und Webentwicklung
- .NET Core 3.1 SDK
- Visual Studio 2019 16.8 oder höher mit ASP.NET und Webentwicklung Workload
- .NET 5 SDK
Erstellen eines Razor Pages-Projekts
Detaillierte Anweisungen zum Erstellen eines Pages-Projekts finden Sie unter "Erste Schritte mit Pages."
Razor Seiten
Razor Pages ist in Startup.cs aktiviert:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
Sehen Sie sich diese einfache Seite an:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
Der vorangehende Code sieht ähnlich wie eine Razor Ansichtsdatei aus, die in einer ASP.NET Core App mit Controllern und Ansichten verwendet wird. Der Unterschied besteht in der @page-Anweisung.
@page macht die Datei zu einer MVC-Aktion, d.h. dass Anfragen direkt ohne einen Controller verarbeitet werden.
@page muss die erste Razor-Anweisung auf einer Seite sein.
@page wirkt sich auf das Verhalten aller anderen Razor-Konstrukte aus.
Razor Pages-Dateinamen haben das Suffix .cshtml.
Eine ähnliche Seite, die die PageModel-Klasse verwendet, wird in den folgenden zwei Dateien angezeigt. Die datei Pages/Index2.cshtml:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Das Pages/Index2.cshtml.cs Seitenmodell:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Standardmäßig hat die PageModel-Klassendatei denselben Namen wie die Razor-Seiten-Datei, wobei .cs angehängt wird. Die vorherige Razor Seite ist Pages/Index2.cshtml. Die Datei mit der PageModel-Klasse heißt Pages/Index2.cshtml.cs.
Die Zuordnungen von URL-Pfaden zu Seiten werden durch den Speicherort der Seite im Dateisystem bestimmt. Die folgende Tabelle zeigt einen Razor Seitenpfad und die entsprechende URL:
| Dateiname und Pfad | übereinstimmende URL |
|---|---|
/Pages/Index.cshtml |
/ oder /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store oder /Store/Index |
Notes:
- Die Laufzeitumgebung sucht standardmäßig im Ordner Pages nach Razor Pages-Dateien.
- Wenn eine Seite nicht in einer URL enthalten ist, ist
Indexdie Standardseite.
Schreiben eines einfachen Formulars
Razor Pages ist darauf ausgelegt, allgemeine Muster, die mit Webbrowsern verwendet werden können, beim Erstellen einer App leichter implementieren zu können. Die Modellbindung, Taghilfsprogramme und alle HTML-Hilfsprogramme funktionieren nur mit den Eigenschaften, die in einer Klasse der Razor Pages definiert wurden. Nehmen wir z.B. eine Seite, die ein allgemeines Kontaktformular für das Contact-Modell implementiert:
Für die Beispiele in diesem Dokument wird die DbContext in der Datei Startup.cs initialisiert.
Für die In-Memory-Datenbank ist das NuGet-Paket Microsoft.EntityFrameworkCore.InMemory erforderlich.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
services.AddRazorPages();
}
Das Datenmodell:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
Der db-Kontext:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Models;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
}
Die Ansichtsdatei Pages/Create.cshtml:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Das Pages/Create.cshtml.cs Seitenmodell:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using RazorPagesContacts.Models;
using System.Threading.Tasks;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Die PageModel -Klasse heißt standardmäßig <PageName>Model und befindet sich im selben Namespace wie die Seite.
Mit der Klasse PageModel kann die Logik einer Seite von deren Darstellung getrennt werden. Sie definiert Seitenhandler für Anforderungen, die an die Seite geschickt wurden, und für zum Rendern der Seite verwendete Daten. Diese Trennung ermöglicht Folgendes:
- Verwalten von Seitenabhängigkeiten mithilfe von Dependency Injection
- Einheitstests
Die Seite verfügt über eine OnPostAsync-Handlermethode, die bei POST-Anforderungen ausgeführt wird (wenn ein Benutzer das Formular sendet). Für alle HTTP-Verben können Handlermethoden hinzugefügt werden. Die am häufigsten verwendeten Handler sind:
-
OnGet, um den für eine Seite erforderlichen Status zu initialisieren. Im vorangehenden Code wird dieOnGetCreateModel.cshtml-Seite durch die Razor-Methode dargestellt. -
OnPost, um Formular-Einsendungen zu behandeln.
Das Namenssuffix Async ist optional. Es wird jedoch standardmäßig häufig für asynchrone Funktionen verwendet. Der vorhergehende Code ist typisch für Razor Pages.
Wenn Sie mit ASP.NET-Apps mit Controllern und Ansichten vertraut sind:
- Der
OnPostAsync-Code im vorangehenden Beispiel ähnelt dem typischen Controllercode. - Die meisten primitiven MVC-Typen wie solche für Modellbindungen, Validierungen und Aktionsergebnisse werden in Controllern und Razor Pages auf dieselbe Weise eingesetzt.
Die vorherige OnPostAsync-Methode:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Der grundlegende Ablauf von OnPostAsync:
Überprüfen Sie auf Validierungsfehler
- Wenn keine Fehler vorliegen, werden die Daten gespeichert und weitergeleitet.
- Wenn es Fehler gibt, zeigen Sie die Seite erneut mit den Validierungsmeldungen an. denn Validierungsfehler werden oftmals auf dem Client erkannt und nie an den Server übermittelt.
Die Ansichtsdatei Pages/Create.cshtml:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Der von Pages/Create.cshtml gerenderte HTML-Code sieht wie folgt aus:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
Im vorherigen Code gilt für die Formularübermittlung mittels POST Folgendes:
Bei gültigen Daten:
Die
OnPostAsync-Handlermethode ruft die RedirectToPage-Hilfsmethode auf. Eine Instanz vonRedirectToPagewird von RedirectToPageResult zurückgegeben.RedirectToPage:- Ist ein Aktionsergebnis.
- ähnelt
RedirectToActionoderRedirectToRoute(wird in Controllern und Ansichten verwendet). - Ist für Seiten angepasst. Im vorhergehenden Beispiel leitet es an die Stammindexseite (
/Index) weiter. Informationen zuRedirectToPagefinden Sie im Abschnitt URL-Generierung für Seiten.
Bei Validierungsfehlern, die an den Server übermittelt werden:
- Die
OnPostAsync-Handlermethode ruft die Page-Hilfsmethode auf. Eine Instanz vonPagewird von PageResult zurückgegeben. Der Vorgang, bei demPagezurückgegeben wird, ähnelt dem Vorgang, bei dem Aktionen im ControllerViewzurückgeben.PageResultist der Standardrückgabetyp für eine Handlermethode. Eine Handlermethode, dievoidzurückgibt, rendert die Seite. - Wenn im vorangehenden Beispiel das Formular per POST übermittelt wird und dabei kein Wert angegeben wird, gibt ModelState.IsValid 'false' zurück. In diesem Beispiel werden keine Validierungsfehler auf dem Client angezeigt. Die Verarbeitung von Validierungsfehlern wird weiter unten in diesem Artikel behandelt.
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _context.Customers.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }- Die
Bei Validierungsfehlern, die durch eine clientseitige Validierung erkannt werden:
- Die Daten werden nicht an den Server übertragen.
- Die clientseitige Validierung wird weiter unten in diesem Artikel erläutert.
Die Eigenschaft Customer verwendet das [BindProperty]-Attribut, um die Modellbindung zu aktivieren:
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
[BindProperty] sollte nicht in Modellen mit Eigenschaften verwendet werden, die vom Client nicht geändert werden dürfen. Weitere Informationen finden Sie unter Overposting.
Razor Pages binden Eigenschaften standardmäßig nur an Nicht-GET-Verben. Durch die Bindung an Eigenschaften entfällt das Schreiben von Code, mit dem HTTP-Daten in den Modelltyp konvertiert werden. Die Bindung reduziert den Code, indem sie die gleiche Eigenschaft verwendet, um Formularfelder (<input asp-for="Customer.Name">) zu rendern und die Eingabe zu akzeptieren.
Warning
Aus Sicherheitsgründen müssen Sie sich dafür entscheiden, Daten von GET-Anforderungen an Seitenmodelleigenschaften zu binden. Überprüfen Sie die Benutzereingaben, bevor Sie sie den Eigenschaften zuordnen. Die Verwendung der GET-Bindung ist von Vorteil, wenn Sie Szenarios behandeln, die von Abfragezeichenfolgen oder Routenwerten abhängig sind.
Legen Sie die GET-Eigenschaft des [BindProperty]-Attributs auf SupportsGet fest, um eine Eigenschaft an true-Anforderungen zu binden:
[BindProperty(SupportsGet = true)]
Weitere Informationen finden Sie in der ASP.NET Core Community-Standup: Diskussion über Bindungen bei GET (YouTube).
Sehen Sie sich die Ansichtsdatei Pages/Create.cshtml an:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
- Im vorangehenden Code bindet das Eingabetag-Hilfsprogramm
<input asp-for="Customer.Name" />das HTML-Element<input>an den ModellausdruckCustomer.Name. -
@addTagHelperstellt Taghilfsprogramme zur Verfügung.
Die Startseite
Index.cshtml ist die Startseite:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var contact in Model.Customer)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
Die zugeordnete PageModel-Klasse (Index.cshtml.cs):
public class IndexModel : PageModel
{
private readonly CustomerDbContext _context;
public IndexModel(CustomerDbContext context)
{
_context = context;
}
public IList<Customer> Customer { get; set; }
public async Task OnGetAsync()
{
Customer = await _context.Customers.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
Die Datei Index.cshtml enthält das folgende Markup:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
Der <a /a>Anchor Tag Helper verwendete das asp-route-{value}-Attribut, um einen Link zur Edit-Seite zu erzeugen. Der Link enthält die Routendaten mit der Kontakt-ID. Beispiel: https://localhost:5001/Edit/1.
Taghilfsprogramme ermöglichen serverseitigem Code das Mitwirken am Erstellen und Rendern von HTML-Elementen in Razor-Dateien.
Die Datei Index.cshtml enthält das Markup zum Erstellen der Schaltfläche „delete“ (Löschen) für jeden Kundenkontakt:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
Der gerenderte HTML-Code sieht wie folgt aus:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Wenn die „delete“-Schaltfläche in HTML gerendert wird, enthält das zugehörige formaction-Element Parameter für Folgendes:
- die Kundenkontakt-ID, die durch das
asp-route-id-Attribut angegeben wird - den
handler, der durch dasasp-page-handler-Attribut angegeben wird
Wenn die Schaltfläche ausgewählt wird, wird eine POST-Anforderung an den Server gesendet. Durch Konvention wird der Name der Handlermethode auf Grundlage des Werts des handler-Parameters gemäß dem Schema OnPost[handler]Async ausgewählt.
Da der handler in diesem Beispiel delete ist, wird die Handlermethode OnPostDeleteAsync verwendet, um die POST-Anforderung zu verarbeiten. Wenn asp-page-handler auf einen anderen Wert (z. B. remove) festgelegt wird, wird eine Handlermethode namens OnPostRemoveAsync ausgewählt.
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
Die OnPostDeleteAsync-Methode:
- ruft
idaus der Abfragezeichenfolge ab. - Fragt mit
FindAsyncdie Datenbank nach dem Kundenkontakt ab. - Wenn der Kundenkontakt gefunden wird, wird er entfernt, und die Datenbank wird aktualisiert.
- Ruft RedirectToPage auf, um zur Hauptseite (
/Index) umzuleiten.
Die Datei „Edit.cshtml“
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
<div asp-validation-summary="All"></div>
<input asp-for="Customer.Id" type="hidden" />
<div>
<label asp-for="Customer.Name"></label>
<div>
<input asp-for="Customer.Name" />
<span asp-validation-for="Customer.Name"></span>
</div>
</div>
<div>
<button type="submit">Save</button>
</div>
</form>
Die erste Zeile enthält die @page "{id:int}"-Anweisung. Die Routingbeschränkung "{id:int}" weist die Seite an, Anfragen für die Seite zu akzeptieren, die int-Routendaten enthalten. Wenn eine Anforderung an die Seite bestimmte Routingdaten nicht enthält, die in einen int konvertiert werden können, gibt die Runtime einen Fehler vom Typ „HTTP 404: Nicht gefunden“ zurück. Um die ID optional zu machen, fügen Sie ? an die Routeneinschränkung an:
@page "{id:int?}"
Die datei Edit.cshtml.cs:
public class EditModel : PageModel
{
private readonly CustomerDbContext _context;
public EditModel(CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Customer = await _context.Customers.FindAsync(id);
if (Customer == null)
{
return RedirectToPage("./Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception($"Customer {Customer.Id} not found!");
}
return RedirectToPage("./Index");
}
}
Validation
Gültigkeitsprüfungsregeln:
- In der Modellklasse werden sie deklarativ angegeben.
- Sie werden überall in der App erzwungen.
Der Namespace System.ComponentModel.DataAnnotations stellt eine Gruppe integrierter Validierungsattribute bereit, die deklarativ auf eine Klasse oder Eigenschaft angewendet werden. „DataAnnotations“ enthält auch Formatierungsattribute wie [DataType], die bei der Formatierung helfen und keinerlei Validierung bereitstellen.
Sehen Sie sich das Customer-Modell an:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
Mithilfe der folgenden Create.cshtml Ansichtsdatei:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Der vorangehende Code:
umfasst jQuery-Skripts einschließlich solcher zur Validierung.
Verwendet die
<div />- und<span />Tag-Hilfsprogramme, um Folgendes zu ermöglichen:- Clientseitige Überprüfung.
- Darstellung von Validierungsfehlern.
wird der folgende HTML-Code generiert:
<p>Enter a customer name:</p> <form method="post"> Name: <input type="text" data-val="true" data-val-length="The field Name must be a string with a maximum length of 10." data-val-length-max="10" data-val-required="The Name field is required." id="Customer_Name" maxlength="10" name="Customer.Name" value="" /> <input type="submit" /> <input name="__RequestVerificationToken" type="hidden" value="<Antiforgery token here>" /> </form> <script src="/lib/jquery/dist/jquery.js"></script> <script src="/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Wenn Sie das Formular „Erstellen“ ohne einen Wert für den Namen übermitteln, wird die Fehlermeldung „Das Namensfeld ist erforderlich“ auf dem Formular angezeigt. Wenn JavaScript auf dem Client aktiviert ist, zeigt der Browser den Fehler an, ohne dass Daten per POST an den Server gesendet werden.
Das [StringLength(10)]-Attribut generiert data-val-length-max="10" für den gerenderten HTML-Code.
data-val-length-max verhindert, dass im Browser ein Wert eingegeben wird, der die angegebene Maximallänge überschreitet. Wenn ein Tool wie Fiddler zum Bearbeiten und erneuten Senden einer POST-Anforderung verwendet wird
- Mit einem Namen, der länger als 10 Zeichen ist.
- geschieht Folgendes: Die Fehlermeldung „The field Name must be a string with a maximum length of 10“ (Das Namensfeld muss eine Zeichenfolge mit maximal 10 Zeichen enthalten) wird zurückgegeben.
Sehen Sie sich das folgende Movie-Modell an:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Die Validierungsattribute geben das Verhalten an, das auf die Modelleigenschaften angewendet werden soll:
Die Attribute
RequiredundMinimumLengthgeben an, dass eine Eigenschaft einen Wert haben muss. Ein Benutzer kann allerdings ein Leerzeichen eingeben, um diese Anforderung zu erfüllen.Das Attribut
RegularExpressionwird verwendet, um einzuschränken, welche Zeichen eingegeben werden dürfen. Im vorhergehenden Code: "Genre":- Es dürfen nur Buchstaben enthalten sein.
- Der erste Buchstabe muss ein Großbuchstabe sein. Leerzeichen, Zahlen und Sonderzeichen sind nicht zulässig.
Für
RegularExpression-„Rating“ (Bewertung) gilt Folgendes:- Das erste Zeichen muss ein Großbuchstabe sein.
- Erlaubt Sonderzeichen und Zahlen in nachfolgenden Leerzeichen. „PG-13“ ist als Bewertung („Rating“) gültig, nicht jedoch als „Genre“.
Das Attribut
Rangeschränkt einen Wert auf einen bestimmten Bereich ein.Mit dem Attribut
StringLengthkann die maximale Länge einer Zeichenfolgeneigenschaft und optional die minimale Länge festlegt werden.Werttypen (wie
decimal,int,float,DateTime) sind grundsätzlich erforderlich und benötigen nicht das Attribut[Required].
Auf der Seite „Erstellen“ für das Movie-Modell werden Anzeigeprobleme mit ungültigen Werten gezeigt.
Weitere Informationen finden Sie unter:
Verarbeiten von HEAD-Anforderungen mit einem OnGet-Handlerfallback
HEAD-Anforderungen ermöglichen das Abrufen der Header für eine bestimmte Ressource. Im Gegensatz zu GET-Anforderungen geben HEAD-Anforderungen keinen Antworttext zurück.
Normalerweise wird ein OnHead-Handler erstellt und für HEAD-Anforderungen aufgerufen:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Razor Pages greift auf den OnGet-Handler zurück, falls kein OnHead-Handler definiert ist.
XSRF/CSRF und Razor Seiten
Razor Seiten werden durch Antifälschungsvalidierung geschützt. Das FormTagHelper fügt Antifälschungstoken in HTML-Formularelemente hinzu.
Verwenden von Layouts, Partialansichten, Vorlagen und Tag-Hilfsprogrammen mit Razor Pages
Razor Pages beinhaltet alle Funktionen der Razor-Anzeige-Engine. Layouts, Teilansichten, Vorlagen, Tag-Helper, _ViewStart.cshtml und _ViewImports.cshtml funktionieren genauso wie bei herkömmlichen Razor-Ansichten.
Lassen Sie uns diese Seite aufräumen, indem wir einige dieser praktischen Funktionen nutzen.
Fügen Sie eine Layoutseite zu Pages/Shared/_Layout.cshtml hinzu:
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
Das Layout:
- Steuert das Layout der einzelnen Seiten, es sei denn, das Layout wird für eine Seite deaktiviert.
- Importiert HTML-Strukturen, z.B. JavaScript und Stylesheets.
- Der Inhalt der Razor-Seite wird gerendert, wenn
@RenderBody()aufgerufen wird.
Weitere Informationen finden Sie unter Layout-Seite.
Die Eigenschaft Layout wird in Pages/_ViewStart.cshtml festgelegt:
@{
Layout = "_Layout";
}
Das Layout befindet sich im Ordner Pages/Shared. Seiten suchen hierarchisch nach anderen Ansichten (Layouts, Vorlagen oder Teilansichten) und beginnen im gleichen Ordner wie die aktuelle Seite. Ein Layout im Ordner Pages/Shared kann von jeder Razor-Seite aus unter dem Ordner Pages verwendet werden.
Die Layoutdatei sollte im Ordner Pages/Shared gespeichert werden.
Wir empfehlen Ihnen, die Layoutdatei nicht im Ordner Views/Shared zu platzieren. Views/Shared ist ein MVC-Ansichtsmuster. Razor Seiten sollen sich auf die Ordnerhierarchie stützen, nicht auf Pfadkonventionen.
Die Ansichtssuche von einer Razor Page enthält den Ordner Pages. Die Layouts, Vorlagen und Teilansichten, die mit MVC-Controllern und herkömmlichen Razor-Ansichten verwendet werden, funktionieren problemlos.
Fügen Sie eine Pages/_ViewImports.cshtml-Datei hinzu:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace wird weiter unten im Tutorial erläutert. Die @addTagHelper-Anweisung bringt die integrierten Taghilfsprogramme zu allen Seiten in der Ordner Pages.
Die @namespace-Anweisung wird auf einer Seite festgelegt.
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
Die @namespace-Anweisung legt den Namespace für die Seite fest. Die @model-Anweisung muss den Namespace nicht enthalten.
Wenn sich die @namespace-Anweisung in _ViewImports.cshtml befindet, stellt der angegebene Namespace das Präfix für den generierten Namespace auf der Seite bereit, die die @namespace-Anweisung importiert. Der rest des generierten Namespaces (der Suffixteil) ist der punkttrennte relative Pfad zwischen dem Ordner, der die Seite enthält _ViewImports.cshtml , und dem Ordner, der die Seite enthält.
Die PageModel-Klasse Pages/Customers/Edit.cshtml.cs legt den Namespace explizit fest:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Die Datei Pages/_ViewImports.cshtml legt den folgenden Namespace fest:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Der generierte Namespace für die Pages/Customers/Edit.cshtmlRazor-Seite ist identisch mit der PageModel-Klasse.
@namespace
funktioniert auch mit konventionellen Razor-Ansichten.
Sehen Sie sich die Ansichtsdatei Pages/Create.cshtml an:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Die aktualisierte Ansichtsdatei Pages/Create.cshtml mit _ViewImports.cshtml und der vorherigen Layoutdatei sieht wie folgt aus:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Im vorangehenden Code hat _ViewImports.cshtml den Namespace und die Tag Helpers importiert. Die JavaScript-Dateien werden von der Layoutdatei importiert.
Der Razor Pages starter project enthält die Pages/_ValidationScriptsPartial.cshtml, wodurch die clientseitige Überprüfung eingebunden wird.
Weitere Informationen zu Teilansichten finden Sie unter Partial views in ASP.NET Core.
URL-Generierung für Seiten
Die zuvor gezeigte Create-Seite verwendet RedirectToPage:
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Die App hat die folgende Datei/Ordner-Struktur:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
Die Seiten Pages/Customers/Create.cshtml und Pages/Customers/Edit.cshtml führen bei Erfolg eine Umleitung zu Pages/Customers/Index.cshtml durch. Die Zeichenfolge ./Index ist ein relativer Seitenname, der zum Zugriff auf die vorherige Seite verwendet wird. Sie wird für das Generieren von URIs für die Seite Pages/Customers/Index.cshtml verwendet. Beispiel:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
Der absolute Seitenname /Index wird zum Generieren der URLs für die Seite Pages/Index.cshtml verwendet. Beispiel:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
Der Seitenname ist der Pfad zu der Seite vom Stammordner /Pages (einschließlich eines vorangestellten /, z.B. /Index). Die oben stehenden Beispiele für eine URL-Generierung bieten erweiterte Optionen und Funktionen, durch die Sie URLs nicht mehr hartcodieren müssen. Bei der URL-Generierung wird Routing verwendet. Außerdem können damit Parameter generiert und entsprechend der Definition der Route im Zielpfad codiert werden.
Die URL-Generierung für Seiten unterstützt relative Namen. In der folgenden Tabelle wird dargestellt, welche Indexseite durch verschiedene RedirectToPage-Parameter in Pages/Customers/Create.cshtml ausgewählt wird.
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index"), RedirectToPage("./Index") und RedirectToPage("../Index") sind relative Namen. Der RedirectToPage-Parameter wird mit dem Pfad der aktuellen Seite kombiniert, um den Namen der Zielseite zu berechnen.
Das Verknüpfen relativer Namen eignet sich beim Erstellen von Websites mit einer komplexen Struktur. Wenn durch relative Namen Seiten in einem Ordner verknüpft werden, hat das folgende Vorteile:
- Das Umbenennen eines Ordners bricht die relativen Verknüpfungen nicht.
- Links sind nicht defekt, da sie den Ordnernamen nicht enthalten.
Um auf eine Seite in einem anderen Bereich umzuleiten, geben Sie den Bereich an:
RedirectToPage("/Index", new { area = "Services" });
Weitere Informationen finden Sie unter Areas in ASP.NET Core und Razor Pages route and app conventions in ASP.NET Core.
ViewData-Attribut
Daten können mit ViewDataAttribute an eine Seite übermittelt werden. Für Eigenschaften mit dem [ViewData]-Attribut werden die Werte in ViewDataDictionary gespeichert und daraus geladen.
Im folgenden Beispiel wendet AboutModel das [ViewData]-Attribut auf die Title-Eigenschaft an:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Greifen Sie auf der 'Über'-Seite auf die Eigenschaft Title als Modelleigenschaft zu.
<h1>@Model.Title</h1>
Im Layout wird der Titel aus dem ViewData-Wörterbuch gelesen:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core macht den TempData verfügbar. Diese Eigenschaft speichert Daten, bis sie gelesen werden. Die Methoden Keep und Peek können verwendet werden, um die Daten zu überprüfen, ohne sie zu löschen.
TempData eignet sich für die Umleitung, wenn Daten für mehr als eine Anforderung benötigt werden.
Im folgenden Code wird der Wert von Message mit TempData festgelegt:
public class CreateDotModel : PageModel
{
private readonly AppDbContext _db;
public CreateDotModel(AppDbContext db)
{
_db = db;
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./Index");
}
}
Das folgende Markup in der Datei Pages/Customers/Index.cshtml zeigt den Wert von Message mit TempData an.
<h3>Msg: @Model.Message</h3>
Das Seitenmodell Pages/Customers/Index.cshtml.cs wendet das [TempData]-Attribut auf die Eigenschaft Message an.
[TempData]
public string Message { get; set; }
Weitere Informationen finden Sie unter TempData.
Mehrere Handler pro Seite
Die folgende Seite generiert Markup für zwei Handler mithilfe des asp-page-handler Tag Helpers:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
Das Formular im vorherigen Beispiel hat zwei Sendeschaltflächen, und jede verwendet FormActionTagHelper, um an eine andere URL zu übermitteln. Das asp-page-handler-Attribut ist eine Ergänzung für asp-page.
asp-page-handler generiert URLs, die an die von einer Seite definierten Handler-Methoden übermittelt werden.
asp-page wird nicht angegeben, weil das Beispiel mit der aktuellen Seite verknüpft.
Das Seitenmodell:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
Der vorherige Code verwendet benannte Handlermethoden. Benannte Handlermethoden werden aus dem Text im Namen nach On<HTTP Verb> und vor Async (falls vorhanden) erstellt. Im vorherigen Beispiel sind OnPostJoinListAsync und OnPostJoinListUCAsync die Seitenmethoden. Wenn Sie OnPost und Async entfernen, lauten die Handlernamen JoinList und JoinListUC.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Mit dem vorherigen Code lautet der URL-Pfad, der an OnPostJoinListAsync übermittelt, https://localhost:5001/Customers/CreateFATH?handler=JoinList. Der URL-Pfad, der an OnPostJoinListUCAsync übermittelt, lautet https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.
Benutzerdefinierte Routen
Verwenden Sie die @page-Anweisung für Folgendes:
- Geben Sie eine benutzerdefinierte Route zu einer Seite an. Die Route zur Seite „Info“ kann mit
/Some/Other/Pathbeispielsweise auf@page "/Some/Other/Path"festgelegt werden. - Das Anfügen von Segmenten an die Standardroute einer Seite. Mit
@page "item"kann beispielsweise ein item-Segment an die Standardroute der Seite angefügt werden. - Das Anfügen von Parametern an die Standardroute einer Seite. Beispielsweise kann ein ID-Parameter,
id, für eine Seite mit@page "{id}"erforderlich sein.
Ein pfad zum Stamm, der am Anfang mit einer Tilde (~) versehen ist, wird unterstützt. Beispielsweise ist @page "~/Some/Other/Path" mit @page "/Some/Other/Path"identisch.
Wenn Sie nicht möchten, dass die Abfragezeichenfolge ?handler=JoinList in der URL enthalten ist, ändern Sie die Route so, dass der Handlername im Pfadteil der URL eingefügt wird. Sie können die Route anpassen, indem Sie nach der @page-Anweisung eine Routenvorlage in doppelten Anführungszeichen hinzufügen.
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Mit dem vorherigen Code lautet der URL-Pfad, der an OnPostJoinListAsync übermittelt, https://localhost:5001/Customers/CreateFATH/JoinList. Der URL-Pfad, der an OnPostJoinListUCAsync übermittelt, lautet https://localhost:5001/Customers/CreateFATH/JoinListUC.
Das ? nach handler bedeutet, dass der Routenparameter optional ist.
Erweiterte Konfigurationen und Einstellungen
Die Konfigurationen und Einstellungen in den folgenden Abschnitten sind für die meisten Apps nicht erforderlich.
Um die erweiterten Optionen zu konfigurieren, verwenden Sie die Überladung AddRazorPages, die RazorPagesOptions konfiguriert:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
Verwenden Sie RazorPagesOptions, um das Stammverzeichnis für Seiten festzulegen oder Anwendungsmodellkonventionen für Seiten hinzuzufügen. Weitere Informationen zu Konventionen finden Sie unter Razor Pages-Autorisierungskonventionen.
Informationen zum Vorkompilieren von Ansichten finden Sie unter Razor-Ansichtenkompilierung.
Festlegen, dass Razor Pages im Inhaltsstammverzeichnis sind
Standardmäßig lautet das Stammverzeichnis für Razor Pages /Pages. Fügen Sie WithRazorPagesAtContentRoot hinzu, um anzugeben, dass sich Ihre Razor-Seiten im Inhaltsstammverzeichnis (ContentRootPath) der App befinden:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
Geben Sie an, dass Razor Pages sich in einem benutzerdefinierten Stammverzeichnis befinden.
Fügen Sie WithRazorPagesRoot hinzu, um anzugeben, dass sich Ihre Razor-Seiten in einem benutzerdefinierten Stammverzeichnis der App befinden. Geben Sie dabei einen relativen Pfad an:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
}
Weitere Ressourcen
- Weitere Informationen finden Sie unter Erste Schritte mit Razor Pages, das auf dieser Einführung basiert.
-
[Authorize]Attribut in Razor Seiten-Apps - Download- oder Ansichtsbeispielcode
- Overview von ASP.NET Core
- Razor Syntaxreferenz für ASP.NET Core
- Bereiche in ASP.NET Core
- Tutorial: Einführung in die Razor Seiten in ASP.NET Core
- Razor Pages-Autorisierungskonventionen in ASP.NET Core
- Razor Seiten-, Routen- und App-Konventionen in ASP.NET Core
- Razor Pages Komponententests in ASP.NET Core
- Partialansichten in ASP.NET Core