Dela via


Introduktion till Razor sidor i ASP.NET Core

Av Rick Anderson, Dave Brock och Kirk Larkin

Anmärkning

Det här är inte den senaste versionen av den här artikeln. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Varning

Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i supportpolicyn för .NET och .NET Core. För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Viktigt!

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

För den nuvarande utgåvan, se .NET 9-versionen av den här artikeln .

Razor Sidor kan göra det enklare och mer produktivt att koda sidfokuserade scenarier än att använda kontrollanter och vyer.

Om du letar efter en självstudiekurs som använder metoden Modell–View-Controller kan du läsa Komma igång med ASP.NET Core MVC.

Det här dokumentet ger en introduktion till Razor Sidor. Det är inte en steg för steg-handledning. Om du tycker att vissa av avsnitten är för avancerade kan du läsa Komma igång med Razor Sidor. En översikt över ASP.NET Core finns i Introduktion till ASP.NET Core.

Förutsättningar

Skapa ett Razor pages-projekt

Mer information om hur du skapar ett Razor pages-projekt finns .

Razor sidor

Razor Sidor är aktiverade i 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();

I koden ovan:

Överväg en grundläggande sida:

@page

<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>

Föregående kod ser ut ungefär som en Razor vyfil som används i en ASP.NET Core-app med kontrollanter och vyer. Det som gör det @page annorlunda är direktivet. @page gör filen till en MVC-åtgärd, vilket innebär att den hanterar begäranden direkt, utan att gå igenom en kontrollant. @page måste vara det första Razor direktivet på en sida. @page påverkar beteendet hos andra Razor konstruktioner. Razor Sidfilnamn har ett .cshtml suffix.

En liknande sida, med hjälp av en PageModel klass, visas i följande två filer. Filen Pages/Index2.cshtml:

@page
@using RazorPagesIntro.Pages
@model Index2Model

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

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

Enligt konvention har PageModel-klassfilen samma namn som Razor-sidfilen med .cs bifogat. Till exempel är Razorföregående Pages/Index2.cshtml sida . Filen som innehåller PageModel klassen heter Pages/Index2.cshtml.cs.

Associationerna för URL-sökvägar till sidor bestäms av sidans plats i filsystemet. I följande tabell visas en Razor sidsökväg och matchande URL:

Filnamn och sökväg matchande URL
/Pages/Index.cshtml / eller /Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store eller /Store/Index

Anteckningar:

  • Körningen söker Razor efter Pages-filer i mappen Pages som standard.
  • Index är standardsidan när en URL inte innehåller en sida.

Skriva ett grundläggande formulär

Razor Sidor är utformade för att göra vanliga mönster som används med webbläsare enkla att implementera när du skapar en app. Modellbindning, Tag Helpers och HTML-hjälpfunktioner fungerar med de egenskaper som definierats i en Razor sidklass. Överväg en sida som implementerar ett grundläggande "kontakta oss"-formulär för Contact modellen:

För exemplen i det här dokumentet initieras DbContext i Program.cs.

För minnesdatabasen krävs NuGet-paketet 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();

Datamodellen:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string? Name { get; set; }
    }
}

Databas-kontexten:

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

Filen för vy 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>

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

Enligt konvention benämns PageModel-klassen <PageName>Model och finns i samma namnområde som sidan.

Klassen PageModel tillåter separation av logiken för en sida från presentationen. Den definierar sidhanterare för begäranden som skickas till sidan och de data som används för att återge sidan. Den här separationen tillåter:

Sidan har en OnPostAsynchanteringsmetod som körs på POST begäranden (när en användare publicerar formuläret). Du kan lägga till hanteringsmetoder för alla HTTP-verb. De vanligaste hanterarna är:

  • OnGet för att initiera det tillstånd som behövs för sidan. I den föregående koden visar OnGet-metoden Create.cshtmlRazor-sidan.
  • OnPost för att hantera formulärinlämningar.

Namngivningssuffixet Async är valfritt men används ofta av konventionen för asynkrona funktioner. Föregående kod är typisk för Razor Sidor.

Om du är bekant med ASP.NET-applikationer med kontroller och vyer:

  • Koden OnPostAsync i föregående exempel ser ut ungefär som vanlig styrenhetskod.
  • De flesta av MVC-primitiverna som modellbindning, validering och åtgärdsresultat fungerar på samma sätt med kontrollanter och Razor sidor.

Föregående OnPostAsync metod:

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

Det grundläggande flödet av OnPostAsync:

Sök efter verifieringsfel.

  • Om det inte finns några fel sparar du data och omdirigerar.
  • Om det finns fel visar du sidan igen med valideringsmeddelanden. I många fall identifieras verifieringsfel på klienten och skickas aldrig till servern.

Filen för vy 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>

Den renderade HTML-koden från 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>

I föregående kod publicerar du formuläret:

  • Med giltiga data:

    • Hanteringsmetoden OnPostAsync anropar RedirectToPage hjälpmetoden. RedirectToPage returnerar en instans av RedirectToPageResult. RedirectToPage:

      • Är ett åtgärdsresultat.
      • Liknar RedirectToAction eller RedirectToRoute (används i kontrollanter och vyer).
      • Är anpassad för sidor. I föregående exempel omdirigeras den till rotindexsidan (/Index). RedirectToPage beskrivs i avsnittet URL-generering för sidor .
  • Med valideringsfel som skickas till servern:

    • Hanteringsmetoden OnPostAsync anropar Page hjälpmetoden. Page returnerar en instans av PageResult. Att returnera Page är likt hur åtgärder i kontroller returnerar View. PageResult är standardreturtypen för en hanteringsmetod. En hanteringsmetod som returnerar void återger sidan.
    • I föregående exempel resulterar publicering av formuläret utan värde i ModelState.IsValid som returnerar false. I det här exemplet visas inga verifieringsfel på klienten. Verifieringsfelhantering beskrivs senare i det här dokumentet.
    [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");
    }
    
  • Med valideringsfel som identifierats av verifiering på klientsidan:

    • Data publiceras inte på servern.
    • Validering på klientsidan förklaras senare i det här dokumentet.

Egenskapen Customer använder [BindProperty] attributet för att välja modellbindning:

[BindProperty]
public Customer? Customer { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    if (Customer != null) _context.Customer.Add(Customer);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

[BindProperty] ska inte användas på modeller som innehåller egenskaper som inte ska ändras av klienten. Mer information finns i Överpublicering.

Razor Sidor binder som standard endast egenskaper med icke-verbGET . Bindning till egenskaper tar bort behovet av att skriva kod för att konvertera HTTP-data till modelltypen. Bindning minskar koden genom att använda samma egenskap för att återge formulärfält (<input asp-for="Customer.Name">) och acceptera indata.

Varning

Av säkerhetsskäl måste du välja att binda GET begärandedata till sidmodellegenskaper. Verifiera användarindata innan du mappar den till egenskaper. Att välja bindning GET är användbart när du hanterar scenarier som förlitar sig på frågesträngar eller vägvärden.

Om du vill binda en egenskap för GET begäranden anger du [BindProperty] attributets SupportsGet egenskap till true:

[BindProperty(SupportsGet = true)]

Mer information finns i ASP.NET Core Community Standup: Bind på GET-diskussion (YouTube).

Granskning av Pages/Customers/Create.cshtml vyfilen:

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

Startsidan

Index.cshtml är startsidan:

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

Den associerade PageModel klassen (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();
    }
}

