Présentation de Razor Pages dans ASP.NET CoreIntroduction to Razor Pages in ASP.NET Core

De Rick Anderson et Ryan NowakBy Rick Anderson and Ryan Nowak

Razor Pages constitue un nouvel aspect d’ASP.NET Core MVC qui permet de développer des scénarios orientés page de façon plus simple et plus productive.Razor Pages is a new aspect of ASP.NET Core MVC that makes coding page-focused scenarios easier and more productive.

Si vous cherchez un didacticiel qui utilise l’approche Model-View-Controller, consultez Bien démarrer avec ASP.NET Core MVC.If you're looking for a tutorial that uses the Model-View-Controller approach, see Get started with ASP.NET Core MVC.

Ce document fournit une introduction à Razor Pages.This document provides an introduction to Razor Pages. Il ne s’agit pas d’un didacticiel pas à pas.It's not a step by step tutorial. Si certaines sections vous semblent trop techniques, consultez Bien démarrer avec Razor Pages.If you find some of the sections too advanced, see Get started with Razor Pages. Pour une vue d’ensemble d’ASP.NET Core, consultez Introduction à ASP.NET Core.For an overview of ASP.NET Core, see the Introduction to ASP.NET Core.

PrérequisPrerequisites

Installez l’un des éléments suivants :Install one of the following:

Création d’un projet Razor PagesCreating a Razor Pages project

Pour obtenir des instructions détaillées sur la création d’un projet Razor Pages avec Visual Studio, consultez Bien démarrer avec Razor Pages.See Get started with Razor Pages for detailed instructions on how to create a Razor Pages project using Visual Studio.

Razor PagesRazor Pages

Razor Pages est activé dans Startup.cs :Razor Pages is enabled in Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }
}

Considérez une page de base : Consider a basic page:

@page

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

Le code précédent ressemble beaucoup à un fichier vue Razor.The preceding code looks a lot like a Razor view file. Ce qui le rend différent est la directive @page.What makes it different is the @page directive. @page fait du fichier une action MVC, ce qui signifie qu’il gère les demandes directement, sans passer par un contrôleur.@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page doit être la première directive Razor sur une page.@page must be the first Razor directive on a page. @page affecte le comportement d’autres constructions Razor.@page affects the behavior of other Razor constructs.

Une page similaire, utilisant une classe PageModel, est illustrée dans les deux fichiers suivants.A similar page, using a PageModel class, is shown in the following two files. Le fichier Pages/Index2.cshtml :The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model IndexModel2

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

Le modèle de page Pages/Index2.cshtml.cs :The Pages/Index2.cshtml.cs page model:

using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace RazorPagesIntro.Pages
{
    public class IndexModel2 : PageModel
    {
        public string Message { get; private set; } = "PageModel in C#";

        public void OnGet()
        {
            Message += $" Server time is { DateTime.Now }";
        }
    }
}

Par convention, le fichier de classe PageModel a le même nom que le fichier Page Razor, avec .cs en plus.By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. Par exemple, la page Razor précédente est Pages/Index2.cshtml.For example, the previous Razor Page is Pages/Index2.cshtml. Le fichier contenant la classe PageModel se nomme Pages/Index2.cshtml.cs.The file containing the PageModel class is named Pages/Index2.cshtml.cs.

Les associations des chemins d’URL aux pages sont déterminées par l’emplacement de la page dans le système de fichiers.The associations of URL paths to pages are determined by the page's location in the file system. Le tableau suivant montre un chemin Page Razor et l’URL correspondante :The following table shows a Razor Page path and the matching URL:

Nom et chemin de fichierFile name and path URL correspondantematching URL
/Pages/Index.cshtml/Pages/Index.cshtml / ou /Index/ or /Index
/Pages/Contact.cshtml/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml/Pages/Store/Index.cshtml /Store ou /Store/Index/Store or /Store/Index

Remarques :Notes:

  • Le runtime recherche les fichiers Razor Pages dans le dossier Pages par défaut.The runtime looks for Razor Pages files in the Pages folder by default.
  • Index est la page par défaut quand une URL n’inclut pas de page.Index is the default page when a URL doesn't include a page.

Écriture d’un formulaire de baseWriting a basic form

Razor Pages est conçu pour que les modèles courants utilisés avec les navigateurs web soient faciles à implémenter lors de la création d’une application.Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. La liaison de modèle, les Tag Helpers et les assistances HTML fonctionnent tous avec les propriétés définies dans une classe Page Razor.Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Considérez une page qui implémente un formulaire « Nous contacter » de base pour le modèle Contact :Consider a page that implements a basic "contact us" form for the Contact model:

