Einführung in Razor Pages in ASP.NET Core
Von Rick Anderson, Dave Brock und Kirk Larkin
Hinweis
Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Warnung
Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Wichtig
Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.
Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.
Razor Pages kann im Vergleich zu Controllern und Ansichten das Programmieren seitenbasierter Anwendungen vereinfachen und die Produktivität erhöhen.
Ein Tutorial, in dem der Model-View-Controller-Ansatz verwendet wird, finden Sie unter Erste Schritte mit ASP.NET Core MVC und Visual Studio.
Dieses Dokument bietet eine Einführung in Razor Pages. Es handelt sich nicht um ein Schritt-für-Schritt-Tutorial. Wenn es Ihnen Probleme bereitet, die Ausführungen in einigen Abschnitten nachzuvollziehen, lesen Sie Erste Schritte mit Razor Pages in ASP.NET Core. Eine Übersicht über ASP.NET Core finden Sie unter Einführung in ASP.NET Core.
Voraussetzungen
- Visual Studio 2022 mit der Workload ASP.NET und Webentwicklung
- .NET 6.0 SDK
Erstellen eines Razor Pages-Projekts
Ausführliche Informationen zum Erstellen eines Razor Pages-Projekts finden Sie unter Erste Schritte mit Razor Pages.
Razor Pages
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();
Für den Code oben gilt:
- AddRazorPages fügt Dienste für Razor Pages zur App hinzu.
- MapRazorPages fügt Endpunkte für Razor Pages 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 vorherige Code ähnelt sehr einer Razor-Ansichtsdatei, 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
enthält Folgendes:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Das Pages/Index2.cshtml.cs
Page-Modell:
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 }";
}
}
}
Die PageModel
-Klassendatei hat standardmäßig den gleichen Namen wie die Datei mit Razor Pages, nur dass außerdem .cs
angefügt wird. Die vorherige Datei mit Razor Page lautet beispielsweise 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 Pfad zu Razor Pages und die entsprechende URL:
Dateiname und Pfad | Entsprechende URL |
---|---|
/Pages/Index.cshtml |
/ oder /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store oder /Store/Index |
Notizen:
- Die Runtime sucht standardmäßig im Ordner Pages (Seiten) nach Dateien mit RRazor Pages.
- Wenn eine Seite nicht in einer URL enthalten ist, ist
Index
die 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 mit den Eigenschaften, die in einer Razor Pages-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 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 Seitenmodell Pages/Customers/Create.cshtml.cs
:
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
- Komponententests
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 dieCreate.cshtml
Razor-Seite durch dieOnGet
-Methode dargestellt.OnPost
, um Formularübermittlungen 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 vertraut sind, die Controller und Ansichten verwenden, werden Ihnen folgende Fakten bekannt vorkommen:
- 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
:
Prüfen 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.RedirectToPage
gibt eine Instanz von RedirectToPageResult zurück.RedirectToPage
:- ist ein Aktionsergebnis.
- ähnelt
RedirectToAction
oderRedirectToRoute
(wird in Controllern und Ansichten verwendet). - ist an Seiten angepasst. Im vorhergehenden Beispiel leitet es an die Stammindexseite (
/Index
) weiter. Informationen zuRedirectToPage
finden Sie im Abschnitt URL-Generierung für Seiten.
Bei Validierungsfehlern, die an den Server übermittelt werden:
- Die
OnPostAsync
-Handlermethode ruft die Page-Hilfsmethode auf.Page
gibt eine Instanz von PageResult zurück. Der Vorgang, bei demPage
zurückgegeben wird, ähnelt dem Vorgang, bei dem Aktionen im ControllerView
zurückgeben.PageResult
ist der Standardrückgabetyp für eine Handlermethode. Eine Handlermethode, dievoid
zurückgibt, rendert die Seite. - Wenn im vorangehenden Beispiel das Formular mithilfe von POST übermittelt 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.
[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 per POST 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 mithilfe der gleichen Eigenschaft, um Formularfelder (<input asp-for="Customer.Name">
) zu rendern und die Eingabe zu akzeptieren.
Warnung
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 SupportsGet
-Eigenschaft des [BindProperty]
-Attributs auf true
fest, um eine Eigenschaft an GET
-Anforderungen zu binden:
[BindProperty(SupportsGet = true)]
Weitere Informationen finden Sie unter ASP.NET Core Community Standup: Binden auf GET-Diskussion (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
. @addTagHelper
stellt Taghilfsprogramme zur Verfügung.
Die home-Seite
Index.cshtml
ist die home-Seite:
@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 Anchor-Taghilfsprogramm <a /a>
verwendet das asp-route-{value}
-Attribut, um einen Link zur Bearbeitungsseite zu generieren. Der Link enthält die Routendaten mit der Kontakt-ID. Beispielsweise 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:
- ruft die
id
der Abfragezeichenfolge ab. - Fragt mit
FindAsync
die Datenbank nach dem Kundenkontakt ab. - Wenn der Kundenkontakt gefunden wird, wird er entfernt, und die Datenbank wird aktualisiert.
- Ruft RedirectToPage auf, um die 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, die Anforderungen für die Seite zu akzeptieren, die int
-Routingdaten 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
enthält Folgendes:
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);
}
}
Validierung
Für Validierungsregeln gilt Folgendes:
- Sie werden deklarativ in der Modellklasse 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; }
}
}
Für die folgende Ansichtsdatei Create.cshtml
gilt Folgendes:
@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 Taghilfsprogramme
<div />
und<span />
, um Folgendes zu ermöglichen:- clientseitiger Validierung.
- Rendering 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 „Create“ (Erstellen) ohne einen Wert für den Namen mit POST übermitteln, wird die Fehlermeldung „The Name field is required“ (Für das Feld ‚Name‘ muss ein Wert angegeben werden) 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
- und die Länge des Namens 10 Zeichen überschreitet,
- 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 für die Modelleigenschaften erzwungen werden soll:
Die Attribute
Required
undMinimumLength
geben an, dass eine Eigenschaft einen Wert haben muss. Ein Benutzer kann allerdings ein Leerzeichen eingeben, um diese Anforderung zu erfüllen.Das Attribut
RegularExpression
wird verwendet, um einzuschränken, welche Zeichen eingegeben werden dürfen. Für „Genre“ im Code oben gilt Folgendes:- 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.
- Sonderzeichen und Zahlen sind als darauffolgende Zeichen zulässig. „PG-13“ ist als Bewertung („Rating“) gültig, nicht jedoch als „Genre“.
Das Attribut
Range
schränkt einen Wert auf einen bestimmten Bereich ein.Mit dem Attribut
StringLength
kann 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 „Create“ (Erstellen) für das Movie
-Modell werden ungültige Werte und die daraus resultierenden Fehler angezeigt:
Weitere Informationen finden Sie unter:
CSS-Isolation
Isolieren Sie zur Reduzierung oder Vermeidung CSS-Formatvorlagen auf einzelne Seiten, Ansichten und Komponenten:
- 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 Pages) 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 Assemblyname des Projekts. Ein Link zu den gebündelten CSS-Stilen wird im Layout der App platziert.
Im Inhalt von <head>
der Datei Pages/Shared/_Layout.cshtml
(Razor Pages) oder Views/Shared/_Layout.cshtml
(MVC) der App fügen Sie den Link zu den gebündelten CSS-Styles hinzu oder bestätigen ihn:
<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
. CSS- und Vererbungsregeln bleiben für bereichsbezogene CSS-Dateien 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
.
Hinweis
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. Zum Zeitpunkt der Erstellung wird ein Projektbündel mit der Konvention {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css
erstellt, wobei der Platzhalter {STATIC WEB ASSETS BASE PATH}
der statische Basispfad für 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.
- Sie wird nicht als statische Webressource der App veröffentlicht, die die Stile verwendet.
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 z. B. mithilfe von Visual Studio die vorhandene Präprozessorkompilierung als Aufgabe Vor Build im Aufgabenausführungs-Explorer von Visual Studio.
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 Format der Bereichsbezeichner anzupassen, aktualisieren Sie die Projektdatei mit dem gewünschten 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 Beispiel für eine Projektdatei enthält eine BaseView.cshtml.css
-Datei allgemeine Stile 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 Platzhalteroperator (*
), um Bereichsbezeichner für mehrere Dateien freizugeben:
<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 Projektdatei die StaticWebAssetBasePath
-Eigenschaft, um den Standardpfad zu ändern. Das folgende Beispiel platziert die zugewiesene CSS-Datei und die rest der Assets der App unter dem Pfad _content
:
<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 href
-Attribut des <link>
-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 ist standardmäßig der Assemblyname des Projekts, wenn der Paketbezeichner nicht in der Projektdatei angegeben ist.
Im folgenden Beispiel:
- Der Basispfad der statischen Webressource lautet
_content/ClassLib
. - Der Assemblyname der Klassenbibliothek lautet
ClassLib
.
Pages/Shared/_Layout.cshtml
(Razor Pages) 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:
- Wiederverwendbare -Benutzeroberfläche in Klassenbibliotheken mit ASP.NET CoreRazor
- Nutzen von ASP.NET Core Razor-Komponenten über eine Razor-Klassenbibliothek (RCL)
Informationen zur CSS-Isolation in Blazor finden Sie unter CSS-Isolation in Blazor in ASP.NET Core.
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 ruft den OnGet
-Handler auf, wenn kein OnHead
-Handler definiert ist.
XSRF/CSRF und Razor Pages
Razor Pages wird durch Validierungsmaßnahmen vor XSRF/CSRF-Angriffen geschützt. Das Formulartag-Hilfsprogramm injiziert Anti-XSRF/CSRF-Token in HTML-Formularelemente.
Verwenden von Layouts, Teilansichten, Vorlagen und Taghilfsprogrammen mit Razor Pages
Razor Pages beinhaltet alle Funktionen der Razor-Anzeige-Engine. Layouts, Teilansichten, Vorlagen, Taghilfsprogramme, _ViewStart.cshtml
und _ViewImports.cshtml
funktionieren auf die gleiche Weise wie für herkömmliche Razor-Ansichten.
Strukturieren Sie diese Seite mit einigen dieser praktischen Funktionen.
Fügen Sie der Pages/Shared/_Layout.cshtml
eine Layoutseite 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 Layoutseite.
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 Pages basieren auf der Ordnerhierarchie, nicht auf Pfadkonventionen.
Die Ansichtensuche in 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 wie folgt für eine 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 Namespace (der Suffix-Teil) ist der durch Punkte getrennte relative Pfad zwischen dem Ordner, der _ViewImports.cshtml
enthält, 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.cshtml
Razor-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 werden von _ViewImports.cshtml
der Namespace und die Taghilfsprogramme importiert. Die JavaScript-Dateien werden von der Layoutdatei importiert.
Das Razor Pages-Startprojekt enthält die Seite Pages/_ValidationScriptsPartial.cshtml
, die die clientseitige Validierung bindet.
Weitere Informationen zu Teilansichten finden Sie unter Teilansichten 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.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.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
stellt einen relativen Seitennamen dar, der für den 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. Zum 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) | Seite |
---|---|
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:
- Relative Links funktionieren weiterhin, wenn ein Ordner umbenannt wird.
- Links funktionieren weiterhin, da sie keinen Ordnernamen 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 Bereiche in ASP.NET Core und Routen- und App-Konventionen für Razor Pages in ASP.NET Core.
Attribut „ViewData“
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 Infoseite 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 TempData verfügbar. Diese Eigenschaft speichert Daten, bis sie gelesen wurden. 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 mit dem asp-page-handler
-Taghilfsprogramm Markup für zwei Handler:
@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 als Übermittlungsziel jeweils die durch eine Seite festgelegte Handlermethode verwenden. 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:
- Das Angeben einer benutzerdefinierten Route zu einer Seite. Die Route zur Seite „Info“ kann mit
@page "/Some/Other/Path"
beispielsweise auf/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. Mit
@page "{id}"
kann beispielsweise ein ID-Parameter (id
) für eine Seite angefordert werden.
Es wird ein relativer Pfad zum Stamm unterstützt, der durch eine Tilde (~
) festgelegt wird. @page "~/Some/Other/Path"
entspricht beispielsweise @page "/Some/Other/Path"
.
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.
Kollokation 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.js
Beispiele:Pages/Index.cshtml.js
für dieIndex
-Seite einer Razor Pages-App unterPages/Index.cshtml
Views/Home/Index.cshtml.js
für dieIndex
-Ansicht einer MVC-App unterViews/Home/Index.cshtml
Zusammengestellte JS-Dateien sind über den Pfad zur Datei im Projekt öffentlich adressierbar:
Seiten und Ansichten aus einer kombinierten 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, entwederrazor
odercshtml
.
Razor Pages-Beispiel:
Eine JS-Datei für die Seite
Index
wird imPages
-Ordner (Pages/Index.cshtml.js
) neben der SeiteIndex
(Pages/Index.cshtml
) abgelegt. Auf der SeiteIndex
wird auf das Skript unter dem Pfad im OrdnerPages
verwiesen:@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 Beispieldownload verwendet das vorherige Codeschnipsel, 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 Zielframeworkmoniker 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, entwederrazor
odercshtml
.
- Der Platzhalter
Erweiterte Konfigurationen und Einstellungen
Die Konfigurationen und Einstellungen in den folgenden Abschnitten sind für die meisten Apps nicht erforderlich.
Verwenden Sie die Überladung AddRazorPages, die RazorPagesOptions konfiguriert, um die erweiterten Optionen zu konfigurieren:
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 des Inhaltsstammverzeichnisses für Razor Pages
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();
Festlegen eines benutzerdefinierten Stammverzeichnisses für Razor Pages
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();
Zusätzliche Ressourcen
- Lesen Sie auch den Artikel Erste Schritte mit Razor Pages, der auf dieser Einführung aufbaut.
- Authorize-Attribut und Razor Pages
- Herunterladen und Anzeigen des Beispielcodes
- Übersicht über ASP.NET Core
- Razor-Syntaxreferenz für ASP.NET Core
- Bereiche in ASP.NET Core
- Tutorial: Erste Schritte mit Razor Pages in ASP.NET Core
- RazorAutorisierungskonventionen für Pages in ASP.NET Core
- Razor Pages: Routen- und App-Konventionen in ASP.NET Core
- RazorKomponententests für Pages in ASP.NET Core
- Teilansichten in ASP.NET Core
- Visual Studio 2019 Version 16.4 oder höher mit der Workload ASP.NET und Webentwicklung.
- .NET Core 3.1 SDK
- Visual Studio 2019 Version 16.8 oder höher mit der Workload ASP.NET und Webentwicklung
- .NET 5.0 SDK
Erstellen eines Razor Pages-Projekts
Ausführliche Informationen zum Erstellen eines Razor Pages-Projekts finden Sie unter Erste Schritte mit Razor Pages.
Razor Pages
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 vorherige Code ähnelt sehr einer Razor-Ansichtsdatei, 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
enthält Folgendes:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Das Pages/Index2.cshtml.cs
Page-Modell:
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 }";
}
}
}
Die PageModel
-Klassendatei hat standardmäßig den gleichen Namen wie die Datei mit Razor Pages, nur dass außerdem .cs
angefügt wird. Die vorherige Datei mit Razor Page lautet beispielsweise 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 Pfad zu Razor Pages und die entsprechende URL:
Dateiname und Pfad | Entsprechende URL |
---|---|
/Pages/Index.cshtml |
/ oder /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store oder /Store/Index |
Notizen:
- Die Runtime sucht standardmäßig im Ordner Pages (Seiten) nach Dateien mit RRazor Pages.
- Wenn eine Seite nicht in einer URL enthalten ist, ist
Index
die 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 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 Seitenmodell Pages/Create.cshtml.cs
:
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
- Komponententests
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 dieCreateModel.cshtml
Razor-Seite durch dieOnGet
-Methode dargestellt.OnPost
, um Formularübermittlungen 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 vertraut sind, die Controller und Ansichten verwenden, werden Ihnen folgende Fakten bekannt vorkommen:
- 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
:
Prüfen 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.RedirectToPage
gibt eine Instanz von RedirectToPageResult zurück.RedirectToPage
:- ist ein Aktionsergebnis.
- ähnelt
RedirectToAction
oderRedirectToRoute
(wird in Controllern und Ansichten verwendet). - ist an Seiten angepasst. Im vorhergehenden Beispiel leitet es an die Stammindexseite (
/Index
) weiter. Informationen zuRedirectToPage
finden Sie im Abschnitt URL-Generierung für Seiten.
Bei Validierungsfehlern, die an den Server übermittelt werden:
- Die
OnPostAsync
-Handlermethode ruft die Page-Hilfsmethode auf.Page
gibt eine Instanz von PageResult zurück. Der Vorgang, bei demPage
zurückgegeben wird, ähnelt dem Vorgang, bei dem Aktionen im ControllerView
zurückgeben.PageResult
ist der Standardrückgabetyp für eine Handlermethode. Eine Handlermethode, dievoid
zurückgibt, rendert die Seite. - Wenn im vorangehenden Beispiel das Formular mithilfe von POST übermittelt 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 per POST 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:
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 mithilfe der gleichen Eigenschaft, um Formularfelder (<input asp-for="Customer.Name">
) zu rendern und die Eingabe zu akzeptieren.
Warnung
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 SupportsGet
-Eigenschaft des [BindProperty]
-Attributs auf true
fest, um eine Eigenschaft an GET
-Anforderungen zu binden:
[BindProperty(SupportsGet = true)]
Weitere Informationen finden Sie unter ASP.NET Core Community Standup: Binden auf GET-Diskussion (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
. @addTagHelper
stellt Taghilfsprogramme zur Verfügung.
Die home-Seite
Index.cshtml
ist die home-Seite:
@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> |
Das Anchor-Taghilfsprogramm <a /a>
verwendet das asp-route-{value}
-Attribut, um einen Link zur Bearbeitungsseite zu generieren. Der Link enthält die Routendaten mit der Kontakt-ID. Beispielsweise 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 die
id
der Abfragezeichenfolge ab. - Fragt mit
FindAsync
die Datenbank nach dem Kundenkontakt ab. - Wenn der Kundenkontakt gefunden wird, wird er entfernt, und die Datenbank wird aktualisiert.
- Ruft RedirectToPage auf, um die Stammindexseite (
/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, die Anforderungen für die Seite zu akzeptieren, die int
-Routingdaten 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
enthält Folgendes:
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");
}
}
Validierung
Für Validierungsregeln gilt Folgendes:
- Sie werden deklarativ in der Modellklasse 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; }
}
}
Für die folgende Ansichtsdatei Create.cshtml
gilt Folgendes:
@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 Taghilfsprogramme
<div />
und<span />
, um Folgendes zu ermöglichen:- clientseitiger Validierung.
- Rendering 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 „Create“ (Erstellen) ohne einen Wert für den Namen mit POST übermitteln, wird die Fehlermeldung „The Name field is required“ (Für das Feld ‚Name‘ muss ein Wert angegeben werden) 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
- und die Länge des Namens 10 Zeichen überschreitet,
- 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 für die Modelleigenschaften erzwungen werden soll:
Die Attribute
Required
undMinimumLength
geben an, dass eine Eigenschaft einen Wert haben muss. Ein Benutzer kann allerdings ein Leerzeichen eingeben, um diese Anforderung zu erfüllen.Das Attribut
RegularExpression
wird verwendet, um einzuschränken, welche Zeichen eingegeben werden dürfen. Für „Genre“ im Code oben gilt Folgendes:- 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.
- Sonderzeichen und Zahlen sind als darauffolgende Zeichen zulässig. „PG-13“ ist als Bewertung („Rating“) gültig, nicht jedoch als „Genre“.
Das Attribut
Range
schränkt einen Wert auf einen bestimmten Bereich ein.Mit dem Attribut
StringLength
kann 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 „Create“ (Erstellen) für das Movie
-Modell werden ungültige Werte und die daraus resultierenden Fehler angezeigt:
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 ruft den OnGet
-Handler auf, wenn kein OnHead
-Handler definiert ist.
XSRF/CSRF und Razor Pages
Razor Pages wird durch Validierungsmaßnahmen vor XSRF/CSRF-Angriffen geschützt. Das Formulartag-Hilfsprogramm injiziert Anti-XSRF/CSRF-Token in HTML-Formularelemente.
Verwenden von Layouts, Teilansichten, Vorlagen und Taghilfsprogrammen mit Razor Pages
Razor Pages beinhaltet alle Funktionen der Razor-Anzeige-Engine. Layouts, Teilansichten, Vorlagen, Taghilfsprogramme, _ViewStart.cshtml
und _ViewImports.cshtml
funktionieren auf die gleiche Weise wie für herkömmliche Razor-Ansichten.
Strukturieren Sie diese Seite mit einigen dieser praktischen Funktionen.
Fügen Sie der Pages/Shared/_Layout.cshtml
eine Layoutseite 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 Layoutseite.
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 Pages basieren auf der Ordnerhierarchie, nicht auf Pfadkonventionen.
Die Ansichtensuche in 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 wie folgt für eine 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 Namespace (der Suffix-Teil) ist der durch Punkte getrennte relative Pfad zwischen dem Ordner, der _ViewImports.cshtml
enthält, 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.cshtml
Razor-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 werden von _ViewImports.cshtml
der Namespace und die Taghilfsprogramme importiert. Die JavaScript-Dateien werden von der Layoutdatei importiert.
Das Razor Pages-Startprojekt enthält die Seite Pages/_ValidationScriptsPartial.cshtml
, die die clientseitige Validierung bindet.
Weitere Informationen zu Teilansichten finden Sie unter Teilansichten 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.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.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
stellt einen relativen Seitennamen dar, der für den 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. Zum 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) | Seite |
---|---|
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:
- Relative Links funktionieren weiterhin, wenn ein Ordner umbenannt wird.
- Links funktionieren weiterhin, da sie keinen Ordnernamen 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 Bereiche in ASP.NET Core und Routen- und App-Konventionen für Razor Pages in ASP.NET Core.
Attribut „ViewData“
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 Infoseite 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 TempData verfügbar. Diese Eigenschaft speichert Daten, bis sie gelesen wurden. 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 mit dem asp-page-handler
-Taghilfsprogramm Markup für zwei Handler:
@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 als Übermittlungsziel jeweils die durch eine Seite festgelegte Handlermethode verwenden. 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:
- Das Angeben einer benutzerdefinierten Route zu einer Seite. Die Route zur Seite „Info“ kann mit
@page "/Some/Other/Path"
beispielsweise auf/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. Mit
@page "{id}"
kann beispielsweise ein ID-Parameter (id
) für eine Seite angefordert werden.
Es wird ein relativer Pfad zum Stamm unterstützt, der durch eine Tilde (~
) festgelegt wird. @page "~/Some/Other/Path"
entspricht beispielsweise @page "/Some/Other/Path"
.
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.
Verwenden Sie die Überladung AddRazorPages, die RazorPagesOptions konfiguriert, um die erweiterten Optionen zu konfigurieren:
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 des Inhaltsstammverzeichnisses für Razor Pages
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();
}
Festlegen eines benutzerdefinierten Stammverzeichnisses für Razor Pages
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");
}
Zusätzliche Ressourcen
- Lesen Sie auch den Artikel Erste Schritte mit Razor Pages, der auf dieser Einführung aufbaut.
- Authorize-Attribut und Razor Pages
- Herunterladen und Anzeigen des Beispielcodes
- Übersicht über ASP.NET Core
- Razor-Syntaxreferenz für ASP.NET Core
- Bereiche in ASP.NET Core
- Tutorial: Erste Schritte mit Razor Pages in ASP.NET Core
- RazorAutorisierungskonventionen für Pages in ASP.NET Core
- Razor Pages: Routen- und App-Konventionen in ASP.NET Core
- RazorKomponententests für Pages in ASP.NET Core
- Teilansichten in ASP.NET Core