Filen Index.cshtml innehåller följande markering:

<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |

<a /a> Anchor Tag Helper använde asp-route-{value} attributet för att generera en länk till Redigeringssidan. Länken innehåller routningsdata med kontakt-ID:t. Till exempel https://localhost:5001/Edit/1. Tagghjälpare gör det möjligt för kod på serversidan att delta i skapandet och återgivningen av HTML-element i Razor filer.

Filen Index.cshtml innehåller markering för att skapa en borttagningsknapp för varje kundkontakt:

<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>

Den renderade HTML-koden:

<button type="submit" formaction="/Customers?id=1&amp;handler=delete">delete</button>

När borttagningsknappen återges i HTML innehåller dess formaction parametrar för:

  • Kundens kontakt-ID, som anges av attributet asp-route-id .
  • handler anges av attributet asp-page-handler.

När knappen har valts skickas en formulärbegäran POST till servern. Enligt konventionen väljs namnet på hanteringsmetoden baserat på värdet för parametern handler enligt schemat OnPost[handler]Async.

Eftersom handler är delete i det här exemplet, används OnPostDeleteAsync-hanteringsmetoden för att bearbeta POST-begäran. Om asp-page-handler är inställt på ett annat värde, till exempel remove, väljs en hanteringsmetod med namnet 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();
}

OnPostDeleteAsync-metoden:

  • Hämtar id från frågesträngen.
  • Söker i databasen efter kundkontakten med FindAsync.
  • Om kundkontakten hittas tas den bort och databasen uppdateras.
  • Anrop RedirectToPage för att omdirigera till rotindexsidan (/Index).

Filen Edit.cshtml

@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Customer</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Customer!.Id" />
            <div class="form-group">
                <label asp-for="Customer!.Name" class="control-label"></label>
                <input asp-for="Customer!.Name" class="form-control" />
                <span asp-validation-for="Customer!.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Den första raden innehåller @page "{id:int}" direktivet. Routningsbegränsningen "{id:int}" uppmanar sidan att acceptera begäranden till sidan som innehåller int routningsdata. Om en begäran till sidan inte innehåller routningsdata som kan konverteras till en intreturnerar körningen ett HTTP 404-fel (hittades inte). Om du vill göra ID:t valfritt lägger du till i routningsbegränsningen ? :

@page "{id:int?}"

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

Validering

Valideringsregler:

  • Anges deklarativt i modellklassen.
  • Tillämpas överallt i appen.

Namnområdet System.ComponentModel.DataAnnotations innehåller en uppsättning inbyggda valideringsattribut som tillämpas deklarativt på en klass eller egenskap. DataAnnotations innehåller också formateringsattribut som [DataType] det hjälper till med formatering och ger ingen verifiering.

Tänk på Customer modellen:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string? Name { get; set; }
    }
}

Använd följande Create.cshtml visningsfil:

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

Föregående kod:

  • Innehåller jQuery- och jQuery-valideringsskript.

  • Använder <div /> och <span />taghjälparna för att aktivera:

    • Validering på klientsidan.
    • Renderingsfel vid validering.
  • Genererar följande 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>
    

Publicering av Skapa-formuläret utan ett namn visar felmeddelandet "Fältet Namn är obligatoriskt" i formuläret. Om JavaScript är aktiverat på klienten visar webbläsaren felet utan att publicera till servern.

Attributet [StringLength(10)] genererar data-val-length-max="10" på den renderade HTML-koden. data-val-length-max förhindrar att webbläsare anger mer än den angivna maximala längden. Om ett verktyg som Fiddler används för att redigera och spela upp inlägget igen:

  • Med ett namn längre än 10 tecken.
  • Felmeddelandet "Fältnamnet måste vara en sträng med en maximal längd på 10." returneras.

Tänk på följande Movie modell:

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

Valideringsattributen anger beteende för att framtvinga de modellegenskaper som de tillämpas på:

  • Attributen Required och MinimumLength anger att en egenskap måste ha ett värde, men ingenting hindrar en användare från att ange tomt utrymme för att uppfylla den här valideringen.

  • Attributet RegularExpression används för att begränsa vilka tecken som kan matas in. I föregående kod, "Genre":

    • Får endast använda bokstäver.
    • Den första bokstaven måste vara versaler. Blanksteg, siffror och specialtecken tillåts inte.
  • RegularExpression "Omdöme":

    • Kräver att det första tecknet är versaler.
    • Tillåter specialtecken och siffror i efterföljande mellanrum. "PG-13" är giltig för en klassificering, men inte för en "Genre".
  • Attributet Range begränsar ett värde till inom ett angivet intervall.

  • Attributet StringLength anger den maximala längden för en strängegenskap och eventuellt dess minsta längd.

  • Värdetyper (till exempel decimal, int, float, DateTime) är av sig själva nödvändiga och behöver inte attributet [Required].

Sidan Skapa för modellen Movie visar fel med ogiltiga värden:

Filmvisningsformulär med flera jQuery-valideringsfel på klientsidan

Mer information finns i:

CSS-isolering

Isolera CSS-format på enskilda sidor, vyer och komponenter för att minska eller undvika:

  • Beroenden på globala format som kan vara svåra att underhålla.
  • Formatkonflikter i kapslat innehåll.

För att lägga till en CSS-fil som är anpassad för en specifik sida eller vy, placera CSS-formatmallarna i en tillhörande fil med samma namn som den aktuella filen. I följande exempel tillhandahåller en Index.cshtml.css fil CSS-formatmallar som endast tillämpas på Index.cshtml sidan eller vyn.

Pages/Index.cshtml.css (Razor Sidor) eller Views/Index.cshtml.css (MVC):

h1 {
    color: red;
}

CSS-isolering sker vid byggtiden. Ramverket skriver om CSS-väljare för att matcha markering som återges av appens sidor eller vyer. De omskrivna CSS-formaten paketeras och skapas som en statisk resurs, {APP ASSEMBLY}.styles.css. Platshållaren {APP ASSEMBLY} är projektets sammansättningsnamn. En länk till de paketerade CSS-formaten placeras i appens layout.

I innehållet i appens <head>Pages/Shared/_Layout.cshtml (Razor Sidor) eller Views/Shared/_Layout.cshtml (MVC), lägg till eller bekräfta att länken till de paketerade CSS-stilmallarna finns:

<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />

I följande exempel är WebAppappens sammansättningsnamn :

<link rel="stylesheet" href="WebApp.styles.css" />