Pour les exemples de ce document, le DbContext est initialisé dans le fichier Startup.cs.For the samples in this document, the DbContext is initialized in the Startup.cs file.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesContacts.Data;

namespace RazorPagesContacts
{
    public class Startup
    {
        public IHostingEnvironment HostingEnvironment { get; }

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

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
}

Le modèle de données :The data model:

using System.ComponentModel.DataAnnotations;

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

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

Le contexte de la base de données :The db context:

using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Customer> Customers { get; set; }
    }
}

Le fichier vue Pages/Create.cshtml :The Pages/Create.cshtml view file:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

Le modèle de page Pages/Create.cshtml.cs :The Pages/Create.cshtml.cs page model:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

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

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

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

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

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }
    }
}

Par convention, la classe PageModel se nomme <PageName>Model et se trouve dans le même espace de noms que la page.By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

La classe PageModel permet de séparer la logique d’une page de sa présentation.The PageModel class allows separation of the logic of a page from its presentation. Elle définit des gestionnaires de page pour les demandes envoyées à la page et les données utilisées pour l’afficher.It defines page handlers for requests sent to the page and the data used to render the page. Cette séparation permet de gérer les dépendances entre les pages au moyen de l’injection de dépendances et d’effectuer des tests unitaires sur les pages.This separation allows you to manage page dependencies through dependency injection and to unit test the pages.

La page a une méthode de gestionnaire OnPostAsync, qui s’exécute sur les requêtes POST (quand un utilisateur poste le formulaire).The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). Vous pouvez ajouter des méthodes de gestionnaire pour n’importe quel verbe HTTP.You can add handler methods for any HTTP verb. Les gestionnaires les plus courants sont :The most common handlers are:

  • OnGet pour initialiser l’état nécessaire pour la page.OnGet to initialize state needed for the page. Exemple OnGet.OnGet sample.
  • OnPost pour gérer les envois de formulaire.OnPost to handle form submissions.

Le suffixe de nommage Async est facultatif, mais souvent utilisé par convention pour les fonctions asynchrones.The Async naming suffix is optional but is often used by convention for asynchronous functions. Le code OnPostAsync dans l’exemple précédent est similaire à celui que vous écririez normalement dans un contrôleur.The OnPostAsync code in the preceding example looks similar to what you would normally write in a controller. Le code précédent est typique de Razor Pages.The preceding code is typical for Razor Pages. La plupart des primitives MVC comme la liaison de modèle, la validation et les résultats d’action sont partagés.Most of the MVC primitives like model binding, validation, and action results are shared.

La méthode OnPostAsync précédente :The previous OnPostAsync method:

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

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage("/Index");
}

Le flux de base de OnPostAsync :The basic flow of OnPostAsync:

Recherchez les erreurs de validation.Check for validation errors.

  • S’il n’y a aucune erreur, enregistrez les données et redirigez.If there are no errors, save the data and redirect.
  • S’il y a des erreurs, réaffichez la page avec des messages de validation.If there are errors, show the page again with validation messages. La validation côté client est identique à celle des applications ASP.NET Core MVC traditionnelles.Client-side validation is identical to traditional ASP.NET Core MVC applications. Dans de nombreux cas, les erreurs de validation sont détectées sur le client et jamais envoyées au serveur.In many cases, validation errors would be detected on the client, and never submitted to the server.

Quand les données sont entrées correctement, la méthode de gestionnaire OnPostAsync appelle la méthode d’assistance RedirectToPage pour retourner une instance de RedirectToPageResult.When the data is entered successfully, the OnPostAsync handler method calls the RedirectToPage helper method to return an instance of RedirectToPageResult. RedirectToPage est un nouveau résultat d’action, semblable à RedirectToAction ou RedirectToRoute, mais personnalisé pour les pages.RedirectToPage is a new action result, similar to RedirectToAction or RedirectToRoute, but customized for pages. Dans l’exemple précédent, il redirige vers la page Index racine (/Index).In the preceding sample, it redirects to the root Index page (/Index). RedirectToPage est détaillé dans la section Génération d’URL pour les pages.RedirectToPage is detailed in the URL generation for Pages section.

