Wprowadzenie do rozwiązania Razor Pages na platformie ASP.NET Core
Autorzy: Rick Anderson, Dave Brock i Kirk Larkin
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
W scenariuszach opartych na stronach rozwiązanie Razor Pages może zwiększyć wydajność i łatwość programowania w porównaniu z używaniem kontrolerów i widoków.
Jeśli szukasz samouczka, w którym wykorzystano podejście Model-View-Controller, zobacz Wprowadzenie do podejścia MVC na platformie ASP.NET Core.
Ten dokument zawiera wprowadzenie do rozwiązania Razor Pages. Nie jest to samouczek krok po kroku. Jeśli okaże się, że niektóre sekcje są zbyt zaawansowane, zobacz Wprowadzenie do rozwiązaniaRazor Pages. Aby zapoznać się z omówieniem platformy ASP.NET Core, zobacz Wprowadzenie do platformy ASP.NET Core.
Wymagania wstępne
- Program Visual Studio 2022 z pakietem roboczym tworzenia aplikacji ASP.NET i aplikacji internetowych.
- Zestaw SDK dla platformy .NET 6.0
Tworzenie projektu rozwiązania Razor Pages
Zobacz temat Wprowadzenie do rozwiązania Razor Pages, aby uzyskać szczegółowe instrukcje dotyczące tworzenia projektu za pomocą rozwiązania Razor Pages.
Razor Pages
Rozwiązanie Razor Pages można włączyć w pliku Program.cs
:
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();
Powyższy kod:
- Metoda AddRazorPages dodaje usługi rozwiązania Razor Pages do aplikacji.
- Metoda MapRazorPages dodaje punkty końcowe rozwiązania Razor Pages do interfejsu IEndpointRouteBuilder.
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
Powyższy kod w dużym stopniu przypomina plik widoku Razor używany w aplikacji platformy ASP.NET Core z kontrolerami i widokami. Różni się dyrektywą @page
. Dyrektywa @page
zamienia plik w działanie MVC, co oznacza, że obsługuje żądania bezpośrednio, bez udziału kontrolera. Dyrektywa @page
musi być pierwszą dyrektywą Razor na stronie. Dyrektywa @page
wpływa na działanie innych konstrukcji Razor. Nazwy plików rozwiązania Razor Pages mają sufiks .cshtml
.
Podobna strona, korzystająca z klasy PageModel
, jest pokazana w następujących dwóch plikach. Plik Pages/Index2.cshtml
:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Model strony Pages/Index2.cshtml.cs
:
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 }";
}
}
}
Zgodnie z konwencją plik klasy PageModel
ma taką samą nazwę jak plik strony Razor z dodanym rozszerzeniem .cs
. Na przykład poprzednia strona Razor to Pages/Index2.cshtml
. Plik zawierający klasę PageModel
nosi nazwę Pages/Index2.cshtml.cs
.
Skojarzenia ścieżek URL ze stronami są określane przez lokalizację strony w systemie plików. W poniższej tabeli przedstawiono ścieżkę strony Razor i odpowiadający jej adres URL:
Nazwa i ścieżka pliku | Odpowiedni adres URL |
---|---|
/Pages/Index.cshtml |
/ lub /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store lub /Store/Index |
Uwagi:
- Środowisko uruchomieniowe domyślnie wyszukuje pliki rozwiązania Razor Pages w folderze Pages.
Index
to strona domyślna, jeśli adres URL nie zawiera strony.
Pisanie podstawowego formularza
Rozwiązanie Razor Pages zostało stworzone, aby ułatwić wdrożenie typowych wzorców używanych w przeglądarkach internetowych podczas tworzenia aplikacji. Powiązanie modelu, pomocnicy tagów i pomocnicy HTML działają przy użyciu właściwości zdefiniowanych w klasie strony Razor. Rozważ stronę, w której wdrożony będzie podstawowy formularz kontaktowy w przypadku modelu Contact
:
W przypadku przykładów w tym dokumencie DbContext
plik jest inicjowany w pliku Program.cs .
Baza danych w pamięci wymaga pakietu NuGet Microsoft.EntityFrameworkCore.InMemory
.
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();
Model danych:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
Kontekst bazy danych:
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>();
}
}
Plik widoku 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>
Model strony 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");
}
}
Zgodnie z konwencją klasa PageModel
nazywa się <PageName>Model
i znajduje się w tej samej przestrzeni nazw co strona.
Klasa PageModel
umożliwia oddzielenie logiki strony od jej prezentacji. Definiuje procedury obsługi stron dla żądań wysyłanych do strony oraz dla danych używanych do renderowania strony. To rozdzielenie umożliwia:
- Zarządzanie zależnościami stron za pomocą wstrzykiwania zależności.
- Testowanie jednostek
Strona ma metodę OnPostAsync
obsługi, która jest uruchamiana na POST
żądaniach (gdy użytkownik publikuje formularz). Można dodawać metody procedury obsługi dla dowolnego czasownika HTTP. Najczęstsze procedury obsługi to:
OnGet
— na potrzeby inicjowania stanu wymaganego dla strony. W poprzednim kodzie metodaOnGet
spowodowała wyświetlenie stronyCreate.cshtml
Razor.OnPost
— na potrzeby obsługi przesłanych formularzy.
Sufiks nazewnictwa Async
jest opcjonalny, ale jest często używany zgodnie z konwencją na potrzeby funkcji asynchronicznych. Powyższy kod jest typowy dla rozwiązania Razor Pages.
Jeśli znasz aplikacje ASP.NET korzystające z kontrolerów i widoków:
- Kod
OnPostAsync
w poprzednim przykładzie wygląda podobnie do typowego kodu kontrolera. - Większość typów pierwotnych MVC, takich jak powiązanie modelu, walidacja i wyniki akcji działają tak samo w przypadku kontrolerów i rozwiązania Razor Pages.
Poprzednia metoda OnPostAsync
:
[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");
}
Podstawowy przepływ metody OnPostAsync
:
Sprawdź, czy wystąpiły błędy walidacji.
- Jeśli nie ma żadnych błędów, zapisz dane i przeprowadź przekierowanie.
- Jeśli wystąpią jakieś błędy, pokaż stronę ponownie z komunikatami walidacji. W wielu przypadkach błędy weryfikacji zostaną wykryte w kliencie i nigdy nie zostaną przesłane do serwera.
Plik widoku 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>
Renderowany kod HTML z pliku Pages/Customers/Create.cshtml
:
<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>
W poprzednim kodzie w przypadku opublikowania formularza:
Z prawidłowymi danymi:
Metoda procedury obsługi
OnPostAsync
wywołuje metodę pomocnika RedirectToPage. MetodaRedirectToPage
zwraca wystąpienie klasy RedirectToPageResult.RedirectToPage
:- Jest wynikiem akcji.
- Przypomina metodę
RedirectToAction
lubRedirectToRoute
(używaną w kontrolerach i widokach). - Jest dostosowana pod kątem stron. W poprzednim przykładzie nastąpiło przekierowanie do głównej strony indeksu (
/Index
). MetodaRedirectToPage
jest opisana szczegółowo w sekcji dotyczącej generowania adresu URL w rozwiązaniu Pages.
Z błędami walidacji, które są przekazywane do serwera:
- Metoda procedury obsługi
OnPostAsync
wywołuje metodę pomocnika Page. MetodaPage
zwraca wystąpienie klasy PageResult. Zwrócenie elementuPage
wygląda podobnie jak zwrócenie elementuView
przez akcje w kontrolerach. ElementPageResult
jest domyślnym zwracanym typem w przypadku metody procedury obsługi. Metoda procedury obsługi, która zwraca wartośćvoid
, renderuje stronę. - W poprzednim przykładzie opublikowanie formularza bez żadnych wartości spowodowało, że właściwość ModelState.IsValid zwróciła wartość false. W tym przykładzie po stronie klienta nie są wyświetlane żadne błędy walidacji. Obsługa błędów walidacji jest omówiona w dalszej części tego dokumentu.
[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"); }
- Metoda procedury obsługi
W przypadku wykrycia błędów walidacji po stronie klienta:
- Dane nie są publikowane na serwerze.
- Walidacja po stronie klienta zostanie wyjaśniona w dalszej części tego dokumentu.
Do wyrażenia zgody na powiązanie modelu właściwość Customer
wykorzystuje atrybut [BindProperty]
:
[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");
}
Właściwość [BindProperty]
nie powinna być używana w modelach zawierających właściwości, które nie powinny być zmieniane przez klienta. Aby uzyskać więcej informacji, zobacz Przesyłanie dodatkowych danych.
Rozwiązanie Razor Pages domyślnie wiąże właściwości wyłącznie z czasownikami innymi niż GET
. Powiązanie z właściwościami eliminuje konieczność pisania kodu w celu przekonwertowania danych HTTP na typ modelu. Powiązanie redukuje kod poprzez użycie tej samej właściwości do renderowania pól formularza (<input asp-for="Customer.Name">
) i akceptowania danych wejściowych.
Ostrzeżenie
Ze względów bezpieczeństwa należy wyrazić zgodę na powiązanie danych żądania GET
z właściwościami modelu strony. Przed mapowaniem danych wejściowych użytkownika na właściwości zweryfikuj je. Wyrażenie zgody na powiązanie danych GET
jest przydatne w scenariuszach, które wykorzystują ciągi zapytania lub wartości trasy.
Aby powiązać właściwość w przypadku żądań GET
, ustaw właściwość SupportsGet
atrybutu [BindProperty]
na true
:
[BindProperty(SupportsGet = true)]
Aby uzyskać więcej informacji, zobacz Podsumowanie ASP.NET Core Community Standup: dyskusja na temat powiązania z użyciem metody GET (YouTube).
Przegląd pliku widoku 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>
- W poprzednim kodzie pomocnik
<input asp-for="Customer.Name" />
tagu wejściowego wiąże element HTML<input>
z wyrażeniemCustomer.Name
modelu. - Dyrektywa
@addTagHelper
udostępnia pomocników tagów.
Strona home
Index.cshtml
home to strona:
@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>
Powiązana klasa PageModel
(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();
}
}
Plik Index.cshtml
zawiera następujące znaczniki:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<a /a>
Pomocnik tagu zakotwiczenia użył atrybutu asp-route-{value}
do wygenerowania linku do strony Edytuj. Link zawiera dane trasy z identyfikatorem kontaktu. Na przykład https://localhost:5001/Edit/1
. Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
Plik Index.cshtml
zawiera znaczniki umożliwiające tworzenie przycisku usuwania dla każdego kontaktu klienta:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
Renderowany kod HTML:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Gdy przycisk usuwania jest renderowany w formacie HTML, jego akcja formularza zawiera następujące parametry:
- Identyfikator kontaktu klienta określony przez atrybut
asp-route-id
. - Element
handler
określony przez atrybutasp-page-handler
.
Po wybraniu przycisku żądanie formularza POST
jest wysyłane do serwera. Zgodnie z konwencją nazwa metody procedury obsługi jest wybierana na podstawie wartości parametru handler
zgodnie ze schematem OnPost[handler]Async
.
Ponieważ procedura obsługi handler
w tym przykładzie to delete
, metoda procedury obsługi OnPostDeleteAsync
jest używana do przetworzenia żądania POST
. Jeśli procedura obsługi asp-page-handler
zostanie ustawiona na inną wartość, na przykład remove
, zostanie wybrana metoda procedury obsługi o nazwie OnPostRemoveAsync
.
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();
}
Metoda OnPostDeleteAsync
:
- Pobiera identyfikator
id
z ciągu zapytania. - Tworzy zapytanie w bazie danych dotyczące kontaktu klienta przy użyciu metody
FindAsync
. - Jeśli kontakt klienta zostanie odnaleziony, zostaje usunięty, a baza danych zostaje zaktualizowana.
- Tworzy wywołanie RedirectToPage w celu przekierowania do głównej strony indeksu (
/Index
).
Plik Edit.cshtml file
@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");}
}
Pierwszy wiersz zawiera dyrektywę @page "{id:int}"
. Ograniczenie routingu "{id:int}"
instruuje stronę, aby akceptowała żądania do strony zawierające dane trasy int
. Jeśli żądanie do strony nie zawiera danych trasy, które można przekonwertować na element int
, środowisko uruchomieniowe zwraca kod błędu HTTP 404 (nie znaleziono). Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Plik Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesContacts.Data.CustomerDbContext _context;
public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
if (Customer == null)
{
return NotFound();
}
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null)
{
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.Id))
{
return NotFound();
}
else
{
throw;
}
}
}
return RedirectToPage("./Index");
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.Id == id);
}
}
Walidacja
Reguły walidacji:
- Są deklaratywnie określone w klasie modelu.
- Są wymuszane w każdym miejscu w aplikacji.
Przestrzeń nazw System.ComponentModel.DataAnnotations zapewnia zestaw wbudowanych atrybutów walidacji, które są stosowane deklaratywnie wobec klasy lub właściwości. Przestrzeń nazw DataAnnotations zawiera również atrybuty formatowania, takie jak [DataType]
, które pomagają w formatowaniu i nie zapewniają żadnej walidacji.
Rozważmy model Customer
:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
W przypadku użycia następującego pliku widoku Create.cshtml
:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Powyższy kod ma następujące działanie:
Obejmuje skrypty walidacji jQuery i jQuery.
Używa pomocników tagów
<div />
i<span />
do włączenia:- Walidacji po stronie klienta.
- Renderowania błędów walidacji.
Generuje następujący kod HTML:
<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>
Opublikowanie formularza tworzenia bez wartości nazwy powoduje wyświetlenie komunikatu o błędzie „Pole nazwy jest wymagane” w formularzu. Jeśli po stronie klienta włączono język JavaScript, błąd zostanie wyświetlony w przeglądarce bez publikowania na serwerze.
Atrybut [StringLength(10)]
generuje element data-val-length-max="10"
w renderowanym kodzie HTML. Element data-val-length-max
uniemożliwia przeglądarkom wprowadzanie większej liczby znaków niż określono. Jeśli do edytowania i powtarzania publikowania używane jest narzędzie takie jak Fiddler:
- W przypadku nazwy dłuższej niż 10 znaków.
- Zwracany jest komunikat o błędzie „Pole Nazwa musi być ciągiem o maksymalnej długości 10 znaków”.
Rozważ następujący model Movie
:
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; }
}
}
Atrybuty walidacji określają działanie wymuszane wobec właściwości modelu, do których są stosowane:
Atrybuty
Required
iMinimumLength
wskazują, że właściwość musi mieć wartość, ale nic nie uniemożliwia użytkownikowi wprowadzania białych znaków w celu spełnienia zasad tej walidacji.Atrybut
RegularExpression
służy do ograniczania liczby znaków, które mogą być wprowadzane jako dane wejściowe. W poprzednim kodzie w polu „Genre” (Gatunek):- Muszą być używane tylko litery.
- Pierwsza litera musi być wielką literą. Białe znaki, liczby i znaki specjalne nie są dozwolone.
W atrybucie
RegularExpression
„Rating” (Klasyfikacja):- Pierwszy znak musi być wielką literą.
- Kolejne znaki mogą być znakami specjalnymi i cyframi. W atrybucie Rating wartość „PG-13” jest prawidłowa, ale w przypadku atrybutu Genre — nie.
Atrybut
Range
ogranicza wartość do określonego zakresu.Atrybut
StringLength
wyznacza maksymalną długość właściwości ciągu oraz opcjonalnie jego minimalną długość.Typy wartości (takie jak
decimal
,int
,float
,DateTime
) są z natury wymagane i nie wymagają atrybutu[Required]
.
Na stronie tworzenia w przypadku modelu Movie
wyświetlane są błędy z nieprawidłowymi wartościami:
Aby uzyskać więcej informacji, zobacz:
Izolacja CSS
Izolowanie stylu CSS do pojedynczych stron, widoków i składników umożliwia zmniejszenie lub uniknięcie:
- Zależności stylów globalnych, które mogą być trudne do utrzymania.
- Konfliktów stylów w zawartości zagnieżdżonej.
Aby dodać plik CSS o określonym zakresie dla strony lub widoku, umieść style CSS w pliku towarzyszącym .cshtml.css
pasującym do nazwy pliku .cshtml
. W poniższym przykładzie plik Index.cshtml.css
dostarcza style CSS, które są stosowane tylko w przypadku strony lub widoku Index.cshtml
.
Pages/Index.cshtml.css
(Razor Pages) lub Views/Index.cshtml.css
(MVC):
h1 {
color: red;
}
Izolacja stylów CSS ma miejsce w czasie kompilacji. Struktura ponownie zapisuje selektory CSS, tak aby były zgodne ze znacznikami renderowanymi przez strony lub widoki aplikacji. Ponownie napisane style CSS są umieszczane w pakiecie i przedstawiane jako zasób statyczny {APP ASSEMBLY}.styles.css
. Symbol zastępczy {APP ASSEMBLY}
to nazwa zestawu projektu. Link do stylów CSS w pakiecie jest umieszczany w układzie aplikacji.
W zawartości tagu <head>
aplikacji Pages/Shared/_Layout.cshtml
(Razor Pages) lub Views/Shared/_Layout.cshtml
(MVC) dodaj lub potwierdź obecność linku do stylów CSS w pakiecie:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
W poniższym przykładzie nazwa zestawu aplikacji to WebApp
:
<link rel="stylesheet" href="WebApp.styles.css" />
Style zdefiniowane w pliku CSS o określonym zakresie są stosowane tylko w przypadku renderowanych danych wyjściowych pasującego pliku. W poprzednim przykładzie wszystkie deklaracje CSS h1
zdefiniowane w innym miejscu w aplikacji nie powodują konfliktu ze stylem nagłówka strony Index
. Reguły dziedziczenia i kaskadowania stylu CSS pozostają aktualne w przypadku plików CSS o określonym zakresie. Na przykład style zastosowane bezpośrednio do elementu <h1>
w pliku Index.cshtml
zastępują style pliku CSS o określonym zakresie w pliku Index.cshtml.css
.
Uwaga
Aby zagwarantować izolację stylu CSS podczas tworzenia pakietu, nie jest obsługiwane importowanie pliku CSS w blokach kodu Razor.
Izolacja CSS ma zastosowanie tylko w przypadku elementów kodu HTML. Izolacja CSS nie jest obsługiwana w przypadku pomocników tagów.
W pakiecie pliku CSS każda strona, widok lub składnik Razor są skojarzone z identyfikatorem zakresu w formacie b-{STRING}
, gdzie symbol zastępczy {STRING}
to ciąg dziesięciu znaków generowany przez strukturę. W poniższym przykładzie przedstawiono styl dla poprzedniego elementu <h1>
na stronie Index
aplikacji Razor Pages:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
Na stronie Index
, na której jest stosowany styl CSS z pliku w pakiecie, identyfikator zakresu jest dołączany jako atrybut kodu HTML:
<h1 b-3xxtam6d07>
Identyfikator jest unikatowy dla aplikacji. W czasie kompilacji pakiet projektu jest tworzony przy użyciu konwencji {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css
, gdzie symbol zastępczy {STATIC WEB ASSETS BASE PATH}
to statyczna podstawowa ścieżka zasobów internetowych.
Jeśli są używane inne projekty, takie jak pakiety NuGet lub biblioteki klas Razor, plik w pakiecie:
- Odwołuje się do stylów przy użyciu zaimportowanych plików CSS.
- Nie jest publikowany jako statyczny zasób internetowy aplikacji, która korzysta ze stylów.
Obsługa preprocesora CSS
Preprocesory CSS poprawiają tworzenie plików CSS poprzez wykorzystanie funkcji takich jak zmienne, zagnieżdżanie, moduły, domieszki i dziedziczenie. Mimo że izolacja CSS nie obsługuje natywnie preprocesorów CSS, takich jak Sass lub Less, integracja preprocesorów CSS jest bezproblemowa, o ile kompilacja preprocesora nastąpi, zanim struktura ponownie napisze selektory CSS podczas procesu kompilacji. Na przykład przy użyciu programu Visual Studio można skonfigurować istniejącą kompilację preprocesora jako zadanie Przed kompilowaniem w eksploratorze modułu uruchamiającego zadanie programu Visual Studio.
Wiele pakietów NuGet innych firm, takich jak AspNetCore.SassCompiler
, umożliwia kompilowanie plików SASS/SCSS na początku procesu kompilowania przed wystąpieniem izolacji CSS i nie jest wymagana żadna dodatkowa konfiguracja.
Konfiguracja izolacji CSS
Izolacja CSS umożliwia konfigurację w przypadku niektórych bardziej zaawansowanych scenariuszy, na przykład wtedy, gdy w istniejących narzędziach lub przepływach pracy występują zależności.
Dostosowywanie formatu identyfikatora zakresu
W tej sekcji symbol zastępczy {Pages|Views}
to albo Pages
w przypadku aplikacji Razor Pages, albo Views
w przypadku aplikacji MVC.
Domyślnie identyfikatory zakresu korzystają z formatu b-{STRING}
, w którym symbol zastępczy {STRING}
to ciąg zawierający dziesięć znaków wygenerowany przez strukturę. Aby dostosować format identyfikatora zakresu, zaktualizuj plik projektu do odpowiedniego wzorca:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
W poprzednim przykładzie plik CSS wygenerowany dla pliku Index.cshtml.css
zmienia identyfikator zakresu z b-{STRING}
na custom-scope-identifier
.
Użyj identyfikatorów zakresu, aby umożliwić dziedziczenie w przypadku plików CSS z określonym zakresem. W poniższym przykładzie pliku projektu plik BaseView.cshtml.css
zawiera popularne style w widokach. Plik DerivedView.cshtml.css
dziedziczy te style.
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Użyj operatora symbolu wieloznacznego (*
), aby udostępnić identyfikatory zakresu w wielu plikach:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Zmienianie podstawowej ścieżki w przypadku statycznych zasobów internetowych
Plik CSS o określonym zakresie jest generowany na poziomie głównym aplikacji. W pliku projektu użyj właściwości StaticWebAssetBasePath
, aby zmienić domyślną ścieżkę. W poniższym przykładzie plik CSS o określonym zakresie i rest zasoby aplikacji znajdują się w ścieżce _content
:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Wyłączanie automatycznego tworzenia pakietów
Aby zrezygnować ze sposobu, w jaki struktura publikuje i ładuje pliki o określonym zakresie w środowisku uruchomieniowym, użyj właściwości DisableScopedCssBundling
. W przypadku użycia tej właściwości inne narzędzia lub procesy są odpowiedzialne za pobieranie wyizolowanych plików CSS z katalogu obj
, a następnie publikowanie i ładowanie ich w środowisku uruchomieniowym:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Obsługa biblioteki klas Razor (RCL)
Gdy biblioteka klas Razor (RCL) zapewnia wyizolowane style, atrybut href
tagu <link>
wskazuje lokalizację {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css
, gdzie symbole zastępcze mają następujące znaczenie:
{STATIC WEB ASSET BASE PATH}
: podstawowa ścieżka statycznego zasobu internetowego.{PACKAGE ID}
: identyfikator pakietu biblioteki. Identyfikator pakietu domyślnie odpowiada nazwie zestawu projektu, jeśli identyfikator pakietu nie jest określony w pliku projektu.
W poniższym przykładzie:
- Podstawowa ścieżka statycznego zasobu internetowego to
_content/ClassLib
. - Nazwa pakietu biblioteki klas to
ClassLib
.
Pages/Shared/_Layout.cshtml
(Razor Pages) lub Views/Shared/_Layout.cshtml
(MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Aby uzyskać więcej informacji na temat bibliotek klas Razor, zobacz następujące artykuły:
- Interfejs użytkownika Razor do ponownego wykorzystania w bibliotekach klas na platformie ASP.NET Core
- Korzystanie ze składników Razor platformy ASP.NET Core z biblioteki klas Razor (RCL)
Aby uzyskać więcej informacji na temat izolacji CSS w przypadku rozwiązania Blazor, zobacz Izolacja CSS w rozwiązaniu Blazor na platformie ASP.NET Core.
Obsługa żądań HEAD za pomocą elementu fallback procedury obsługi OnGet
Żądania HEAD
umożliwiają pobieranie nagłówków dla określonego zasobu. W przeciwieństwie do żądań GET
żądania HEAD
nie zwracają treści odpowiedzi.
Zwykle procedura obsługi OnHead
jest tworzona i wywoływana pod kątem żądań HEAD
:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Rozwiązanie Razor Pages korzysta z wywołania procedury obsługi OnGet
rezerwowo, jeśli procedura obsługi OnHead
została zdefiniowana.
XSRF/CSRF i rozwiązanie Razor Pages
RozwiązanieRazor Pages jest chronione przez walidację chroniącą przed fałszerstwem. Element FormTagHelper wprowadza tokeny chroniące przed fałszerstwem do elementów formularza HTML.
Korzystanie z układów, stron częściowych, szablonów i pomocników tagu w rozwiązaniu Razor Pages
Strony działają ze wszystkimi funkcjami aparatu widoku Razor. Układy, strony częściowe, szablony, pomocnicy tagów oraz pliki _ViewStart.cshtml
i _ViewImports.cshtml
działają w taki sam sposób, jak w przypadku konwencjonalnych widoków rozwiązania Razor.
Uporządkujmy tę stronę, wykorzystując niektóre z tych funkcji.
Dodaj stronę układu do pliku Pages/Shared/_Layout.cshtml
:
<!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>
- Steruje układem każdej strony (chyba, że strona jest wyłączona z układu).
- Importuje struktury HTML, takie jak JavaScript i arkusze stylów.
- Zawartość strony Razor jest renderowana, tam gdzie jest wywoływana dyrektywa
@RenderBody()
.
Aby uzyskać więcej informacji, zobacz Strona układu.
Właściwość Layout jest ustawiona w pliku Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
Układ znajduje się w folderze Pages/Shared. Strony szukają innych widoków (układów, szablonów, stron częściowych) hierarchicznie, zaczynając od tego samego folderu, w którym znajduje się bieżąca strona. Układ w folderze Pages/Shared może być używany z poziomu dowolnej strony Razor w folderze Pages.
Plik układu powinien znajdować się w folderze Pages/Shared.
Nie zalecamy umieszczania pliku układu w folderze Views/Shared. Folder Views/Shared jest zgodny ze wzorcem widoków MVC. Rozwiązanie Razor Pages ma wykorzystywać hierarchię folderów, a nie konwencje ścieżek.
Wyszukiwanie widoków z poziomu rozwiązania Razor Pages obejmuje folder Pages. Układy, szablony i strony częściowe używane w przypadku kontrolerów MVC i konwencjonalnych widoków Razor po prostu działają.
Dodaj plik Pages/_ViewImports.cshtml
:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Dyrektywa @namespace
jest omówiona w dalszej części samouczka. Dyrektywa @addTagHelper
wprowadza wbudowanych pomocników tagów na wszystkich stronach w folderze Pages.
Dyrektywa @namespace
ustawiona na stronie:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
Dyrektywa @namespace
ustawia przestrzeń nazw dla strony. Dyrektywa @model
nie musi zawierać przestrzeni nazw.
Gdy dyrektywa @namespace
jest zawarta w pliku _ViewImports.cshtml
, określona przestrzeń nazw dostarcza prefiks dla wygenerowanej przestrzeni nazw na stronie, która importuje dyrektywę @namespace
. Wygenerowana rest przestrzeń nazw (część sufiksu) to ścieżka względna rozdzielona kropką między folderem zawierającym _ViewImports.cshtml
i folderem zawierającym stronę.
Na przykład klasa PageModel
w pliku Pages/Customers/Edit.cshtml.cs
jawnie konfiguruje przestrzeń nazw:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Plik Pages/_ViewImports.cshtml
konfiguruje następującą przestrzeń nazw:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Wygenerowana przestrzeń nazw dla strony Razor Pages/Customers/Edit.cshtml
jest taka sama jak klasa PageModel
.
@namespace
współpracuje również z konwencjonalnymi Razor widokami.
Rozważ plik widoku Pages/Customers/Create.cshtml
:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Zaktualizowany plik widoku Pages/Customers/Create.cshtml
z plikiem _ViewImports.cshtml
i poprzednim plikiem układu:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
W poprzednim kodzie plik _ViewImports.cshtml
służył do zaimportowania przestrzeni nazw i pomocników tagów. Plik układu zaimportował pliki JavaScript.
Projekt początkowy rozwiązania Razor Pages zawiera plik Pages/_ValidationScriptsPartial.cshtml
, który podłącza walidację po stronie klienta.
Aby uzyskać więcej informacji na temat widoków częściowych, zobacz Widoki częściowe na platformie ASP.NET Core.
Generowanie adresów URL dla stron
Na wcześniej wyświetlonej stronie Create
użyto metody 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");
}
}
Aplikacja ma następującą strukturę plików/folderów:
/Pages
Index.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.cshtml
Jeśli proces przebiega pomyślnie, strony Pages/Customers/Create.cshtml
i Pages/Customers/Edit.cshtml
przekierowują do pliku Pages/Customers/Index.cshtml
. Ciąg ./Index
to względna nazwa strony używana do uzyskiwania dostępu do poprzedniej strony. Służy do generowania adresów URL strony Pages/Customers/Index.cshtml
. Na przykład:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
Bezwzględna nazwa strony /Index
jest używana do generowania adresów URL strony Pages/Index.cshtml
. Na przykład:
Url.Page("/Index", ...)
<a asp-page="/Index">Home Index Page</a>
RedirectToPage("/Index")
Nazwa strony to ścieżka prowadząca do tej strony z folderu głównego /Pages, zawierająca element rozpoczynający /
(na przykład /Index
). Poprzednie przykłady generowania adresów URL oferują rozszerzone opcje i funkcje w porównaniu z trwałym kodowaniem adresu URL. W trakcie generowania adresu URL wykorzystywany jest routing, co umożliwia generowanie i kodowanie parametrów zgodnie z tym, jak zdefiniowana jest trasa w ścieżce docelowej.
Generowanie adresów URL dla stron obsługuje nazwy względne. W poniższej tabeli pokazano, która strona indeksu jest wybierana w przypadku użycia różnych parametrów RedirectToPage
w ścieżce Pages/Customers/Create.cshtml
.
RedirectToPage(x) | Strona |
---|---|
RedirectToPage(„/Index”) | Pages/Index |
RedirectToPage(„./Index”); | Pages/Customers/Index |
RedirectToPage(„../Index”) | Pages/Index |
RedirectToPage(„Index”) | Pages/Customers/Index |
RedirectToPage("Index")
, RedirectToPage("./Index")
oraz RedirectToPage("../Index")
to nazwy względne. Parametr RedirectToPage
jest łączony ze ścieżką bieżącej strony w celu obliczenia nazwy strony docelowej.
Łączenie nazw względnych jest przydatne podczas tworzenia stron o złożonej strukturze. Gdy nazwy względne są używane do łączenia stron w folderze:
- Zmiana nazwy folderu nie powoduje przerwania linków względnych.
- Linki nie zostają uszkodzone, ponieważ nie zawierają nazwy folderu.
Aby przekierować do strony w innym obszarze, określ obszar:
RedirectToPage("/Index", new { area = "Services" });
Aby uzyskać więcej informacji, zobacz Obszary na platformie ASP.NET Core i Konwencje tras i aplikacji rozwiązania Razor Pages na platformie ASP.NET Core.
Atrybut ViewData
Dane można przekazać do strony za pomocą polecenia ViewDataAttribute. Wartości właściwości z atrybutem [ViewData]
są przechowywane i ładowane z elementu ViewDataDictionary.
W poniższym przykładzie atrybut AboutModel
stosuje wartość [ViewData]
do właściwości Title
:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na stronie Informacje można uzyskać dostęp do właściwości Title
jako właściwości modelu:
<h1>@Model.Title</h1>
W układzie tytuł jest odczytywany ze słownika ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
Platforma ASP.NET Core uwidocznia właściwość TempData. Ta właściwość przechowuje dane do momentu ich odczytania. Metody Keep i Peek mogą służyć do badania danych bez ich usuwania. Właściwość TempData
przydaje się do przekierowywania, gdy dane są potrzebne w przypadku więcej niż jednego żądania.
Poniższy kod ustawia wartość Message
przy użyciu właściwości TempData
:
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");
}
}
Poniższe znaczniki w pliku Pages/Customers/Index.cshtml
wyświetlają wartość Message
przy użyciu właściwości TempData
.
<h3>Msg: @Model.Message</h3>
Model strony Pages/Customers/Index.cshtml.cs
stosuje atrybut [TempData]
do właściwości Message
.
[TempData]
public string Message { get; set; }
Aby uzyskać więcej informacji, zobacz TempData.
Wiele procedur obsługi na stronę
Na poniższej stronie wygenerowano znaczniki dla dwóch procedur obsługi przy użyciu pomocnika tagów asp-page-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>
Formularz w poprzednim przykładzie zawiera dwa przyciski przesyłania, z których każdy używa elementu FormActionTagHelper
do przesyłania do innego adresu URL. Atrybut asp-page-handler
jest atrybutem towarzyszącym elementu asp-page
. Atrybut asp-page-handler
generuje adresy URL przesyłane do poszczególnych metod procedur obsługi zdefiniowanych przez stronę. Element asp-page
nie jest określony, ponieważ przykład jest połączony z bieżącą stroną.
Model strony:
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();
}
}
}
W powyższym kodzie użyto nazwanych metod procedury obsługi. Nazwane metody procedury obsługi są tworzone przy użyciu tekstu w nazwie po elemencie On<HTTP Verb>
, a przed elementem Async
(jeśli występuje). W poprzednim przykładzie metody stron to OnPostJoinListAsync i OnPostJoinListUCAsync. Po usunięciu metod OnPost i Async nazwy procedury obsługi to JoinList
i JoinListUC
.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Korzystając z poprzedniego kodu, ścieżka adresu URL przesyłana do metody OnPostJoinListAsync
to https://localhost:5001/Customers/CreateFATH?handler=JoinList
. Ścieżka adresu URL przesyłana do metody OnPostJoinListUCAsync
to https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
.
Trasy niestandardowe
Użyj dyrektywy @page
, aby:
- Określić trasę niestandardową do strony. Na przykład trasę do strony Informacje można ustawić na wartość
/Some/Other/Path
za pomocą kodu@page "/Some/Other/Path"
. - Dołączyć segmenty do trasy domyślnej strony. Na przykład segment „Produkt" można dodać do domyślnej trasy strony przy użyciu polecenia
@page "item"
. - Dołączyć parametry do trasy domyślnej strony. Na przykład parametr identyfikatora
id
może być wymagany w przypadku strony z elementem@page "{id}"
.
Obsługiwana jest ścieżka względna katalogu głównego oznaczona tyldą (~
) na początku ścieżki. Na przykład kod @page "~/Some/Other/Path"
jest taki sam jak kod @page "/Some/Other/Path"
.
Jeśli ciąg zapytania ?handler=JoinList
w adresie URL nie jest dla Ciebie odpowiedni, zmień trasę, umieszczając nazwę procedury obsługi w części ścieżki adresu URL. Trasę można dostosowywać poprzez dodanie szablonu trasy w podwójnym cudzysłowie po dyrektywie @page
.
@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>
Korzystając z poprzedniego kodu, ścieżka adresu URL przesyłana do metody OnPostJoinListAsync
to https://localhost:5001/Customers/CreateFATH/JoinList
. Ścieżka adresu URL przesyłana do metody OnPostJoinListUCAsync
to https://localhost:5001/Customers/CreateFATH/JoinListUC
.
Znak ?
następujący po elemencie handler
oznacza, że parametr ścieżki jest opcjonalny.
Kolokacja plików JavaScript (JS)
Kolokacja plików JavaScript (JS) dla stron i widoków jest wygodnym sposobem organizowania skryptów w aplikacji.
Umieść w tej samej lokalizacji pliki JS przy użyciu następujących konwencji rozszerzenia nazwy pliku:
- Strony aplikacji Razor Pages i widoki aplikacji MVC:
.cshtml.js
. Przykłady:- Skrypt
Pages/Index.cshtml.js
dla stronyIndex
aplikacji Razor Pages w lokalizacjiPages/Index.cshtml
. - Skrypt
Views/Home/Index.cshtml.js
dla widokuIndex
aplikacji MVC w lokalizacjiViews/Home/Index.cshtml
.
- Skrypt
Do umieszczonych w tej samej lokalizacji plików JS można odwoływać się przez adres publiczny przy użyciu ścieżki do pliku w projekcie:
Strony i widoki ze stronicowanego pliku skryptów w aplikacji:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js
- Symbol zastępczy
{PATH}
to ścieżka do strony, widoku lub składnika. - Symbol zastępczy
{PAGE, VIEW, OR COMPONENT}
to strona, widok lub składnik. - Symbol zastępczy
{EXTENSION}
jest zgodny z rozszerzeniem strony, widoku lub składnikarazor
albocshtml
.
Przykład produktu Razor Pages:
Plik JS dla strony
Index
jest umieszczany w folderzePages
(Pages/Index.cshtml.js
) obok stronyIndex
(Pages/Index.cshtml
). Na stronieIndex
odwołanie do skryptu występuje w ścieżce w folderzePages
:@section Scripts { <script src="~/Pages/Index.cshtml.js"></script> }
- Symbol zastępczy
Układ domyślny Pages/Shared/_Layout.cshtml
można skonfigurować tak, aby uwzględniał stronicowane JS pliki, eliminując konieczność indywidualnego skonfigurowania każdej strony:
<script asp-src-include="@(ViewContext.View.Path).js"></script>
W przykładowym pobraniu użyto poprzedniego fragmentu kodu w celu uwzględnienia s collocated JS plików w układzie domyślnym.
Po opublikowaniu aplikacji struktura automatycznie przenosi skrypt do katalogu głównego sieci Web. W poprzednim przykładzie skrypt jest przenoszony do lokalizacji bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Pages\Index.cshtml.js
, gdzie symbol zastępczy {TARGET FRAMEWORK MONIKER}
to moniker platformy docelowej. Na stronie Index
nie jest wymagana żadna zmiana względnego adresu URL skryptu.
Po opublikowaniu aplikacji struktura automatycznie przenosi skrypt do katalogu głównego sieci Web. W poprzednim przykładzie skrypt jest przenoszony do lokalizacji bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Components\Pages\Index.razor.js
, gdzie symbol zastępczy {TARGET FRAMEWORK MONIKER}
to moniker platformy docelowej. W składniku Index
nie jest wymagana żadna zmiana względnego adresu URL skryptu.
W przypadku skryptów dostarczanych przez bibliotekę klas Razor (RCL):
_content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js
- Symbol zastępczy
{PACKAGE ID}
to identyfikator pakietu biblioteki RCL (lub nazwa biblioteki dla biblioteki klas, do której odwołuje się aplikacja). - Symbol zastępczy
{PATH}
to ścieżka do strony, widoku lub składnika. Jeśli składnik Razor znajduje się w katalogu głównym biblioteki RCL, segment ścieżki nie jest uwzględniany. - Symbol zastępczy
{PAGE, VIEW, OR COMPONENT}
to strona, widok lub składnik. - Symbol zastępczy
{EXTENSION}
jest zgodny z rozszerzeniem strony, widoku lub składnikarazor
albocshtml
.
- Symbol zastępczy
Zaawansowane ustawienia i konfiguracja
Konfiguracja i ustawienia w poniższych sekcjach nie są wymagane przez większość aplikacji.
Aby skonfigurować opcje zaawansowane, użyj przeciążenia AddRazorPages, które konfiguruje klasę RazorPagesOptions:
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();
Użyj klasy RazorPagesOptions, aby ustawić katalog główny dla stron lub dodać konwencje modeli aplikacji dla stron. Aby uzyskać więcej informacji na temat konwencji, zobacz Konwencje autoryzacji rozwiązania Razor Pages.
Aby wstępnie skompilować widoki, zobacz Kompilowanie widoków Razor.
Określanie, że strony Razor znajdują się w katalogu głównym zawartości
Domyślnie strony Razor znajdują się w katalogu /Pages. Dodaj metodę WithRazorPagesAtContentRoot, aby określić, że strony Razor znajdują się w katalogu głównym zawartości (ContentRootPath) aplikacji:
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();
Określanie, że strony Razor znajdują się w niestandardowym katalogu głównym
Dodaj metodę WithRazorPagesRoot, aby określić, że strony Razor znajdują się w niestandardowym katalogu głównym w aplikacji (podaj ścieżkę względną):
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();
Dodatkowe zasoby
- Zobacz temat Wprowadzenie do rozwiązania Razor Pages, który stanowi kontynuację tego wprowadzenia.
- Atrybut Authorize i strony Razor
- Pobieranie i wyświetlanie kodu przykładowego
- Omówienie platformy ASP.NET Core
- Dokumentacja składni aparatu Razor dla platformy ASP.NET Core
- Obszary na platformie ASP.NET Core
- Samouczek: rozpoczynanie pracy z rozwiązaniem Razor Pages na platformie ASP.NET Core
- Konwencje autoryzacji rozwiązania Razor Pages na platformie ASP.NET Core
- Konwencje tras i aplikacji rozwiązania Razor Pages na platformie ASP.NET Core
- Testy jednostkowe rozwiązania Razor Pages na platformie ASP.NET Core
- Widoki częściowe na platformie ASP.NET Core
- Program Visual Studio 2019 16.4 lub nowsza wersja z pakietem roboczym tworzenia aplikacji ASP.NET. i aplikacji internetowych
- Zestaw .NET Core SDK 3.1
- Program Visual Studio 2019 16.8 lub nowsza wersja z pakietem roboczym tworzenia aplikacji ASP.NET. i aplikacji internetowych
- Zestaw .NET SDK 5.0
Tworzenie projektu rozwiązania Razor Pages
Zobacz temat Wprowadzenie do rozwiązania Razor Pages, aby uzyskać szczegółowe instrukcje dotyczące tworzenia projektu za pomocą rozwiązania Razor Pages.
Razor Pages
Rozwiązanie Razor Pages można włączyć w pliku Startup.cs
:
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();
});
}
}
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
Powyższy kod w dużym stopniu przypomina plik widoku Razor używany w aplikacji platformy ASP.NET Core z kontrolerami i widokami. Różni się dyrektywą @page
. Dyrektywa @page
zamienia plik w działanie MVC, co oznacza, że obsługuje żądania bezpośrednio, bez udziału kontrolera. Dyrektywa @page
musi być pierwszą dyrektywą Razor na stronie. Dyrektywa @page
wpływa na działanie innych konstrukcji Razor. Nazwy plików rozwiązania Razor Pages mają sufiks .cshtml
.
Podobna strona, korzystająca z klasy PageModel
, jest pokazana w następujących dwóch plikach. Plik Pages/Index2.cshtml
:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Model strony Pages/Index2.cshtml.cs
:
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 }";
}
}
}
Zgodnie z konwencją plik klasy PageModel
ma taką samą nazwę jak plik strony Razor z dodanym rozszerzeniem .cs
. Na przykład poprzednia strona Razor to Pages/Index2.cshtml
. Plik zawierający klasę PageModel
nosi nazwę Pages/Index2.cshtml.cs
.
Skojarzenia ścieżek URL ze stronami są określane przez lokalizację strony w systemie plików. W poniższej tabeli przedstawiono ścieżkę strony Razor i odpowiadający jej adres URL:
Nazwa i ścieżka pliku | Odpowiedni adres URL |
---|---|
/Pages/Index.cshtml |
/ lub /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store lub /Store/Index |
Uwagi:
- Środowisko uruchomieniowe domyślnie wyszukuje pliki rozwiązania Razor Pages w folderze Pages.
Index
to strona domyślna, jeśli adres URL nie zawiera strony.
Pisanie podstawowego formularza
Rozwiązanie Razor Pages zostało stworzone, aby ułatwić wdrożenie typowych wzorców używanych w przeglądarkach internetowych podczas tworzenia aplikacji. Powiązanie modelu, pomocnicy tagów i pomocnicy HTML działają przy użyciu właściwości zdefiniowanych w klasie strony Razor. Rozważ stronę, w której wdrożony będzie podstawowy formularz kontaktowy w przypadku modelu Contact
:
W przykładach kodu w tym dokumencie element DbContext
jest inicjowany w pliku Startup.cs..
Baza danych w pamięci wymaga pakietu NuGet Microsoft.EntityFrameworkCore.InMemory
.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
services.AddRazorPages();
}
Model danych:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
Kontekst bazy danych:
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; }
}
}
Plik widoku 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>
Model strony 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");
}
}
}
Zgodnie z konwencją klasa PageModel
nazywa się <PageName>Model
i znajduje się w tej samej przestrzeni nazw co strona.
Klasa PageModel
umożliwia oddzielenie logiki strony od jej prezentacji. Definiuje procedury obsługi stron dla żądań wysyłanych do strony oraz dla danych używanych do renderowania strony. To rozdzielenie umożliwia:
- Zarządzanie zależnościami stron za pomocą wstrzykiwania zależności.
- Testowanie jednostek
Strona ma metodę OnPostAsync
obsługi, która jest uruchamiana na POST
żądaniach (gdy użytkownik publikuje formularz). Można dodawać metody procedury obsługi dla dowolnego czasownika HTTP. Najczęstsze procedury obsługi to:
OnGet
— na potrzeby inicjowania stanu wymaganego dla strony. W poprzednim kodzie metodaOnGet
spowodowała wyświetlenie stronyCreateModel.cshtml
Razor.OnPost
— na potrzeby obsługi przesłanych formularzy.
Sufiks nazewnictwa Async
jest opcjonalny, ale jest często używany zgodnie z konwencją na potrzeby funkcji asynchronicznych. Powyższy kod jest typowy dla rozwiązania Razor Pages.
Jeśli znasz aplikacje ASP.NET korzystające z kontrolerów i widoków:
- Kod
OnPostAsync
w poprzednim przykładzie wygląda podobnie do typowego kodu kontrolera. - Większość typów pierwotnych MVC, takich jak powiązanie modelu, walidacja i wyniki akcji działają tak samo w przypadku kontrolerów i rozwiązania Razor Pages.
Poprzednia metoda OnPostAsync
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Podstawowy przepływ metody OnPostAsync
:
Sprawdź, czy wystąpiły błędy walidacji.
- Jeśli nie ma żadnych błędów, zapisz dane i przeprowadź przekierowanie.
- Jeśli wystąpią jakieś błędy, pokaż stronę ponownie z komunikatami walidacji. W wielu przypadkach błędy weryfikacji zostaną wykryte w kliencie i nigdy nie zostaną przesłane do serwera.
Plik widoku 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>
Renderowany kod HTML z pliku Pages/Create.cshtml
:
<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>
W poprzednim kodzie w przypadku opublikowania formularza:
Z prawidłowymi danymi:
Metoda procedury obsługi
OnPostAsync
wywołuje metodę pomocnika RedirectToPage. MetodaRedirectToPage
zwraca wystąpienie klasy RedirectToPageResult.RedirectToPage
:- Jest wynikiem akcji.
- Przypomina metodę
RedirectToAction
lubRedirectToRoute
(używaną w kontrolerach i widokach). - Jest dostosowana pod kątem stron. W poprzednim przykładzie nastąpiło przekierowanie do głównej strony indeksu (
/Index
). MetodaRedirectToPage
jest opisana szczegółowo w sekcji dotyczącej generowania adresu URL w rozwiązaniu Pages.
Z błędami walidacji, które są przekazywane do serwera:
- Metoda procedury obsługi
OnPostAsync
wywołuje metodę pomocnika Page. MetodaPage
zwraca wystąpienie klasy PageResult. Zwrócenie elementuPage
wygląda podobnie jak zwrócenie elementuView
przez akcje w kontrolerach. ElementPageResult
jest domyślnym zwracanym typem w przypadku metody procedury obsługi. Metoda procedury obsługi, która zwraca wartośćvoid
, renderuje stronę. - W poprzednim przykładzie opublikowanie formularza bez żadnych wartości spowodowało, że właściwość ModelState.IsValid zwróciła wartość false. W tym przykładzie po stronie klienta nie są wyświetlane żadne błędy walidacji. Obsługa błędów walidacji jest omówiona w dalszej części tego dokumentu.
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _context.Customers.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
- Metoda procedury obsługi
W przypadku wykrycia błędów walidacji po stronie klienta:
- Dane nie są publikowane na serwerze.
- Walidacja po stronie klienta zostanie wyjaśniona w dalszej części tego dokumentu.
Do wyrażenia zgody na powiązanie modelu właściwość Customer
wykorzystuje atrybut [BindProperty]
:
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");
}
}
Właściwość [BindProperty]
nie powinna być używana w modelach zawierających właściwości, które nie powinny być zmieniane przez klienta. Aby uzyskać więcej informacji, zobacz Przesyłanie dodatkowych danych.
Rozwiązanie Razor Pages domyślnie wiąże właściwości wyłącznie z czasownikami innymi niż GET
. Powiązanie z właściwościami eliminuje konieczność pisania kodu w celu przekonwertowania danych HTTP na typ modelu. Powiązanie redukuje kod poprzez użycie tej samej właściwości do renderowania pól formularza (<input asp-for="Customer.Name">
) i akceptowania danych wejściowych.
Ostrzeżenie
Ze względów bezpieczeństwa należy wyrazić zgodę na powiązanie danych żądania GET
z właściwościami modelu strony. Przed mapowaniem danych wejściowych użytkownika na właściwości zweryfikuj je. Wyrażenie zgody na powiązanie danych GET
jest przydatne w scenariuszach, które wykorzystują ciągi zapytania lub wartości trasy.
Aby powiązać właściwość w przypadku żądań GET
, ustaw właściwość SupportsGet
atrybutu [BindProperty]
na true
:
[BindProperty(SupportsGet = true)]
Aby uzyskać więcej informacji, zobacz Podsumowanie ASP.NET Core Community Standup: dyskusja na temat powiązania z użyciem metody GET (YouTube).
Przegląd pliku widoku 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>
- W poprzednim kodzie pomocnik
<input asp-for="Customer.Name" />
tagu wejściowego wiąże element HTML<input>
z wyrażeniemCustomer.Name
modelu. - Dyrektywa
@addTagHelper
udostępnia pomocników tagów.
Strona home
Index.cshtml
home to strona:
@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>
Powiązana klasa PageModel
(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();
}
}
Plik Index.cshtml
zawiera następujące znaczniki:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<a /a>
Pomocnik tagu zakotwiczenia użył atrybutu asp-route-{value}
do wygenerowania linku do strony Edytuj. Link zawiera dane trasy z identyfikatorem kontaktu. Na przykład https://localhost:5001/Edit/1
. Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
Plik Index.cshtml
zawiera znaczniki umożliwiające tworzenie przycisku usuwania dla każdego kontaktu klienta:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
Renderowany kod HTML:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Gdy przycisk usuwania jest renderowany w formacie HTML, jego akcja formularza zawiera następujące parametry:
- Identyfikator kontaktu klienta określony przez atrybut
asp-route-id
. - Element
handler
określony przez atrybutasp-page-handler
.
Po wybraniu przycisku żądanie formularza POST
jest wysyłane do serwera. Zgodnie z konwencją nazwa metody procedury obsługi jest wybierana na podstawie wartości parametru handler
zgodnie ze schematem OnPost[handler]Async
.
Ponieważ procedura obsługi handler
w tym przykładzie to delete
, metoda procedury obsługi OnPostDeleteAsync
jest używana do przetworzenia żądania POST
. Jeśli procedura obsługi asp-page-handler
zostanie ustawiona na inną wartość, na przykład remove
, zostanie wybrana metoda procedury obsługi o nazwie OnPostRemoveAsync
.
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();
}
Metoda OnPostDeleteAsync
:
- Pobiera identyfikator
id
z ciągu zapytania. - Tworzy zapytanie w bazie danych dotyczące kontaktu klienta przy użyciu metody
FindAsync
. - Jeśli kontakt klienta zostanie odnaleziony, zostaje usunięty, a baza danych zostaje zaktualizowana.
- Tworzy wywołanie RedirectToPage w celu przekierowania do głównej strony indeksu (
/Index
).
Plik Edit.cshtml file
@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>
Pierwszy wiersz zawiera dyrektywę @page "{id:int}"
. Ograniczenie routingu "{id:int}"
instruuje stronę, aby akceptowała żądania do strony zawierające dane trasy int
. Jeśli żądanie do strony nie zawiera danych trasy, które można przekonwertować na element int
, środowisko uruchomieniowe zwraca kod błędu HTTP 404 (nie znaleziono). Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Plik Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly CustomerDbContext _context;
public EditModel(CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Customer = await _context.Customers.FindAsync(id);
if (Customer == null)
{
return RedirectToPage("./Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception($"Customer {Customer.Id} not found!");
}
return RedirectToPage("./Index");
}
}
Walidacja
Reguły walidacji:
- Są deklaratywnie określone w klasie modelu.
- Są wymuszane w każdym miejscu w aplikacji.
Przestrzeń nazw System.ComponentModel.DataAnnotations zapewnia zestaw wbudowanych atrybutów walidacji, które są stosowane deklaratywnie wobec klasy lub właściwości. Przestrzeń nazw DataAnnotations zawiera również atrybuty formatowania, takie jak [DataType]
, które pomagają w formatowaniu i nie zapewniają żadnej walidacji.
Rozważmy model Customer
:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
W przypadku użycia następującego pliku widoku Create.cshtml
:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Powyższy kod ma następujące działanie:
Obejmuje skrypty walidacji jQuery i jQuery.
Używa pomocników tagów
<div />
i<span />
do włączenia:- Walidacji po stronie klienta.
- Renderowania błędów walidacji.
Generuje następujący kod HTML:
<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>
Opublikowanie formularza tworzenia bez wartości nazwy powoduje wyświetlenie komunikatu o błędzie „Pole nazwy jest wymagane” w formularzu. Jeśli po stronie klienta włączono język JavaScript, błąd zostanie wyświetlony w przeglądarce bez publikowania na serwerze.
Atrybut [StringLength(10)]
generuje element data-val-length-max="10"
w renderowanym kodzie HTML. Element data-val-length-max
uniemożliwia przeglądarkom wprowadzanie większej liczby znaków niż określono. Jeśli do edytowania i powtarzania publikowania używane jest narzędzie takie jak Fiddler:
- W przypadku nazwy dłuższej niż 10 znaków.
- Zwracany jest komunikat o błędzie „Pole Nazwa musi być ciągiem o maksymalnej długości 10 znaków”.
Rozważ następujący model Movie
:
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; }
}
}
Atrybuty walidacji określają działanie wymuszane wobec właściwości modelu, do których są stosowane:
Atrybuty
Required
iMinimumLength
wskazują, że właściwość musi mieć wartość, ale nic nie uniemożliwia użytkownikowi wprowadzania białych znaków w celu spełnienia zasad tej walidacji.Atrybut
RegularExpression
służy do ograniczania liczby znaków, które mogą być wprowadzane jako dane wejściowe. W poprzednim kodzie w polu „Genre” (Gatunek):- Muszą być używane tylko litery.
- Pierwsza litera musi być wielką literą. Białe znaki, liczby i znaki specjalne nie są dozwolone.
W atrybucie
RegularExpression
„Rating” (Klasyfikacja):- Pierwszy znak musi być wielką literą.
- Kolejne znaki mogą być znakami specjalnymi i cyframi. W atrybucie Rating wartość „PG-13” jest prawidłowa, ale w przypadku atrybutu Genre — nie.
Atrybut
Range
ogranicza wartość do określonego zakresu.Atrybut
StringLength
wyznacza maksymalną długość właściwości ciągu oraz opcjonalnie jego minimalną długość.Typy wartości (takie jak
decimal
,int
,float
,DateTime
) są z natury wymagane i nie wymagają atrybutu[Required]
.
Na stronie tworzenia w przypadku modelu Movie
wyświetlane są błędy z nieprawidłowymi wartościami:
Aby uzyskać więcej informacji, zobacz:
Obsługa żądań HEAD za pomocą elementu fallback procedury obsługi OnGet
Żądania HEAD
umożliwiają pobieranie nagłówków dla określonego zasobu. W przeciwieństwie do żądań GET
żądania HEAD
nie zwracają treści odpowiedzi.
Zwykle procedura obsługi OnHead
jest tworzona i wywoływana pod kątem żądań HEAD
:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Rozwiązanie Razor Pages korzysta z wywołania procedury obsługi OnGet
rezerwowo, jeśli procedura obsługi OnHead
została zdefiniowana.
XSRF/CSRF i rozwiązanie Razor Pages
RozwiązanieRazor Pages jest chronione przez walidację chroniącą przed fałszerstwem. Element FormTagHelper wprowadza tokeny chroniące przed fałszerstwem do elementów formularza HTML.
Korzystanie z układów, stron częściowych, szablonów i pomocników tagu w rozwiązaniu Razor Pages
Strony działają ze wszystkimi funkcjami aparatu widoku Razor. Układy, strony częściowe, szablony, pomocnicy tagów oraz pliki _ViewStart.cshtml
i _ViewImports.cshtml
działają w taki sam sposób, jak w przypadku konwencjonalnych widoków rozwiązania Razor.
Uporządkujmy tę stronę, wykorzystując niektóre z tych funkcji.
Dodaj stronę układu do pliku Pages/Shared/_Layout.cshtml
:
<!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>
- Steruje układem każdej strony (chyba, że strona jest wyłączona z układu).
- Importuje struktury HTML, takie jak JavaScript i arkusze stylów.
- Zawartość strony Razor jest renderowana, tam gdzie jest wywoływana dyrektywa
@RenderBody()
.
Aby uzyskać więcej informacji, zobacz Strona układu.
Właściwość Layout jest ustawiona w pliku Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
Układ znajduje się w folderze Pages/Shared. Strony szukają innych widoków (układów, szablonów, stron częściowych) hierarchicznie, zaczynając od tego samego folderu, w którym znajduje się bieżąca strona. Układ w folderze Pages/Shared może być używany z poziomu dowolnej strony Razor w folderze Pages.
Plik układu powinien znajdować się w folderze Pages/Shared.
Nie zalecamy umieszczania pliku układu w folderze Views/Shared. Folder Views/Shared jest zgodny ze wzorcem widoków MVC. Rozwiązanie Razor Pages ma wykorzystywać hierarchię folderów, a nie konwencje ścieżek.
Wyszukiwanie widoków z poziomu rozwiązania Razor Pages obejmuje folder Pages. Układy, szablony i strony częściowe używane w przypadku kontrolerów MVC i konwencjonalnych widoków Razor po prostu działają.
Dodaj plik Pages/_ViewImports.cshtml
:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Dyrektywa @namespace
jest omówiona w dalszej części samouczka. Dyrektywa @addTagHelper
wprowadza wbudowanych pomocników tagów na wszystkich stronach w folderze Pages.
Dyrektywa @namespace
ustawiona na stronie:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
Dyrektywa @namespace
ustawia przestrzeń nazw dla strony. Dyrektywa @model
nie musi zawierać przestrzeni nazw.
Gdy dyrektywa @namespace
jest zawarta w pliku _ViewImports.cshtml
, określona przestrzeń nazw dostarcza prefiks dla wygenerowanej przestrzeni nazw na stronie, która importuje dyrektywę @namespace
. Wygenerowana rest przestrzeń nazw (część sufiksu) to ścieżka względna rozdzielona kropką między folderem zawierającym _ViewImports.cshtml
i folderem zawierającym stronę.
Na przykład klasa PageModel
w pliku Pages/Customers/Edit.cshtml.cs
jawnie konfiguruje przestrzeń nazw:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Plik Pages/_ViewImports.cshtml
konfiguruje następującą przestrzeń nazw:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Wygenerowana przestrzeń nazw dla strony Razor Pages/Customers/Edit.cshtml
jest taka sama jak klasa PageModel
.
@namespace
współpracuje również z konwencjonalnymi Razor widokami.
Rozważ plik widoku Pages/Create.cshtml
:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Zaktualizowany plik widoku Pages/Create.cshtml
z plikiem _ViewImports.cshtml
i poprzednim plikiem układu:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
W poprzednim kodzie plik _ViewImports.cshtml
służył do zaimportowania przestrzeni nazw i pomocników tagów. Plik układu zaimportował pliki JavaScript.
Projekt początkowy rozwiązania Razor Pages zawiera plik Pages/_ValidationScriptsPartial.cshtml
, który podłącza walidację po stronie klienta.
Aby uzyskać więcej informacji na temat widoków częściowych, zobacz Widoki częściowe na platformie ASP.NET Core.
Generowanie adresów URL dla stron
Na wcześniej wyświetlonej stronie Create
użyto metody 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");
}
}
Aplikacja ma następującą strukturę plików/folderów:
/Pages
Index.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.cshtml
Jeśli proces przebiega pomyślnie, strony Pages/Customers/Create.cshtml
i Pages/Customers/Edit.cshtml
przekierowują do pliku Pages/Customers/Index.cshtml
. Ciąg ./Index
to względna nazwa strony używana do uzyskiwania dostępu do poprzedniej strony. Służy do generowania adresów URL strony Pages/Customers/Index.cshtml
. Na przykład:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
Bezwzględna nazwa strony /Index
jest używana do generowania adresów URL strony Pages/Index.cshtml
. Na przykład:
Url.Page("/Index", ...)
<a asp-page="/Index">Home Index Page</a>
RedirectToPage("/Index")
Nazwa strony to ścieżka prowadząca do tej strony z folderu głównego /Pages, zawierająca element rozpoczynający /
(na przykład /Index
). Poprzednie przykłady generowania adresów URL oferują rozszerzone opcje i funkcje w porównaniu z trwałym kodowaniem adresu URL. W trakcie generowania adresu URL wykorzystywany jest routing, co umożliwia generowanie i kodowanie parametrów zgodnie z tym, jak zdefiniowana jest trasa w ścieżce docelowej.
Generowanie adresów URL dla stron obsługuje nazwy względne. W poniższej tabeli pokazano, która strona indeksu jest wybierana w przypadku użycia różnych parametrów RedirectToPage
w ścieżce Pages/Customers/Create.cshtml
.
RedirectToPage(x) | Strona |
---|---|
RedirectToPage(„/Index”) | Pages/Index |
RedirectToPage(„./Index”); | Pages/Customers/Index |
RedirectToPage(„../Index”) | Pages/Index |
RedirectToPage(„Index”) | Pages/Customers/Index |
RedirectToPage("Index")
, RedirectToPage("./Index")
oraz RedirectToPage("../Index")
to nazwy względne. Parametr RedirectToPage
jest łączony ze ścieżką bieżącej strony w celu obliczenia nazwy strony docelowej.
Łączenie nazw względnych jest przydatne podczas tworzenia stron o złożonej strukturze. Gdy nazwy względne są używane do łączenia stron w folderze:
- Zmiana nazwy folderu nie powoduje przerwania linków względnych.
- Linki nie zostają uszkodzone, ponieważ nie zawierają nazwy folderu.
Aby przekierować do strony w innym obszarze, określ obszar:
RedirectToPage("/Index", new { area = "Services" });
Aby uzyskać więcej informacji, zobacz Obszary na platformie ASP.NET Core i Konwencje tras i aplikacji rozwiązania Razor Pages na platformie ASP.NET Core.
Atrybut ViewData
Dane można przekazać do strony za pomocą polecenia ViewDataAttribute. Wartości właściwości z atrybutem [ViewData]
są przechowywane i ładowane z elementu ViewDataDictionary.
W poniższym przykładzie atrybut AboutModel
stosuje wartość [ViewData]
do właściwości Title
:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na stronie Informacje można uzyskać dostęp do właściwości Title
jako właściwości modelu:
<h1>@Model.Title</h1>
W układzie tytuł jest odczytywany ze słownika ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
Platforma ASP.NET Core uwidocznia właściwość TempData. Ta właściwość przechowuje dane do momentu ich odczytania. Metody Keep i Peek mogą służyć do badania danych bez ich usuwania. Właściwość TempData
przydaje się do przekierowywania, gdy dane są potrzebne w przypadku więcej niż jednego żądania.
Poniższy kod ustawia wartość Message
przy użyciu właściwości TempData
:
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");
}
}
Poniższe znaczniki w pliku Pages/Customers/Index.cshtml
wyświetlają wartość Message
przy użyciu właściwości TempData
.
<h3>Msg: @Model.Message</h3>
Model strony Pages/Customers/Index.cshtml.cs
stosuje atrybut [TempData]
do właściwości Message
.
[TempData]
public string Message { get; set; }
Aby uzyskać więcej informacji, zobacz TempData.
Wiele procedur obsługi na stronę
Na poniższej stronie wygenerowano znaczniki dla dwóch procedur obsługi przy użyciu pomocnika tagów asp-page-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>
Formularz w poprzednim przykładzie zawiera dwa przyciski przesyłania, z których każdy używa elementu FormActionTagHelper
do przesyłania do innego adresu URL. Atrybut asp-page-handler
jest atrybutem towarzyszącym elementu asp-page
. Atrybut asp-page-handler
generuje adresy URL przesyłane do poszczególnych metod procedur obsługi zdefiniowanych przez stronę. Element asp-page
nie jest określony, ponieważ przykład jest połączony z bieżącą stroną.
Model strony:
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();
}
}
}
W powyższym kodzie użyto nazwanych metod procedury obsługi. Nazwane metody procedury obsługi są tworzone przy użyciu tekstu w nazwie po elemencie On<HTTP Verb>
, a przed elementem Async
(jeśli występuje). W poprzednim przykładzie metody stron to OnPostJoinListAsync i OnPostJoinListUCAsync. Po usunięciu metod OnPost i Async nazwy procedury obsługi to JoinList
i JoinListUC
.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Korzystając z poprzedniego kodu, ścieżka adresu URL przesyłana do metody OnPostJoinListAsync
to https://localhost:5001/Customers/CreateFATH?handler=JoinList
. Ścieżka adresu URL przesyłana do metody OnPostJoinListUCAsync
to https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
.
Trasy niestandardowe
Użyj dyrektywy @page
, aby:
- Określić trasę niestandardową do strony. Na przykład trasę do strony Informacje można ustawić na wartość
/Some/Other/Path
za pomocą kodu@page "/Some/Other/Path"
. - Dołączyć segmenty do trasy domyślnej strony. Na przykład segment „Produkt" można dodać do domyślnej trasy strony przy użyciu polecenia
@page "item"
. - Dołączyć parametry do trasy domyślnej strony. Na przykład parametr identyfikatora
id
może być wymagany w przypadku strony z elementem@page "{id}"
.
Obsługiwana jest ścieżka względna katalogu głównego oznaczona tyldą (~
) na początku ścieżki. Na przykład kod @page "~/Some/Other/Path"
jest taki sam jak kod @page "/Some/Other/Path"
.
Jeśli ciąg zapytania ?handler=JoinList
w adresie URL nie jest dla Ciebie odpowiedni, zmień trasę, umieszczając nazwę procedury obsługi w części ścieżki adresu URL. Trasę można dostosowywać poprzez dodanie szablonu trasy w podwójnym cudzysłowie po dyrektywie @page
.
@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>
Korzystając z poprzedniego kodu, ścieżka adresu URL przesyłana do metody OnPostJoinListAsync
to https://localhost:5001/Customers/CreateFATH/JoinList
. Ścieżka adresu URL przesyłana do metody OnPostJoinListUCAsync
to https://localhost:5001/Customers/CreateFATH/JoinListUC
.
Znak ?
następujący po elemencie handler
oznacza, że parametr ścieżki jest opcjonalny.
Zaawansowane ustawienia i konfiguracja
Konfiguracja i ustawienia w poniższych sekcjach nie są wymagane przez większość aplikacji.
Aby skonfigurować opcje zaawansowane, użyj przeciążenia AddRazorPages, które konfiguruje klasę RazorPagesOptions:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
Użyj klasy RazorPagesOptions, aby ustawić katalog główny dla stron lub dodać konwencje modeli aplikacji dla stron. Aby uzyskać więcej informacji na temat konwencji, zobacz Konwencje autoryzacji rozwiązania Razor Pages.
Aby wstępnie skompilować widoki, zobacz Kompilowanie widoków Razor.
Określanie, że strony Razor znajdują się w katalogu głównym zawartości
Domyślnie strony Razor znajdują się w katalogu /Pages. Dodaj metodę WithRazorPagesAtContentRoot, aby określić, że strony Razor znajdują się w katalogu głównym zawartości (ContentRootPath) aplikacji:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
Określanie, że strony Razor znajdują się w niestandardowym katalogu głównym
Dodaj metodę WithRazorPagesRoot, aby określić, że strony Razor znajdują się w niestandardowym katalogu głównym w aplikacji (podaj ścieżkę względną):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
}
Dodatkowe zasoby
- Zobacz temat Wprowadzenie do rozwiązania Razor Pages, który stanowi kontynuację tego wprowadzenia.
- Atrybut Authorize i strony Razor
- Pobieranie i wyświetlanie kodu przykładowego
- Omówienie platformy ASP.NET Core
- Dokumentacja składni aparatu Razor dla platformy ASP.NET Core
- Obszary na platformie ASP.NET Core
- Samouczek: rozpoczynanie pracy z rozwiązaniem Razor Pages na platformie ASP.NET Core
- Konwencje autoryzacji rozwiązania Razor Pages na platformie ASP.NET Core
- Konwencje tras i aplikacji rozwiązania Razor Pages na platformie ASP.NET Core
- Testy jednostkowe rozwiązania Razor Pages na platformie ASP.NET Core
- Widoki częściowe na platformie ASP.NET Core