Formatmallarna som definieras i en avgränsad CSS-fil tillämpas endast på de renderade resultat från den matchande filen. I föregående exempel står inte css-deklarationer h1 som definierats någon annanstans i appen i konflikt med rubrikformatet Index. CSS-format kaskad- och ärvningsregler gäller fortfarande för scopade CSS-filer. Format som tillämpas direkt på ett <h1>-element i Index.cshtml-filen åsidosätter till exempel den omfångsbestämda CSS-filens format i Index.cshtml.css.

Anmärkning

För att garantera CSS-formatisolering vid paketering stöds inte import av CSS i Razor kodblock.

CSS-isolering gäller endast för HTML-element. CSS-isolering stöds inte för Tag Helpers.

I den paketerade CSS-filen associeras varje sida, vy eller Razor komponent med en omfångsidentifierare i formatet b-{STRING}, där {STRING} platshållaren är en sträng med tio tecken som genereras av ramverket. I följande exempel visas stilen för det föregående elementet <h1>Index sidan i en Razor Pages-app.

/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
    color: red;
}

På sidan Index där CSS-formatmallen tillämpas från den paketerade filen läggs omfångsidentifieraren till som ett HTML-attribut:

<h1 b-3xxtam6d07>

Identifieraren är unik för en app. Vid bygget skapas ett projektpaket med konventionen {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, där platshållaren {STATIC WEB ASSETS BASE PATH} är bassökvägen för statiska webbtillgångar.

Om andra projekt används, till exempel NuGet-paket eller Razor klassbibliotek, den paketerade filen:

  • Refererar till formatmallarna med css-importer.
  • Publiceras inte som en statisk webbtillgång för appen som använder stilarna.

Stöd för CSS-förprocessor

CSS-förprocessorer är användbara för att förbättra CSS-utvecklingen genom att använda funktioner som variabler, kapsling, moduler, mixins och arv. Även om CSS-isolering inte har inbyggt stöd för CSS-förprocessorer som Sass eller Less, är integreringen av CSS-förprocessorer sömlös så länge förprocessorkompileringen sker innan ramverket skriver om CSS-väljare under byggprocessen. Med Visual Studio kan du till exempel konfigurera befintlig förprocessorkompilering som en Before Build-uppgift i Visual Studio Task Runner Explorer.

Många NuGet-paket från tredje part, till exempel AspNetCore.SassCompiler, kan kompilera SASS/SCSS-filer i början av byggprocessen innan CSS-isoleringen sker, och ingen ytterligare konfiguration krävs.

CSS-isoleringskonfiguration

CSS-isolering tillåter konfiguration för vissa avancerade scenarier, till exempel när det finns beroenden för befintliga verktyg eller arbetsflöden.

Anpassa omfångsidentifierarformat

I det här avsnittet {Pages|Views} är platshållaren antingen Pages för Razor Pages-appar eller Views för MVC-appar.

Som standard använder omfångsidentifierare formatet b-{STRING}, där {STRING} platshållaren är en sträng med tio tecken som genereras av ramverket. Om du vill anpassa omfångsidentifierarformatet uppdaterar du projektfilen till ett önskat mönster:

<ItemGroup>
  <None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

I föregående exempel ändrar CSS som genererats för Index.cshtml.css dess omfångsidentifierare från b-{STRING} till custom-scope-identifier.

Använd omfångsidentifierare för att uppnå arv med begränsade CSS-filer. I följande projektfilexempel innehåller en BaseView.cshtml.css fil vanliga format för vyer. En DerivedView.cshtml.css fil ärver dessa formatmallar.

<ItemGroup>
  <None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
  <None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Använd jokertecknet (*) för att dela omfångsidentifierare i flera filer:

<ItemGroup>
  <None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>

Ändra bassökväg för statiska webbtillgångar

Den avgränsade CSS-filen genereras i appens rotmapp. I projektfilen använder du StaticWebAssetBasePath egenskapen för att ändra standardsökvägen. I följande exempel placeras den begränsade CSS-filen och resten av programmets resurser på _content-sökvägen.

<PropertyGroup>
  <StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>

Inaktivera automatisk paketering

Om du vill välja bort hur ramverket publicerar och läser in begränsade filer vid körning använder du DisableScopedCssBundling egenskapen . När du använder den här egenskapen ansvarar andra verktyg eller processer för att ta de isolerade CSS-filerna från obj katalogen och publicera och läsa in dem vid körning:

<PropertyGroup>
  <DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>

Razor stöd för klassbibliotek (RCL)

När ett Razor klassbibliotek (RCL) tillhandahåller isolerade format pekar taggens <link>href attribut på {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, där platshållarna är:

  • {STATIC WEB ASSET BASE PATH}: Basvägen för de statiska webbtillgångarna.
  • {PACKAGE ID}: Bibliotekets paketidentifierare. Paketidentifieraren är som standard projektets sammansättningsnamn om paketidentifieraren inte anges i projektfilen.

I följande exempel:

  • Basvägen för den statiska webbresursen är _content/ClassLib.
  • Klassbibliotekets sammansättningsnamn är ClassLib.

Pages/Shared/_Layout.cshtml (Razor Sidor) eller Views/Shared/_Layout.cshtml (MVC):

<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">

Mer information om RCL:er finns i följande artiklar:

Information om Blazor CSS-isolering finns i ASP.NET Core Blazor CSS-isolering.

Hantera HEAD-begäranden med en OnGet-hanteringsåterställning

HEAD begäranden gör det möjligt att hämta rubrikerna för en specifik resurs. Till skillnad från GET begäranden returnerar HEAD begäranden inte någon svarstext.

Normalt skapas en OnHead hanterare och anropas för HEAD begäranden:

public void OnHead()
{
    HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}

Razor Sidor återgår till att anropa OnGet hanteraren om ingen OnHead hanterare har definierats.

XSRF/CSRF och Razor sidor

Razor Sidor skyddas av Antiforgery-validering. FormTagHelper infogar antiforgery-token i HTML-formulärelement.

Använda layouter, partiella objekt, mallar och tagghjälpare med Razor sidor

Sidorna fungerar med alla funktioner i Razor vymotorn. Layouter, delar, mallar, Tag Helpers _ViewStart.cshtmloch _ViewImports.cshtml fungerar på samma sätt som för konventionella Razor vyer.

Låt oss rensa den här sidan genom att dra nytta av några av dessa funktioner.

Lägg till en layout-sida till 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>

Layout:

  • Styr layouten för varje sida (såvida inte sidan väljer bort layouten).
  • Importerar HTML-strukturer som JavaScript och formatmallar.
  • Innehållet på Razor sidan återges där @RenderBody() anropas.

Mer information finns på layoutsidan.

Egenskapen Layout anges i Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Layouten finns i mappen Pages/Shared . Sidor letar efter andra vyer (layouter, mallar, partiella objekt) hierarkiskt, med början i samma mapp som den aktuella sidan. En layout i mappen Sidor/Delad kan användas från valfri Razor sida under mappen Sidor .

Layoutfilen ska gå till mappen Pages/Shared .

Vi rekommenderar att du inte placerar layoutfilen i mappen Vyer/Delad . Vyer/delad är ett MVC-vymönster. Razor Sidor är avsedda att förlita sig på mapphierarki, inte sökvägskonventioner.

Visa sökning från en Razor sida inkluderar mappen Sidor. Layouter, mallar och delar som används med MVC-styrenheter och konventionella Razor vyer fungerar smidigt.

Lägg till en Pages/_ViewImports.cshtml fil:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@namespace förklaras senare i handledningen. Direktivet @addTagHelper tar med de inbyggda Tag Helpers för alla sidor i mappen Sidor.

Direktivet @namespace anges på en sida:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

Direktivet @namespace anger sidans namnområde. Direktivet @model behöver inte inkludera namnområdet.

@namespace När direktivet finns i _ViewImports.cshtmltillhandahåller det angivna namnområdet prefixet för det genererade namnområdet på sidan som importerar @namespace direktivet. Resten av det genererade namnområdet (suffixdelen) är den punktavgränsade relativa sökvägen mellan mappen som innehåller _ViewImports.cshtml och mappen som innehåller sidan.

Till exempel anger PageModel klassen Pages/Customers/Edit.cshtml.cs uttryckligen namnområdet.

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

Filen Pages/_ViewImports.cshtml anger följande namnområde:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Det genererade namnområdet för sidan Pages/Customers/Edit.cshtmlRazor är samma som PageModel klassen.

@namespace fungerar också med konventionella Razor vyer.

Överväg visningsfilen 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>

Den uppdaterade Pages/Customers/Create.cshtml vyfilen med _ViewImports.cshtml och den föregående layoutfilen:

@page
@model CreateModel

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer!.Name" />
    <input type="submit" />
</form>

I föregående kod importerades _ViewImports.cshtml namnområdet och Tag Helpers. Layoutfilen importerade JavaScript-filerna.

ProjektetRazor Pages starter innehåller Pages/_ValidationScriptsPartial.cshtml, som kopplar upp validering på klientsidan.

Mer information om partiella vyer finns i Partiella vyer i ASP.NET Core.

URL-generering för sidor

Sidan Create , som visades tidigare, använder 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");
    }
}