Quand le formulaire envoyé comporte des erreurs de validation (qui sont passées au serveur), la méthode de gestionnaire OnPostAsync appelle la méthode d’assistance Page.When the submitted form has validation errors (that are passed to the server), theOnPostAsync handler method calls the Page helper method. Page retourne une instance de PageResult.Page returns an instance of PageResult. Le retour de Page est similaire à la façon dont les actions dans les contrôleurs retournent View.Returning Page is similar to how actions in controllers return View. PageResult est le type de retour par défaut pour une méthode de gestionnaire.PageResult is the default return type for a handler method. Une méthode de gestionnaire qui retourne void restitue la page.A handler method that returns void renders the page.

La propriété Customer utilise l’attribut [BindProperty] pour accepter la liaison de modèle.The Customer property uses [BindProperty] attribute to opt in to model binding.

public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

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

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

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

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        return RedirectToPage("/Index");
    }
}

Par défaut, les pages Razor lient les propriétés uniquement avec les verbes non-GET.Razor Pages, by default, bind properties only with non-GET verbs. La liaison aux propriétés peut réduire la quantité de code à écrire.Binding to properties can reduce the amount of code you have to write. Elle réduit la quantité de code en utilisant la même propriété pour afficher les champs de formulaire (<input asp-for="Customer.Name" />) et accepter l’entrée.Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name" />) and accept the input.

Note

Pour des raisons de sécurité, vous devez choisir de lier les données de requête GET aux propriétés du modèle de page.For security reasons, you must opt in to binding GET request data to page model properties. Vérifiez l’entrée utilisateur avant de la mapper à des propriétés.Verify user input before mapping it to properties. Le choix de ce comportement est utile pour les scénarios qui reposent sur des valeurs de route ou de chaîne de requête.Opting in to this behavior is useful when addressing scenarios which rely on query string or route values.

Pour lier une propriété sur des requêtes GET, affectez à la propriété SupportsGet de l’attribut [BindProperty] la valeur true : [BindProperty(SupportsGet = true)]To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true: [BindProperty(SupportsGet = true)]

La page d’accueil (Index.cshtml) :The home page (Index.cshtml):

@page
@model RazorPagesContacts.Pages.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h1>Contacts</h1>
<form method="post">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in Model.Customers)
            {
                <tr>
                    <td>@contact.Id</td>
                    <td>@contact.Name</td>
                    <td>
                        <a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
                        <button type="submit" asp-page-handler="delete" 
                                asp-route-id="@contact.Id">delete</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <a asp-page="./Create">Create</a>
</form>

La classe PageModel associée (Index.cshtml.cs) :The associated PageModel class (Index.cshtml.cs):

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

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

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

        public IList<Customer> Customers { get; private set; }

        public async Task OnGetAsync()
        {
            Customers = await _db.Customers.AsNoTracking().ToListAsync();
        }

        public async Task<IActionResult> OnPostDeleteAsync(int id)
        {
            var contact = await _db.Customers.FindAsync(id);

            if (contact != null)
            {
                _db.Customers.Remove(contact);
                await _db.SaveChangesAsync();
            }

            return RedirectToPage();
        }
    }
}

Le fichier Index.cshtml contient le balisage suivant pour créer un lien d’édition pour chaque contact :The Index.cshtml file contains the following markup to create an edit link for each contact:

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

Le tag helper d’ancre a utilisé l’attribut asp-route-{value} pour générer un lien vers la page Edit.The Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. Le lien contient des données d’itinéraire avec l’ID de contact.The link contains route data with the contact ID. Par exemple, http://localhost:5000/Edit/1.For example, http://localhost:5000/Edit/1.

Le fichier Pages/Edit.cshtml :The Pages/Edit.cshtml file:

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

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

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

La première ligne contient la directive @page "{id:int}".The first line contains the @page "{id:int}" directive. La contrainte de routage "{id:int}" indique à la page qu’elle doit accepter les requêtes qui contiennent des données d’itinéraire int.The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. Si une requête à la page ne contient de données d’itinéraire qui peuvent être converties en int, le runtime retourne une erreur HTTP 404 (introuvable).If a request to the page doesn't contain route data that can be converted to an int, the runtime returns an HTTP 404 (not found) error. Pour que l’ID soit facultatif, ajoutez ? à la contrainte de route :To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

Le fichier Pages/Edit.cshtml.cs :The Pages/Edit.cshtml.cs file:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;

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

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

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

        public async Task<IActionResult> OnGetAsync(int id)
        {
            Customer = await _db.Customers.FindAsync(id);

            if (Customer == null)
            {
                return RedirectToPage("/Index");
            }

            return Page();
        }

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

            _db.Attach(Customer).State = EntityState.Modified;

            try
            {
                await _db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                throw new Exception($"Customer {Customer.Id} not found!");
            }

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

Le fichier Index.cshtml contient également le balisage pour créer un bouton Supprimer pour chaque contact client :The Index.cshtml file also contains markup to create a delete button for each customer contact:

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

Lorsque le bouton Supprimer est rendu en HTML, son formaction comprend des paramètres pour :When the delete button is rendered in HTML, its formaction includes parameters for:

  • L’ID du contact client spécifié par l’attribut asp-route-id.The customer contact ID specified by the asp-route-id attribute.
  • Le handler spécifié par l’attribut asp-page-handler.The handler specified by the asp-page-handler attribute.

Voici un exemple de bouton Supprimer rendu avec un ID de contact client de 1:Here is an example of a rendered delete button with a customer contact ID of 1:

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

Quand le bouton est sélectionné, une demande POST de formulaire est envoyée au serveur.When the button is selected, a form POST request is sent to the server. Par convention, le nom de la méthode de gestionnaire est sélectionné en fonction de la valeur du paramètre handler conformément au schéma OnPost[handler]Async.By convention, the name of the handler method is selected based on the value of the handler parameter according to the scheme OnPost[handler]Async.

Étant donné que le handler est delete dans cet exemple, la méthode de gestionnaire OnPostDeleteAsync est utilisée pour traiter la demande POST.Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. Si asp-page-handler est défini avec une autre valeur, telle que remove, une méthode de gestionnaire de page avec le nom OnPostRemoveAsync est sélectionnée.If the asp-page-handler is set to a different value, such as remove, a page handler method with the name OnPostRemoveAsync is selected.

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
    var contact = await _db.Customers.FindAsync(id);

    if (contact != null)
    {
        _db.Customers.Remove(contact);
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}

La méthode OnPostDeleteAsync :The OnPostDeleteAsync method:

  • Accepte l’id de la chaîne de requête.Accepts the id from the query string.
  • Interroge la base de données pour le contact client avec FindAsync.Queries the database for the customer contact with FindAsync.
  • Si le contact client est trouvé, il est supprimé de la liste des contacts client.If the customer contact is found, they're removed from the list of customer contacts. La base de données est mise à jour.The database is updated.
  • Appelle RedirectToPage pour rediriger vers la page Index racine (/Index).Calls RedirectToPage to redirect to the root Index page (/Index).

Marquer les propriétés de page comme RequiredMark page properties as required

Les propriétés définies sur PageModel peuvent être décorées avec l’attribut Required :Properties on a PageModel can be decorated with the Required attribute:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        [Required(ErrorMessage = "Color is required")]
        public string Color { get; set; }

        public IActionResult OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            // Process color.

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

Pour plus d’informations, consultez Validation de modèle.See Model validation for more information.

Gérer des demandes HEAD avec le gestionnaire OnGetManage HEAD requests with the OnGet handler

Les requêtes HEAD vous permettent de récupérer les en-têtes pour une ressource spécifique.HEAD requests allow you to retrieve the headers for a specific resource. Contrairement aux requêtes GET, les requêtes HEAD ne renvoient un corps de réponse.Unlike GET requests, HEAD requests don't return a response body.

En règle générale, un gestionnaire HEAD est créé et appelé pour des demandes HEAD :Ordinarily, a HEAD handler is created and called for HEAD requests:

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

si aucun gestionnaire HEAD (OnHead) n’est défini, les pages Razor reviennent à l’appel du gestionnaire de page GET (OnGet) dans ASP.NET Core 2.1 ou une version ultérieure.If no HEAD handler (OnHead) is defined, Razor Pages falls back to calling the GET page handler (OnGet) in ASP.NET Core 2.1 or later. Dans ASP.NET Core 2.1 et 2.2, ce comportement se produit avec la version SetCompatibilityVersion dans Startup.Configure :In ASP.NET Core 2.1 and 2.2, this behavior occurs with the SetCompatibilityVersion in Startup.Configure:

services.AddMvc()
    .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);

Les modèles par défaut génèrent l'appel SetCompatibilityVersion dans ASP.NET Core 2.1 et 2.2.The default templates generate the SetCompatibilityVersion call in ASP.NET Core 2.1 and 2.2.

SetCompatibilityVersion définit efficacement l’option Pages Razor AllowMappingHeadRequestsToGetHandler sur true.SetCompatibilityVersion effectively sets the Razor Pages option AllowMappingHeadRequestsToGetHandler to true.