Appen har följande fil-/mappstruktur:

  • /Sidor

    • Index.cshtml

    • Privacy.cshtml

    • /Kunder

      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml

Sidorna Pages/Customers/Create.cshtml och Pages/Customers/Edit.cshtml omdirigeras till Pages/Customers/Index.cshtml efter framgång. Strängen ./Index är ett relativt sidnamn som används för att komma åt föregående sida. Den används för att generera URL:er till sidan Pages/Customers/Index.cshtml . Till exempel:

  • Url.Page("./Index", ...)
  • <a asp-page="./Index">Customers Index Page</a>
  • RedirectToPage("./Index")

Det absoluta sidnamnet /Index används för att generera URL:er till Pages/Index.cshtml sidan. Till exempel:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">Home Index Page</a>
  • RedirectToPage("/Index")

Sidnamnet är sökvägen till sidan från rotmappen /Pages , inklusive en inledande / (till exempel /Index). De föregående exemplen på URL-generering erbjuder förbättrade alternativ och funktioner jämfört med hårdkodning av en URL. URL-genereringen använder routning och kan generera och koda parametrar beroende på hur vägen definieras i målsökvägen.

URL-generering för sidor stöder relativa namn. I följande tabell visas vilken indexsida som väljs med hjälp av olika RedirectToPage parametrar i Pages/Customers/Create.cshtml.

RedirectToPage(x) Sida
RedirectToPage("/Index") Sidor/Index
RedirectToPage("./Index"); Sidor/Kunder/Index
RedirectToPage(".. /Index") Sidor/Index
OmdirigeraTillSida("Index") Sidor/Kunder/Index

RedirectToPage("Index"), RedirectToPage("./Index")och RedirectToPage("../Index") är relativa namn. Parametern RedirectToPagekombineras med sökvägen till den aktuella sidan för att beräkna namnet på målsidan.

Relativ namnlänkning är användbar när du skapar platser med en komplex struktur. När relativa namn används för att länka mellan sidor i en mapp:

  • Om du byter namn på en mapp bryts inte de relativa länkarna.
  • Länkarna är inte brutna eftersom de inte innehåller mappnamnet.

Om du vill omdirigera till en sida i ett annat område anger du området:

RedirectToPage("/Index", new { area = "Services" });

Mer information finns i Områden i ASP.NET Core - och Razor Pages-routnings- och appkonventioner i ASP.NET Core.

ViewData-attribut

Data kan skickas till en sida med ViewDataAttribute. Egenskaper med [ViewData] attributet har sina värden lagrade och inlästa från ViewDataDictionary.

I följande exempel tillämpas AboutModel attributet på [ViewData] egenskapen:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

På sidan Om får du åtkomst till egenskapen Title som en modellegenskap:

<h1>@Model.Title</h1>

I layouten läss rubriken från ordlistan ViewData:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempData

ASP.NET Core tillgängliggör TempData. Den här egenskapen lagrar data tills den har lästs. Metoderna Keep och Peek kan användas för att undersöka data utan borttagning. TempData är användbart för omdirigering, när data behövs för mer än en enskild begäran.

Följande kod anger värdet Message för att använda 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");
    }
}

Följande markering i Pages/Customers/Index.cshtml-filen visar värdet av Message med hjälp av TempData.

<h3>Msg: @Model.Message</h3>

Sidmodellen Pages/Customers/Index.cshtml.cs tillämpar [TempData] attributet på Message egenskapen.

[TempData]
public string Message { get; set; }

Mer information finns i TempData.

Flera hanterare per sida

Följande sida genererar markering för två hanterare med hjälp av asp-page-handler Tag Helper:

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

Formuläret i föregående exempel har två skicka-knappar som var och en använder FormActionTagHelper för att skicka till en annan URL. Attributet asp-page-handler är en följeslagare till asp-page. asp-page-handler genererar URL:er som skickar till var och en av de hanteringsmetoder som definieras av en sida. asp-page anges inte eftersom exemplet länkar till den aktuella sidan.

Sidmodellen:

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

Föregående kod använder namngivna hanteringsmetoder. Namngivna hanteringsmetoder skapas genom att texten tas med i namnet efter On<HTTP Verb> och före Async (om det finns). I föregående exempel är sidmetoderna OnPostJoinListAsync och OnPostJoinListUCAsync. När OnPost och Async har tagits bort är JoinList hanterarnamnen och JoinListUC.

<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />

Föregående kod används för att skicka URL-sökvägen till OnPostJoinListAsync, vilket är https://localhost:5001/Customers/CreateFATH?handler=JoinList. URL-sökvägen som skickas till OnPostJoinListUCAsync är https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Anpassade vägar

@page Använd direktivet för att:

  • Ange en anpassad väg till en sida. Vägen till sidan Om kan till exempel anges till /Some/Other/Path med @page "/Some/Other/Path".
  • Lägg till segment till en sidas standardväg. Ett objektsegment kan till exempel läggas till på en sidas standardväg med @page "item".
  • Lägg till parametrar till en sidas standardväg. Till exempel kan en ID-parameter, id, krävas för en sida med @page "{id}".