Au lieu d’adhérer à tous les comportements de la version 2.1 avec SetCompatibilityVersion, vous pouvez explicitement adhérer à des comportements spécifiques.Rather than opting into all 2.1 behaviors with SetCompatibilityVersion, you can explicitly opt-in to specific behaviors. Le code suivant adhère aux demandes HEAD de mappage avec le gestionnaire GET.The following code opts into the mapping HEAD requests to the GET handler.

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.AllowMappingHeadRequestsToGetHandler = true;
    });

XSRF/CSRF et Razor PagesXSRF/CSRF and Razor Pages

Vous n’avez aucun code à écrire pour la validation anti-contrefaçon.You don't have to write any code for antiforgery validation. La validation et la génération de jetons anti-contrefaçon sont automatiquement incluses dans Razor Pages.Antiforgery token generation and validation are automatically included in Razor Pages.

Utilisation de dispositions, partiels, modèles et Tag Helpers avec Razor PagesUsing Layouts, partials, templates, and Tag Helpers with Razor Pages

Les pages Razor fonctionnent avec toutes les fonctionnalités du moteur de vue Razor.Pages work with all the capabilities of the Razor view engine. Les dispositions, partiels, modèles, Tag Helpers, _ViewStart.cshtml et _ViewImports.cshtml fonctionnent de la même façon que pour les vues Razor classiques.Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, _ViewImports.cshtml work in the same way they do for conventional Razor views.

Nous allons nettoyer un peu cette page en tirant parti de certaines de ces fonctionnalités.Let's declutter this page by taking advantage of some of those capabilities.

Ajoutez une page de disposition à Pages/Shared/_Layout.cshtml :Add a layout page to Pages/Shared/_Layout.cshtml:

Ajoutez une page de disposition à Pages/_Layout.cshtml :Add a layout page to Pages/_Layout.cshtml:

<!DOCTYPE html>
<html>
<head> 
    <title>Razor Pages Sample</title>      
</head>
<body>    
   <a asp-page="/Index">Home</a>
    @RenderBody()  
    <a asp-page="/Customers/Create">Create</a> <br />
</body>
</html>

La disposition :The Layout:

  • Contrôle la disposition de chaque page (à moins que la page ne refuse la disposition).Controls the layout of each page (unless the page opts out of layout).
  • Importe des structures HTML telles que JavaScript et les feuilles de style.Imports HTML structures such as JavaScript and stylesheets.

Pour plus d’informations, consultez Page de disposition.See layout page for more information.

La propriété Layout est définie dans Pages/_ViewStart.cshtml :The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

La disposition est dans le dossier Pages/Shared.The layout is in the Pages/Shared folder. Les pages recherchent d’autres vues (dispositions, modèles, partiels) hiérarchiquement, en commençant dans le même dossier que la page active.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Une disposition dans le dossier Pages/Shared peut être utilisée à partir de n’importe quelle page Razor sous le dossier Pages.A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

Le fichier de disposition doit être placé dans le dossier Pages/Shared.The layout file should go in the Pages/Shared folder.

La disposition est dans le dossier Pages.The layout is in the Pages folder. Les pages recherchent d’autres vues (dispositions, modèles, partiels) hiérarchiquement, en commençant dans le même dossier que la page active.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Une disposition dans le dossier Pages peut être utilisée à partir de n’importe quelle page Razor sous le dossier Pages.A layout in the Pages folder can be used from any Razor page under the Pages folder.

Nous vous recommandons de ne pas placer le fichier de disposition dans le dossier Views/Shared.We recommend you not put the layout file in the Views/Shared folder. Views/Shared est un modèle de vues MVC.Views/Shared is an MVC views pattern. Les pages Razor sont censées s’appuyer sur la hiérarchie des dossiers, pas sur les conventions de chemins.Razor Pages are meant to rely on folder hierarchy, not path conventions.

La recherche de vue à partir d’une page Razor inclut le dossier Pages.View search from a Razor Page includes the Pages folder. Les dispositions, modèles et partiels que vous utilisez avec les contrôleurs MVC et les vues Razor conventionnelles fonctionnent simplement.The layouts, templates, and partials you're using with MVC controllers and conventional Razor views just work.

Ajoutez un fichier Pages/_ViewImports.cshtml :Add a Pages/_ViewImports.cshtml file:

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

@namespace est expliqué plus loin dans le didacticiel.@namespace is explained later in the tutorial. La directive @addTagHelper permet de bénéficier des Tag Helpers intégrés dans toutes les pages du dossier Pages.The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

Quand la directive @namespace est utilisée explicitement sur une page :When the @namespace directive is used explicitly on a page:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

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

La directive définit l’espace de noms pour la page.The directive sets the namespace for the page. La directive @model n’a pas besoin d’inclure l’espace de noms.The @model directive doesn't need to include the namespace.

Quand la directive @namespace est contenue dans _ViewImports.cshtml, l’espace de noms spécifié fournit le préfixe de l’espace de noms généré dans la Page qui importe la directive @namespace.When the @namespace directive is contained in _ViewImports.cshtml, the specified namespace supplies the prefix for the generated namespace in the Page that imports the @namespace directive. Le reste de l’espace de noms généré (la partie suffixe) est le chemin relatif séparé par un point entre le dossier contenant _ViewImports.cshtml et le dossier contenant la page.The rest of the generated namespace (the suffix portion) is the dot-separated relative path between the folder containing _ViewImports.cshtml and the folder containing the page.

Par exemple, la classe PageModel Pages/Customers/Edit.cshtml.cs définit explicitement l’espace de noms :For example, the PageModel class Pages/Customers/Edit.cshtml.cs explicitly sets the namespace:

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

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

        // Code removed for brevity.

Le fichier Pages/_ViewImports.cshtml définit l’espace de noms suivant :The Pages/_ViewImports.cshtml file sets the following namespace:

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

L’espace de noms généré pour la page Razor Pages/Customers/Edit.cshtml est identique à la classe PageModel.The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace fonctionne également avec les vues Razor classiques.@namespace also works with conventional Razor views.

Le fichier de vue Pages/Create.cshtml d’origine :The original Pages/Create.cshtml view file:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

Le fichier vue Pages/Create.cshtml mis à jour :The updated Pages/Create.cshtml view file:

@page
@model CreateModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

Le projet de démarrage de pages Razor contient Pages/_ValidationScriptsPartial.cshtml, qui connecte la validation côté client.The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

Pour plus d'informations sur les affichages partiels, consultez Vues partielles dans ASP.NET Core.For more information on partial views, see Vues partielles dans ASP.NET Core.

Génération d’URL pour les pagesURL generation for Pages

La page Create, illustrée précédemment, utilise RedirectToPage :The Create page, shown previously, uses RedirectToPage:

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

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage("/Index");
}

L’application a la structure de fichiers/dossiers suivante :The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • /Customers/Customers

      • Create.cshtmlCreate.cshtml
      • Edit.cshtmlEdit.cshtml
      • Index.cshtmlIndex.cshtml

Une fois l’opération réussie, les pages Pages/Customers/Create.cshtml et Pages/Customers/Edit.cshtml redirigent vers Pages/Index.cshtml.The Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Index.cshtml after success. La chaîne /Index fait partie de l’URI pour accéder à la page précédente.The string /Index is part of the URI to access the preceding page. La chaîne /Index peut être utilisée pour générer l’URI de la page Pages/Index.cshtml.The string /Index can be used to generate URIs to the Pages/Index.cshtml page. Exemple :For example:

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

Le nom de la page est le chemin de la page à partir du dossier racine /Pages avec un / devant (par exemple, /Index).The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). Les exemples de génération d’URL précédents offrent des options améliorées et des capacités fonctionnelles sur le codage en dur d’une URL.The preceding URL generation samples offer enhanced options and functional capabilities over hardcoding a URL. La génération d’URL utilise le routage et peut générer et encoder des paramètres en fonction de la façon dont l’itinéraire est défini dans le chemin de destination.URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

La génération d’URL pour les pages prend en charge les noms relatifs.URL generation for pages supports relative names. Le tableau suivant montre la page Index sélectionnée avec différents paramètres RedirectToPage à partir de Pages/Customers/Create.cshtml :The following table shows which Index page is selected with different RedirectToPage parameters from Pages/Customers/Create.cshtml:

RedirectToPage(x)RedirectToPage(x) PagePage
RedirectToPage("/Index")RedirectToPage("/Index") Pages/IndexPages/Index
RedirectToPage("./Index");RedirectToPage("./Index"); Pages/Customers/IndexPages/Customers/Index
RedirectToPage("../Index")RedirectToPage("../Index") Pages/IndexPages/Index
RedirectToPage("Index")RedirectToPage("Index") Pages/Customers/IndexPages/Customers/Index

RedirectToPage("Index"), RedirectToPage("./Index") et RedirectToPage("../Index") sont des noms relatifs.RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. Le paramètre RedirectToPage est combiné avec le chemin de la page active pour calculer le nom de la page de destination.The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