En rotrelativ sökväg som anges av en tilde (~) i början av sökvägen stöds. Till exempel är @page "~/Some/Other/Path" samma som @page "/Some/Other/Path".

Om du inte gillar frågesträngen ?handler=JoinList i URL:en ändrar du vägen för att placera hanterarnamnet i sökvägsdelen av URL:en. Vägen kan anpassas genom att lägga till en vägmall som omges av dubbla citattecken efter @page direktivet.

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

Föregående kod används för att skicka URL-sökvägen till OnPostJoinListAsync, vilket är https://localhost:5001/Customers/CreateFATH/JoinList. URL-sökvägen som skickas till OnPostJoinListUCAsync är https://localhost:5001/Customers/CreateFATH/JoinListUC.

Följande ?handler innebär att routningsparametern är valfri.

Sammanställning av JavaScript-filer (JS)

Att sortera JavaScript-filer (JS) för sidor och vyer är ett praktiskt sätt att organisera skript i en app.

Samla in JS filer med hjälp av följande filnamnstilläggskonventioner:

  • Sidor med Razor Pages-appar och vyer för MVC-appar: .cshtml.js. Exempel:
    • Pages/Index.cshtml.js för sidan för Index en Razor pages-app på Pages/Index.cshtml.
    • Views/Home/Index.cshtml.js Index för vyn för en MVC-app på Views/Home/Index.cshtml.

Sorterade JS filer kan adresseras offentligt med hjälp av sökvägen till filen i projektet:

  • Sidor och vyer från en samlad skriptfil i appen:

    {PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js

    • Platshållaren {PATH} är sökvägen till sidan, vyn eller komponenten.
    • Platshållaren {PAGE, VIEW, OR COMPONENT} är sidan, vyn eller komponenten.
    • Platshållaren {EXTENSION} matchar tillägget för sidan, vyn eller komponenten, antingen razor eller cshtml.

    Razor Exempel på sidor:

    En JS fil för sidan Index placeras i Pages mappen (Pages/Index.cshtml.js) bredvid sidan Index (Pages/Index.cshtml). På sidan Index refereras skriptet till på sökvägen i Pages mappen:

    @section Scripts {
      <script src="~/Pages/Index.cshtml.js"></script>
    }
    

Standardlayouten Pages/Shared/_Layout.cshtml kan konfigureras för att inkludera sorterade JS filer, vilket eliminerar behovet av att konfigurera varje sida individuellt:

<script asp-src-include="@(ViewContext.View.Path).js"></script>

Exempelnedladdningen använder föregående kodfragment för att inkludera sorterade JS filer i standardlayouten.

När appen publiceras flyttar ramverket automatiskt skriptet till webbroten. I föregående exempel flyttas skriptet till bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Pages\Index.cshtml.js, där {TARGET FRAMEWORK MONIKER} platshållaren är Target Framework Moniker (TFM). Ingen ändring krävs för skriptets relativa URL på Index sidan.

När appen publiceras flyttar ramverket automatiskt skriptet till webbroten. I föregående exempel flyttas skriptet till bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Components\Pages\Index.razor.js, där {TARGET FRAMEWORK MONIKER} platshållaren är Target Framework Moniker (TFM). Ingen ändring krävs för skriptets relativa URL i komponenten Index .

  • För skript som tillhandahålls av ett Razor klassbibliotek (RCL):

    _content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js

    • Platshållaren {PACKAGE ID} är RCL:s paketidentifierare (eller biblioteksnamn för ett klassbibliotek som refereras av appen).
    • Platshållaren {PATH} är sökvägen till sidan, vyn eller komponenten. Om en Razor komponent finns i roten för RCL inkluderas inte sökvägssegmentet.
    • Platshållaren {PAGE, VIEW, OR COMPONENT} är sidan, vyn eller komponenten.
    • Platshållaren {EXTENSION} matchar tillägget för sida, vy eller komponent, antingen razor eller cshtml.

Avancerad konfiguration och inställningar

Konfigurationen och inställningarna i följande avsnitt krävs inte av de flesta appar.

Om du vill konfigurera avancerade alternativ använder du den AddRazorPages överlagring som konfigurerar 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();

RazorPagesOptions Använd för att ange rotkatalogen för sidor eller lägga till programmodellkonventioner för sidor. Mer information om konventioner finns i Razor Auktoriseringskonventioner för sidor.

För att förkompilera vyer, se vykompileringRazor.

Ange att Razor Sidor finns i innehållsroten

Som standardinställning är Razor Pages placerade i katalogen /Pages. Lägg WithRazorPagesAtContentRoot till för att ange att dina Razor sidor finns i innehållsroten (ContentRootPath) i appen:

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();

Ange att Razor Sidor finns i en anpassad rotkatalog

Lägg WithRazorPagesRoot till för att ange att Razor Sidor finns i en anpassad rotkatalog i appen (ange en relativ sökväg):

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();

Ytterligare resurser

Skapa ett Razor pages-projekt

Mer information om hur du skapar ett Razor pages-projekt finns .

Razor sidor

Razor Sidor är aktiverade i 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();
        });
    }
}

Överväg en grundläggande sida:

@page

<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>

Föregående kod ser ut ungefär som en Razor vyfil som används i en ASP.NET Core-app med kontrollanter och vyer. Det som gör det @page annorlunda är direktivet. @page gör filen till en MVC-åtgärd , vilket innebär att den hanterar begäranden direkt, utan att gå igenom en kontrollant. @page måste vara det första Razor direktivet på en sida. @page påverkar beteendet hos andra Razor konstruktioner. Razor Sidfilnamn har ett .cshtml suffix.

En liknande sida, med hjälp av en PageModel klass, visas i följande två filer. Filen Pages/Index2.cshtml:

@page
@using RazorPagesIntro.Pages
@model Index2Model

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

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

Enligt konvention har PageModel-klassfilen samma namn som Razor-sidfilen med .cs bifogat. Till exempel är Razorföregående Pages/Index2.cshtml sida . Filen som innehåller PageModel klassen heter Pages/Index2.cshtml.cs.

Associationerna för URL-sökvägar till sidor bestäms av sidans plats i filsystemet. I följande tabell visas en Razor sidsökväg och matchande URL:

Filnamn och sökväg matchande URL
/Pages/Index.cshtml / eller /Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store eller /Store/Index

Anteckningar:

  • Körningen söker Razor efter Pages-filer i mappen Pages som standard.
  • Index är standardsidan när en URL inte innehåller en sida.

Skriva ett grundläggande formulär

Razor Sidor är utformade för att göra vanliga mönster som används med webbläsare enkla att implementera när du skapar en app. Modellbindning, Tag Helpers och HTML-hjälpverktyg fungerar bara med de egenskaper som definierats i en Razor sidklass. Överväg en sida som implementerar ett grundläggande "kontakta oss"-formulär för Contact modellen:

För exemplen i det här dokumentet initieras DbContext i filen Startup.cs.

För minnesdatabasen krävs NuGet-paketet Microsoft.EntityFrameworkCore.InMemory.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<CustomerDbContext>(options =>
                      options.UseInMemoryDatabase("name"));
    services.AddRazorPages();
}

Datamodellen:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string Name { get; set; }
    }
}

Databas-kontexten:

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

Filen för vy 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>

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

Enligt konvention benämns PageModel-klassen <PageName>Model och finns i samma namnområde som sidan.

Klassen PageModel tillåter separation av logiken för en sida från presentationen. Den definierar sidhanterare för begäranden som skickas till sidan och de data som används för att återge sidan. Den här separationen tillåter:

Sidan har en OnPostAsynchanteringsmetod som körs på POST begäranden (när en användare publicerar formuläret). Du kan lägga till hanteringsmetoder för alla HTTP-verb. De vanligaste hanterarna är:

  • OnGet för att initiera det tillstånd som behövs för sidan. I den föregående koden visar OnGet-metoden CreateModel.cshtmlRazor-sidan.
  • OnPost för att hantera formulärinlämningar.

Namngivningssuffixet Async är valfritt men används ofta av konventionen för asynkrona funktioner. Föregående kod är typisk för Razor Sidor.

Om du är bekant med ASP.NET-applikationer med kontroller och vyer:

  • Koden OnPostAsync i föregående exempel ser ut ungefär som vanlig styrenhetskod.
  • De flesta av MVC-primitiverna som modellbindning, validering och åtgärdsresultat fungerar på samma sätt med kontrollanter och Razor sidor.

Föregående OnPostAsync metod:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _context.Customers.Add(Customer);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Det grundläggande flödet av OnPostAsync:

Sök efter verifieringsfel.

  • Om det inte finns några fel sparar du data och omdirigerar.
  • Om det finns fel visar du sidan igen med valideringsmeddelanden. I många fall identifieras verifieringsfel på klienten och skickas aldrig till servern.

Filen för vy 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>

Den renderade HTML-koden från 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>

I föregående kod publicerar du formuläret:

  • Med giltiga data:

    • Hanteringsmetoden OnPostAsync anropar RedirectToPage hjälpmetoden. RedirectToPage returnerar en instans av RedirectToPageResult. RedirectToPage:

      • Är ett åtgärdsresultat.
      • Liknar RedirectToAction eller RedirectToRoute (används i kontrollanter och vyer).
      • Är anpassad för sidor. I föregående exempel omdirigeras den till rotindexsidan (/Index). RedirectToPage beskrivs i avsnittet URL-generering för sidor .
  • Med valideringsfel som skickas till servern:

    • Hanteringsmetoden OnPostAsync anropar Page hjälpmetoden. Page returnerar en instans av PageResult. Att returnera Page är likt hur åtgärder i kontroller returnerar View. PageResult är standardreturtypen för en hanteringsmetod. En hanteringsmetod som returnerar void återger sidan.
    • I föregående exempel resulterar publicering av formuläret utan värde i ModelState.IsValid som returnerar false. I det här exemplet visas inga verifieringsfel på klienten. Verifieringsfelhantering beskrivs senare i det här dokumentet.
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();
    
        return RedirectToPage("./Index");
    }
    
  • Med valideringsfel som identifierats av verifiering på klientsidan:

    • Data publiceras inte på servern.
    • Validering på klientsidan förklaras senare i det här dokumentet.

Egenskapen Customer använder [BindProperty] attributet för att välja modellbindning:

public class CreateModel : PageModel
{
    private readonly CustomerDbContext _context;

    public CreateModel(CustomerDbContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

[BindProperty] ska inte användas på modeller som innehåller egenskaper som inte ska ändras av klienten. Mer information finns i Överpublicering.

Razor Sidor binder som standard endast egenskaper med icke-verbGET . Bindning till egenskaper tar bort behovet av att skriva kod för att konvertera HTTP-data till modelltypen. Bindning minskar koden genom att använda samma egenskap för att återge formulärfält (<input asp-for="Customer.Name">) och acceptera indata.

Varning

Av säkerhetsskäl måste du välja att binda GET begärandedata till sidmodellegenskaper. Verifiera användarindata innan du mappar den till egenskaper. Att välja bindning GET är användbart när du hanterar scenarier som förlitar sig på frågesträngar eller vägvärden.

Om du vill binda en egenskap för GET begäranden anger du [BindProperty] attributets SupportsGet egenskap till true:

[BindProperty(SupportsGet = true)]

Mer information finns i ASP.NET Core Community Standup: Bind på GET-diskussion (YouTube).

Granskning av Pages/Create.cshtml vyfilen:

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

Startsidan

Index.cshtml är startsidan:

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

Den associerade PageModel klassen (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();
    }
}

Filen Index.cshtml innehåller följande markering:

<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |

<a /a> Anchor Tag Helper använde asp-route-{value} attributet för att generera en länk till Redigeringssidan. Länken innehåller routningsdata med kontakt-ID:t. Till exempel https://localhost:5001/Edit/1. Tagghjälpare gör det möjligt för kod på serversidan att delta i skapandet och återgivningen av HTML-element i Razor filer.

Filen Index.cshtml innehåller markering för att skapa en borttagningsknapp för varje kundkontakt:

<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>

Den renderade HTML-koden:

<button type="submit" formaction="/Customers?id=1&amp;handler=delete">delete</button>

När borttagningsknappen återges i HTML innehåller dess formaction parametrar för:

  • Kundens kontakt-ID, som anges av attributet asp-route-id .
  • handler anges av attributet asp-page-handler.

När knappen har valts skickas en formulärbegäran POST till servern. Enligt konventionen väljs namnet på hanteringsmetoden baserat på värdet för parametern handler enligt schemat OnPost[handler]Async.

Eftersom handler är delete i det här exemplet, används OnPostDeleteAsync-hanteringsmetoden för att bearbeta POST-begäran. Om asp-page-handler är inställt på ett annat värde, till exempel remove, väljs en hanteringsmetod med namnet 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();
}

OnPostDeleteAsync-metoden:

  • Hämtar id från frågesträngen.
  • Söker i databasen efter kundkontakten med FindAsync.
  • Om kundkontakten hittas tas den bort och databasen uppdateras.
  • Anrop RedirectToPage för att omdirigera till rotindexsidan (/Index).

Filen Edit.cshtml

@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers


<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
    <div asp-validation-summary="All"></div>
    <input asp-for="Customer.Id" type="hidden" />
    <div>
        <label asp-for="Customer.Name"></label>
        <div>
            <input asp-for="Customer.Name" />
            <span asp-validation-for="Customer.Name"></span>
        </div>
    </div>

    <div>
        <button type="submit">Save</button>
    </div>
</form>

Den första raden innehåller @page "{id:int}" direktivet. Routningsbegränsningen "{id:int}" uppmanar sidan att acceptera begäranden till sidan som innehåller int routningsdata. Om en begäran till sidan inte innehåller routningsdata som kan konverteras till en intreturnerar körningen ett HTTP 404-fel (hittades inte). Om du vill göra ID:t valfritt lägger du till i routningsbegränsningen ? :