La liaison de nom relatif est utile lors de la création de sites avec une structure complexe.Relative name linking is useful when building sites with a complex structure. Si vous utilisez des noms relatifs pour établir une liaison entre les pages d’un dossier, vous pouvez renommer ce dossier.If you use relative names to link between pages in a folder, you can rename that folder. Tous les liens fonctionneront encore (car ils n’incluent pas le nom du dossier).All the links still work (because they didn't include the folder name).

Attribut ViewDataViewData attribute

Les données peuvent être passées à une page avec ViewDataAttribute.Data can be passed to a page with ViewDataAttribute. Les valeurs des propriétés définies sur des contrôleurs ou sur des modèles de page Razor décorés avec [ViewData] sont stockées et chargées à partir de ViewDataDictionary.Properties on controllers or Razor Page models decorated with [ViewData] have their values stored and loaded from the ViewDataDictionary.

Dans l’exemple suivant, AboutModel contient une propriété Title décorée avec [ViewData].In the following example, the AboutModel contains a Title property decorated with [ViewData]. La propriété Title a pour valeur le titre de la page À propos de :The Title property is set to the title of the About page:

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

    public void OnGet()
    {
    }
}

Dans la page À propos de, accédez à la propriété Title en tant que propriété de modèle :In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

Dans la disposition, le titre est lu à partir du dictionnaire ViewData :In the layout, the title is read from the ViewData dictionary:

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

TempDataTempData

ASP.NET Core expose la propriété TempData sur un contrôleur.ASP.NET Core exposes the TempData property on a controller. Cette propriété stocke les données jusqu’à ce qu’elles soient lues.This property stores data until it's read. Vous pouvez utiliser les méthodes Keep et Peek pour examiner les données sans suppression.The Keep and Peek methods can be used to examine the data without deletion. TempData est utile pour la redirection, quand des données sont nécessaires pour plusieurs requêtes.TempData is useful for redirection, when data is needed for more than a single request.

L’attribut [TempData] est une nouveauté dans ASP.NET Core 2.0. Il est pris en charge sur les contrôleurs et les pages.The [TempData] attribute is new in ASP.NET Core 2.0 and is supported on controllers and pages.

Le code suivant définit la valeur de Message à l’aide de TempData :The following code sets the value of Message using 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");
    }
}

Le balisage suivant dans le fichier Pages/Customers/Index.cshtml affiche la valeur de Message à l’aide de TempData.The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

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

Le modèle de page Pages/Customers/Index.cshtml.cs applique l’attribut [TempData] à la propriété Message.The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

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

Pour plus d’informations, consultez TempData.See TempData for more information.

Plusieurs gestionnaires par pageMultiple handlers per page

La page suivante génère un balisage pour deux gestionnaires de page à l’aide du Tag Helper asp-page-handler :The following page generates markup for two page handlers using the 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>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

Le formulaire dans l’exemple précédent contient deux boutons d’envoi, chacun utilisant FormActionTagHelper pour envoyer à une URL différente.The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. L’attribut asp-page-handler est un complément de asp-page.The asp-page-handler attribute is a companion to asp-page. asp-page-handler génère des URL qui envoient à chacune des méthodes de gestionnaire définies par une page.asp-page-handler generates URLs that submit to each of the handler methods defined by a page. asp-page n’est pas spécifié, car l’exemple établit une liaison à la page active.asp-page isn't specified because the sample is linking to the current page.

Le modèle de page :The page model:

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?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}

Le code précédent utilise des méthodes de gestionnaire nommées.The preceding code uses named handler methods. Pour créer des méthodes de gestionnaire nommées, il faut prendre le texte dans le nom après On<HTTP Verb> et avant Async (le cas échéant).Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). Dans l’exemple précédent, les méthodes de page sont OnPostJoinListAsync et OnPostJoinListUCAsync.In the preceding example, the page methods are OnPostJoinListAsync and OnPostJoinListUCAsync. Avec OnPost et Async supprimés, les noms des gestionnaires sont JoinList et JoinListUC.With OnPost and Async removed, the handler names are JoinList and JoinListUC.

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

Avec le code précédent, le chemin d’URL qui envoie à OnPostJoinListAsync est http://localhost:5000/Customers/CreateFATH?handler=JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is http://localhost:5000/Customers/CreateFATH?handler=JoinList. Le chemin d’URL qui envoie à OnPostJoinListUCAsync est http://localhost:5000/Customers/CreateFATH?handler=JoinListUC.The URL path that submits to OnPostJoinListUCAsync is http://localhost:5000/Customers/CreateFATH?handler=JoinListUC.

Routes personnaliséesCustom routes

Utilisez la directive @page pour :Use the @page directive to:

  • Spécifier une route personnalisée vers une page.Specify a custom route to a page. Par exemple, la route vers la page À propos peut être définie sur /Some/Other/Path avec @page "/Some/Other/Path".For example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • Ajouter des segments à la route par défaut d’une page.Append segments to a page's default route. Par exemple, un segment « item » peut être ajouté à la route par défaut d’une page avec @page "item".For example, an "item" segment can be added to a page's default route with @page "item".
  • Ajouter des paramètres à la route par défaut d’une page.Append parameters to a page's default route. Par exemple, un paramètre d’ID, id, peut être nécessaire pour une page avec @page "{id}".For example, an ID parameter, id, can be required for a page with @page "{id}".

Un chemin relatif racine désigné par un tilde (~) au début du chemin est pris en charge.A root-relative path designated by a tilde (~) at the beginning of the path is supported. Par exemple, @page "~/Some/Other/Path" est identique à @page "/Some/Other/Path".For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

Vous pouvez remplacer la chaîne de requête ?handler=JoinList dans l’URL par un segment de routage /JoinList en spécifiant le modèle de routage @page "{handler?}".You can change the query string ?handler=JoinList in the URL to a route segment /JoinList by specifying the route template @page "{handler?}".

Si vous ne voulez pas avoir la chaîne de requête ?handler=JoinList dans l’URL, vous pouvez changer l’itinéraire pour placer le nom du gestionnaire dans la partie chemin de l’URL.If you don't like the query string ?handler=JoinList in the URL, you can change the route to put the handler name in the path portion of the URL. Vous pouvez personnaliser l’itinéraire en ajoutant un modèle d’itinéraire placé entre des guillemets doubles après la directive @page.You can customize the route by adding a route template enclosed in double quotes after the @page directive.

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

Avec le code précédent, le chemin d’URL qui envoie à OnPostJoinListAsync est http://localhost:5000/Customers/CreateFATH/JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is http://localhost:5000/Customers/CreateFATH/JoinList. Le chemin d’URL qui envoie à OnPostJoinListUCAsync est http://localhost:5000/Customers/CreateFATH/JoinListUC.The URL path that submits to OnPostJoinListUCAsync is http://localhost:5000/Customers/CreateFATH/JoinListUC.

Le ? suivant handler signifie que le paramètre d’itinéraire est facultatif.The ? following handler means the route parameter is optional.

Configuration et paramètresConfiguration and settings

Pour configurer les options avancées, utilisez la méthode d’extension AddRazorPagesOptions sur le générateur MVC :To configure advanced options, use the extension method AddRazorPagesOptions on the MVC builder:

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

Actuellement, vous pouvez utiliser RazorPagesOptions pour définir le répertoire racine pour les pages, ou ajouter des conventions de modèle d’application pour les pages.Currently you can use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. Nous permettrons à l’avenir une plus grande extensibilité en ce sens.We'll enable more extensibility this way in the future.

Pour précompiler des vues, consultez Compilation de vue Razor.To precompile views, see Razor view compilation .

Téléchargez ou affichez des exemples de code.Download or view sample code.

Consultez Bien démarrer avec Razor Pages qui s’appuie sur cette introduction.See Get started with Razor Pages, which builds on this introduction.

Spécifier que les pages Razor se trouvent à la racine du contenuSpecify that Razor Pages are at the content root

Par défaut, les pages Razor sont associées à la racine /Pages.By default, Razor Pages are rooted in the /Pages directory. Ajoutez WithRazorPagesAtContentRoot à AddMvc pour spécifier que vos pages Razor se trouvent à la racine de contenu (ContentRootPath) de l’application :Add WithRazorPagesAtContentRoot to AddMvc to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesAtContentRoot();

Spécifier que les pages Razor se trouvent dans un répertoire racine personnaliséSpecify that Razor Pages are at a custom root directory

Ajoutez WithRazorPagesRoot à AddMvc pour spécifier que vos pages Razor se trouvent dans le répertoire racine personnalisé de l’application (fournissez un chemin relatif) :Add WithRazorPagesRoot to AddMvc to specify that your Razor Pages are at a custom root directory in the app (provide a relative path):

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesRoot("/path/to/razor/pages");

Ressources supplémentairesAdditional resources