@page "{id:int?}"

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

}

Validering

Valideringsregler:

  • Anges deklarativt i modellklassen.
  • Tillämpas överallt i appen.

Namnområdet System.ComponentModel.DataAnnotations innehåller en uppsättning inbyggda valideringsattribut som tillämpas deklarativt på en klass eller egenskap. DataAnnotations innehåller också formateringsattribut som [DataType] det hjälper till med formatering och ger ingen verifiering.

Tänk på Customer modellen:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Models
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(10)]
        public string Name { get; set; }
    }
}

Använd följande Create.cshtml visningsfil:

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

Föregående kod:

  • Innehåller jQuery- och jQuery-valideringsskript.

  • Använder <div /> och <span />taghjälparna för att aktivera:

    • Validering på klientsidan.
    • Renderingsfel vid validering.
  • Genererar följande 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>
    

Publicering av Skapa-formuläret utan ett namn visar felmeddelandet "Fältet Namn är obligatoriskt" i formuläret. Om JavaScript är aktiverat på klienten visar webbläsaren felet utan att publicera till servern.

Attributet [StringLength(10)] genererar data-val-length-max="10" på den renderade HTML-koden. data-val-length-max förhindrar att webbläsare anger mer än den angivna maximala längden. Om ett verktyg som Fiddler används för att redigera och spela upp inlägget igen:

  • Med ett namn längre än 10 tecken.
  • Felmeddelandet "Fältnamnet måste vara en sträng med en maximal längd på 10." returneras.

Tänk på följande Movie modell:

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

Valideringsattributen anger beteende för att framtvinga de modellegenskaper som de tillämpas på:

  • Attributen Required och MinimumLength anger att en egenskap måste ha ett värde, men ingenting hindrar en användare från att ange tomt utrymme för att uppfylla den här valideringen.

  • Attributet RegularExpression används för att begränsa vilka tecken som kan matas in. I föregående kod, "Genre":

    • Får endast använda bokstäver.
    • Den första bokstaven måste vara versaler. Blanksteg, siffror och specialtecken tillåts inte.
  • RegularExpression "Omdöme":

    • Kräver att det första tecknet är versaler.
    • Tillåter specialtecken och siffror i efterföljande mellanrum. "PG-13" är giltig för en klassificering, men inte för en "Genre".
  • Attributet Range begränsar ett värde till inom ett angivet intervall.

  • Attributet StringLength anger den maximala längden för en strängegenskap och eventuellt dess minsta längd.

  • Värdetyper (till exempel decimal, int, float, DateTime) är av sig själva nödvändiga och behöver inte attributet [Required].

Sidan Skapa för modellen Movie visar fel med ogiltiga värden:

Filmvisningsformulär med flera jQuery-valideringsfel på klientsidan

Mer information finns i:

Hantera HEAD-begäranden med en OnGet-hanteringsåterställning

HEAD begäranden gör det möjligt att hämta rubrikerna för en specifik resurs. Till skillnad från GET begäranden returnerar HEAD begäranden inte någon svarstext.

Normalt skapas en OnHead hanterare och anropas för HEAD begäranden:

public void OnHead()
{
    HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}

Razor Sidor återgår till att anropa OnGet hanteraren om ingen OnHead hanterare har definierats.

XSRF/CSRF och Razor sidor

Razor Sidor skyddas av Antiforgery-validering. FormTagHelper infogar antiforgery-token i HTML-formulärelement.

Använda layouter, partiella objekt, mallar och tagghjälpare med Razor sidor

Sidorna fungerar med alla funktioner i Razor vymotorn. Layouter, delar, mallar, Tag Helpers _ViewStart.cshtmloch _ViewImports.cshtml fungerar på samma sätt som för konventionella Razor vyer.

Låt oss rensa den här sidan genom att dra nytta av några av dessa funktioner.

Lägg till en layout-sida till 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>

Layout:

  • Styr layouten för varje sida (såvida inte sidan väljer bort layouten).
  • Importerar HTML-strukturer som JavaScript och formatmallar.
  • Innehållet på Razor sidan återges där @RenderBody() anropas.

Mer information finns på layoutsidan.

Egenskapen Layout anges i Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Layouten finns i mappen Pages/Shared . Sidor letar efter andra vyer (layouter, mallar, partiella objekt) hierarkiskt, med början i samma mapp som den aktuella sidan. En layout i mappen Sidor/Delad kan användas från valfri Razor sida under mappen Sidor .

Layoutfilen ska gå till mappen Pages/Shared .

Vi rekommenderar att du inte placerar layoutfilen i mappen Vyer/Delad . Vyer/delad är ett MVC-vymönster. Razor Sidor är avsedda att förlita sig på mapphierarki, inte sökvägskonventioner.

Visa sökning från en Razor sida inkluderar mappen Sidor. Layouter, mallar och delar som används med MVC-styrenheter och konventionella Razor vyer fungerar smidigt.

Lägg till en Pages/_ViewImports.cshtml fil:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@namespace förklaras senare i handledningen. Direktivet @addTagHelper tar med de inbyggda Tag Helpers för alla sidor i mappen Sidor.

Direktivet @namespace anges på en sida:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

Direktivet @namespace anger sidans namnområde. Direktivet @model behöver inte inkludera namnområdet.

@namespace När direktivet finns i _ViewImports.cshtmltillhandahåller det angivna namnområdet prefixet för det genererade namnområdet på sidan som importerar @namespace direktivet. Resten av det genererade namnområdet (suffixdelen) är den punktavgränsade relativa sökvägen mellan mappen som innehåller _ViewImports.cshtml och mappen som innehåller sidan.

Till exempel anger PageModel klassen Pages/Customers/Edit.cshtml.cs uttryckligen namnområdet.

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

Filen Pages/_ViewImports.cshtml anger följande namnområde:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Det genererade namnområdet för sidan Pages/Customers/Edit.cshtmlRazor är samma som PageModel klassen.

@namespace fungerar också med konventionella Razor vyer.

Överväg visningsfilen 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>

Den uppdaterade Pages/Create.cshtml vyfilen med _ViewImports.cshtml och den föregående layoutfilen:

@page
@model CreateModel

<p>Enter a customer name:</p>

<form method="post">
    Name:
    <input asp-for="Customer.Name" />
    <input type="submit" />
</form>

I föregående kod importerades _ViewImports.cshtml namnområdet och Tag Helpers. Layoutfilen importerade JavaScript-filerna.

ProjektetRazor Pages starter innehåller Pages/_ValidationScriptsPartial.cshtml, som kopplar upp validering på klientsidan.

Mer information om partiella vyer finns i Partiella vyer i ASP.NET Core.

URL-generering för sidor

Sidan Create , som visades tidigare, använder 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");
    }
}

Appen har följande fil-/mappstruktur:

  • /Sidor

    • Index.cshtml

    • Privacy.cshtml

    • /Kunder

      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml

Sidorna Pages/Customers/Create.cshtml och Pages/Customers/Edit.cshtml omdirigeras till Pages/Customers/Index.cshtml efter framgång. Strängen ./Index är ett relativt sidnamn som används för att komma åt föregående sida. Den används för att generera URL:er till sidan Pages/Customers/Index.cshtml . Till exempel:

  • Url.Page("./Index", ...)
  • <a asp-page="./Index">Customers Index Page</a>
  • RedirectToPage("./Index")

Det absoluta sidnamnet /Index används för att generera URL:er till Pages/Index.cshtml sidan. Till exempel:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">Home Index Page</a>
  • RedirectToPage("/Index")

Sidnamnet är sökvägen till sidan från rotmappen /Pages , inklusive en inledande / (till exempel /Index). De föregående exemplen på URL-generering erbjuder förbättrade alternativ och funktioner jämfört med hårdkodning av en URL. URL-genereringen använder routning och kan generera och koda parametrar beroende på hur vägen definieras i målsökvägen.

URL-generering för sidor stöder relativa namn. I följande tabell visas vilken indexsida som väljs med hjälp av olika RedirectToPage parametrar i Pages/Customers/Create.cshtml.

RedirectToPage(x) Sida
RedirectToPage("/Index") Sidor/Index
RedirectToPage("./Index"); Sidor/Kunder/Index
RedirectToPage(".. /Index") Sidor/Index
OmdirigeraTillSida("Index") Sidor/Kunder/Index

RedirectToPage("Index"), RedirectToPage("./Index")och RedirectToPage("../Index") är relativa namn. Parametern RedirectToPagekombineras med sökvägen till den aktuella sidan för att beräkna namnet på målsidan.

Relativ namnlänkning är användbar när du skapar platser med en komplex struktur. När relativa namn används för att länka mellan sidor i en mapp:

  • Om du byter namn på en mapp bryts inte de relativa länkarna.
  • Länkarna är inte brutna eftersom de inte innehåller mappnamnet.

Om du vill omdirigera till en sida i ett annat område anger du området:

RedirectToPage("/Index", new { area = "Services" });

Mer information finns i Områden i ASP.NET Core - och Razor Pages-routnings- och appkonventioner i ASP.NET Core.

ViewData-attribut

Data kan skickas till en sida med ViewDataAttribute. Egenskaper med [ViewData] attributet har sina värden lagrade och inlästa från ViewDataDictionary.

I följande exempel tillämpas AboutModel attributet på [ViewData] egenskapen:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

På sidan Om får du åtkomst till egenskapen Title som en modellegenskap:

<h1>@Model.Title</h1>

I layouten läss rubriken från ordlistan ViewData:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempData

ASP.NET Core tillgängliggör TempData. Den här egenskapen lagrar data tills den har lästs. Metoderna Keep och Peek kan användas för att undersöka data utan borttagning. TempData är användbart för omdirigering, när data behövs för mer än en enskild begäran.

Följande kod anger värdet Message för att använda 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");
    }
}

Följande markering i Pages/Customers/Index.cshtml-filen visar värdet av Message med hjälp av TempData.

<h3>Msg: @Model.Message</h3>

Sidmodellen Pages/Customers/Index.cshtml.cs tillämpar [TempData] attributet på Message egenskapen.

[TempData]
public string Message { get; set; }

Mer information finns i TempData.

Flera hanterare per sida

Följande sida genererar markering för två hanterare med hjälp av asp-page-handler Tag Helper:

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

Formuläret i föregående exempel har två skicka-knappar som var och en använder FormActionTagHelper för att skicka till en annan URL. Attributet asp-page-handler är en följeslagare till asp-page. asp-page-handler genererar URL:er som skickar till var och en av de hanteringsmetoder som definieras av en sida. asp-page anges inte eftersom exemplet länkar till den aktuella sidan.

Sidmodellen:

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

Föregående kod använder namngivna hanteringsmetoder. Namngivna hanteringsmetoder skapas genom att texten tas med i namnet efter On<HTTP Verb> och före Async (om det finns). I föregående exempel är sidmetoderna OnPostJoinListAsync och OnPostJoinListUCAsync. När OnPost och Async har tagits bort är JoinList hanterarnamnen och JoinListUC.

<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />

Föregående kod används för att skicka URL-sökvägen till OnPostJoinListAsync, vilket är https://localhost:5001/Customers/CreateFATH?handler=JoinList. URL-sökvägen som skickas till OnPostJoinListUCAsync är https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Anpassade vägar

@page Använd direktivet för att:

  • Ange en anpassad väg till en sida. Vägen till sidan Om kan till exempel anges till /Some/Other/Path med @page "/Some/Other/Path".
  • Lägg till segment till en sidas standardväg. Ett objektsegment kan till exempel läggas till på en sidas standardväg med @page "item".
  • Lägg till parametrar till en sidas standardväg. Till exempel kan en ID-parameter, id, krävas för en sida med @page "{id}".

En rotrelativ sökväg som anges av en tilde (~) i början av sökvägen stöds. Till exempel är @page "~/Some/Other/Path" samma som @page "/Some/Other/Path".

Om du inte gillar frågesträngen ?handler=JoinList i URL:en ändrar du vägen för att placera hanterarnamnet i sökvägsdelen av URL:en. Vägen kan anpassas genom att lägga till en vägmall som omges av dubbla citattecken efter @page direktivet.

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

Föregående kod används för att skicka URL-sökvägen till OnPostJoinListAsync, vilket är https://localhost:5001/Customers/CreateFATH/JoinList. URL-sökvägen som skickas till OnPostJoinListUCAsync är https://localhost:5001/Customers/CreateFATH/JoinListUC.

Följande ?handler innebär att routningsparametern är valfri.

Avancerad konfiguration och inställningar

Konfigurationen och inställningarna i följande avsnitt krävs inte av de flesta appar.

Om du vill konfigurera avancerade alternativ använder du den AddRazorPages överlagring som konfigurerar RazorPagesOptions:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
    {
        options.RootDirectory = "/MyPages";
        options.Conventions.AuthorizeFolder("/MyPages/Admin");
    });
}

RazorPagesOptions Använd för att ange rotkatalogen för sidor eller lägga till programmodellkonventioner för sidor. Mer information om konventioner finns i Razor Auktoriseringskonventioner för sidor.

För att förkompilera vyer, se vykompileringRazor.

Ange att Razor Sidor finns i innehållsroten

Som standardinställning är Razor Pages placerade i katalogen /Pages. Lägg WithRazorPagesAtContentRoot till för att ange att dina Razor sidor finns i innehållsroten (ContentRootPath) i appen:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesAtContentRoot();
}

Ange att Razor Sidor finns i en anpassad rotkatalog

Lägg WithRazorPagesRoot till för att ange att Razor Sidor finns i en anpassad rotkatalog i appen (ange en relativ sökväg):

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesRoot("/path/to/razor/pages");
}

Ytterligare resurser