Введение в Razor Pages в ASP.NET CoreIntroduction to Razor Pages in ASP.NET Core

Авторы: Рик Андерсон (Rick Anderson) и Райан Новак (Ryan Nowak)By Rick Anderson and Ryan Nowak

Razor Pages делает создание кодов сценариев для страниц проще и эффективнее по сравнению с использованием контроллеров и представлений.Razor Pages can make coding page-focused scenarios easier and more productive than using controllers and views.

Если вам нужно руководство, использующее подход "модель-представление-контроллер", см. статью Начало работы с MVC в ASP.NET Core.If you're looking for a tutorial that uses the Model-View-Controller approach, see Get started with ASP.NET Core MVC.

Этот документ содержит вводные сведения о Razor Pages.This document provides an introduction to Razor Pages. Это не пошаговое руководство.It's not a step by step tutorial. Если некоторые разделы покажутся вам слишком сложными, см. Начало работы с Razor Pages.If you find some of the sections too advanced, see Get started with Razor Pages. Общие сведения об ASP.NET Core см. в разделе Введение в ASP.NET Core.For an overview of ASP.NET Core, see the Introduction to ASP.NET Core.

Предварительные требованияPrerequisites

Создание проекта Razor PagesCreate a Razor Pages project

Подробные инструкции по созданию проекта Razor Pages см. в статье Начало работы с Razor Pages.See Get started with Razor Pages for detailed instructions on how to create a Razor Pages project.

Razor PagesRazor Pages

Razor Pages активируется в файле Startup.cs:Razor Pages is enabled in 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();
        });
    }
}

Рассмотрим простую страницу: Consider a basic page:

@page

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

Приведенный выше код выглядит как Razor файл представления, используемый в приложениях ASP.NET Core с контроллерами и представлениями.The preceding code looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. Он отличается от него только директивой @page.What makes it different is the @page directive. Директива @page превращает файл в действие MVC, а значит обрабатывает запросы напрямую, минуя контроллер.@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page должна быть первой директивой Razor на странице.@page must be the first Razor directive on a page. @page влияет на поведение всех остальных конструкций Razor.@page affects the behavior of other Razor constructs. Имена файлов Razor Pages имеют суффикс .cshtml.Razor Pages file names have a .cshtml suffix.

Похожая страница с использованием класса PageModel показана в следующих двух файлах.A similar page, using a PageModel class, is shown in the following two files. Файл Pages/Index2.cshtml:The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model Index2Model

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

Модель страницы Pages/Index2.cshtml.cs:The Pages/Index2.cshtml.cs page model:

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

Как правило, файл класса PageModel называется так же, как файл Razor Pages, но с расширением CS.By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. Например, представленная выше страница Razor Pages называется Pages/Index2.cshtml.For example, the previous Razor Page is Pages/Index2.cshtml. Файл, содержащий класс PageModel, называется Pages/Index2.cshtml.cs.The file containing the PageModel class is named Pages/Index2.cshtml.cs.

Сопоставления URL-адресов со страницами определяются расположением конкретной страницы в файловой системе.The associations of URL paths to pages are determined by the page's location in the file system. В приведенной ниже таблице показаны пути Razor Pages и соответствующие URL-адреса.The following table shows a Razor Page path and the matching URL:

Имя файла и путьFile name and path Соответствующий URLmatching URL
/Pages/index.cshtml/Pages/Index.cshtml / или /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 или /Store/Index/Store or /Store/Index

Примечания.Notes:

  • Среда выполнения по умолчанию ищет файлы Razor Pages в папке Pages.The runtime looks for Razor Pages files in the Pages folder by default.
  • Если в URL-адресе не указана конкретная страница, по умолчанию открывается страница Index.Index is the default page when a URL doesn't include a page.

Создание простой формыWrite a basic form

Razor Pages предназначена для упрощения реализации типовых шаблонов, которые используются в браузерах, при создании приложения.Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. Привязки модели, вспомогательные функции тегов и вспомогательные методы HTML отлично работают со свойствами, определенными в классе Razor Pages.Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Рассмотрим страницу с простой формой связи для модели Contact.Consider a page that implements a basic "contact us" form for the Contact model:

В представленных в этой статье примерах DbContext инициализируется в файле Startup.cs.For the samples in this document, the DbContext is initialized in the Startup.cs file.

Для работы с базой данных в памяти требуется пакет NuGet Microsoft.EntityFrameworkCore.InMemory.The in memory database requires the Microsoft.EntityFrameworkCore.InMemory NuGet package.

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

Модель данных:The data model:

using System.ComponentModel.DataAnnotations;

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

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

Контекст базы данных:The db context:

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

Файл представления Pages/Create.cshtml:The Pages/Create.cshtml view file:

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

Модель страницы Pages/Create.cshtml.cs:The Pages/Create.cshtml.cs page model:

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

Как правило, класс PageModel называется <PageName>Model и находится в том же пространстве имен, что и страница.By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

Класс PageModel позволяет разделять логику страницы и ее представление.The PageModel class allows separation of the logic of a page from its presentation. Он определяет обработчики страницы для запросов, отправляемых на страницу, а также данные для ее визуализации.It defines page handlers for requests sent to the page and the data used to render the page. Это разделение позволяет:This separation allows:

Страница содержит метод обработчика OnPostAsync, который выполняется по запросам POST (когда пользователь публикует форму).The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). Можно добавить методы обработчика для любой HTTP-команды.Handler methods for any HTTP verb can be added. Наиболее распространенные обработчикиThe most common handlers are:

  • OnGet — инициализация необходимого для страницы состояния.OnGet to initialize state needed for the page. В приведенном выше коде метод OnGet отображает страницу Razor CreateModel.cshtml.In the preceding code, the OnGet method displays the CreateModel.cshtml Razor Page.
  • OnPost — обработка отправленных через форму данных.OnPost to handle form submissions.

Суффикс Async не является обязательным, но часто используется для асинхронных функций.The Async naming suffix is optional but is often used by convention for asynchronous functions. Этот код типичен для Razor Pages.The preceding code is typical for Razor Pages.

Если вы знакомы с приложениями ASP.NET, использующими контроллеры и представления:If you're familiar with ASP.NET apps using controllers and views:

  • Код OnPostAsync в предыдущем примере похож на стандартный код контроллера.The OnPostAsync code in the preceding example looks similar to typical controller code.
  • Большинство примитивов MVC, включая привязку модели, проверку и результаты действий, одинаково работают с контроллерами и Razor Pages.Most of the MVC primitives like model binding, validation, and action results work the same with Controllers and Razor Pages.

Предыдущий метод OnPostAsync:The previous OnPostAsync method:

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

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

    return RedirectToPage("./Index");
}

Простая схема OnPostAsync:The basic flow of OnPostAsync:

Проверка на наличие ошибок проверки.Check for validation errors.

  • Если ошибок нет, сохранение данных и перенаправление.If there are no errors, save the data and redirect.
  • Если есть ошибки, отображение страницы с сообщениями проверки.If there are errors, show the page again with validation messages. Во многих случаях ошибки проверки выявляются на клиентском компьютере и на сервер не отправляются.In many cases, validation errors would be detected on the client, and never submitted to the server.

Файл представления Pages/Create.cshtml:The Pages/Create.cshtml view file:

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

Преобразованный HTML из Pages/Create.cshtml:The rendered HTML from 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>

В приведенном выше коде, разместив форму:In the previous code, posting the form:

  • С допустимыми данными:With valid data:

    • Метод обработчика OnPostAsync вызывает вспомогательный метод RedirectToPage.The OnPostAsync handler method calls the RedirectToPage helper method. RedirectToPage возвращает экземпляр RedirectToPageResult.RedirectToPage returns an instance of RedirectToPageResult. RedirectToPage:RedirectToPage:

      • Является результатом действия.Is an action result.
      • Аналогичен RedirectToAction или RedirectToRoute (используется в контроллерах и представлениях).Is similar to RedirectToAction or RedirectToRoute (used in controllers and views).
      • Настраивается для страниц.Is customized for pages. В приведенном выше примере он выполняет перенаправление на корневую страницу индекса (/Index).In the preceding sample, it redirects to the root Index page (/Index). Более подробно RedirectToPage рассматривается в разделе Создание URL для страниц.RedirectToPage is detailed in the URL generation for Pages section.
  • С ошибками проверки, передаваемыми на сервер:With validation errors that are passed to the server:

    • Метод обработчика OnPostAsync вызывает вспомогательный метод Page.The OnPostAsync handler method calls the Page helper method. Page возвращает экземпляр PageResult.Page returns an instance of PageResult. Возвращение Page аналогично тому, как действия в контроллерах возвращают View.Returning Page is similar to how actions in controllers return View. PageResult — тип возвращаемого значения по умолчанию для метода обработчика.PageResult is the default return type for a handler method. Метод обработчика, вернувший void, визуализирует страницу.A handler method that returns void renders the page.
    • В предыдущем примере публикация формы без значения приводит к тому, что ModelState.IsValid возвращает значение false.In the preceding example, posting the form with no value results in ModelState.IsValid returning false. В этом примере на клиенте не отображаются ошибки проверки.In this sample, no validation errors are displayed on the client. Обработка ошибок проверки рассматривается далее в этом документе.Validation error handing is covered later in this document.
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
    
        _context.Customers.Add(Customer);
        await _context.SaveChangesAsync();
    
        return RedirectToPage("./Index");
    }
    
  • С ошибками проверки, обнаруженными при проверке на стороне клиента:With validation errors detected by client side validation:

    • Данные не отправляются на сервер.Data is not posted to the server.
    • Проверка на стороне клиента описана далее в этом документе.Client-side validation is explained later in this document.

Для указания согласия на привязку модели в свойстве Customer используется атрибут [BindProperty]:The Customer property uses [BindProperty] attribute to opt in to model binding:

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]не следует использовать в моделях, содержащих свойства, которые не должны изменяться клиентом.[BindProperty] should not be used on models containing properties that should not be changed by the client. Дополнительные сведения см. в этом разделе.For more information, see Overposting.

По умолчанию Razor привязывает свойства ко всем командам, кроме GET.Razor Pages, by default, bind properties only with non-GET verbs. Привязка к свойствам устраняет необходимость в написании кода для преобразования данных HTTP в тип модели.Binding to properties removes the need to writing code to convert HTTP data to the model type. Привязка уменьшает код за счет того, что для визуализации полей формы (<input asp-for="Customer.Name">) и получения входных данных используется одно и то же свойство.Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name">) and accept the input.

Предупреждение

В целях безопасности вам следует задать привязку данных запроса GET к свойствам страничной модели.For security reasons, you must opt in to binding GET request data to page model properties. Проверьте введенные данные пользователя, прежде чем сопоставлять их со свойствами.Verify user input before mapping it to properties. Привязка GET полезна при обращении к сценариям, использующим строку запроса или значения маршрутов.Opting into GET binding is useful when addressing scenarios that rely on query string or route values.

Чтобы привязать свойство к запросам GET, задайте для свойства SupportsGet атрибута [BindProperty] значение true:To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true:

[BindProperty(SupportsGet = true)]

Дополнительные сведения см. в видео, посвященном обсуждению привязки к запросу GET, на канале YouTube.For more information, see ASP.NET Core Community Standup: Bind on GET discussion (YouTube).

Просмотр файла представления Pages/Create.cshtml:Reviewing the Pages/Create.cshtml view file:

@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>
  • В приведенном выше коде вспомогательная функция тега ввода <input asp-for="Customer.Name" /> привязывает элемент <input> HTML к выражению модели Customer.Name.In the preceding code, the input tag helper <input asp-for="Customer.Name" /> binds the HTML <input> element to the Customer.Name model expression.
  • Директива @addTagHelper обеспечивает доступность вспомогательных функций тегов.@addTagHelper makes Tag Helpers available.

Домашняя страницаThe home page

Index.cshtml — это домашняя страница:Index.cshtml is the home page:

@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>
                        <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 New</a>
</form>

Связанный класс PageModel (Index.cshtml.cs):The associated PageModel class (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();
    }
}

Файл index.cshtml содержит следующую разметку:The Index.cshtml file contains the following markup:

<td>

Вспомогательный тег привязки <a /a> использовал атрибут asp-route-{value} для создания ссылки на страницу редактирования.The <a /a> Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. Эта ссылка содержит данные о маршруте с идентификатором контактного лица.The link contains route data with the contact ID. Например, https://localhost:5001/Edit/1.For example, https://localhost:5001/Edit/1. Вспомогательные функции тегов позволяют серверному коду участвовать в создании и отображении HTML-элементов в файлах Razor.Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.

Файл Index.cshtml содержит разметку для создания кнопки удаления у каждого контакта клиента:The Index.cshtml file contains markup to create a delete button for each customer contact:

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

Отображаемый HTML:The rendered HTML:

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

Во время обработки кнопки удаления в HTML ее formaction включает параметры для следующего:When the delete button is rendered in HTML, its formaction includes parameters for:

  • Идентификатор контакта клиента, указанный атрибутом asp-route-id.The customer contact ID, specified by the asp-route-id attribute.
  • Параметр handler, указанный атрибутом asp-page-handler.The handler, specified by the asp-page-handler attribute.

При выборе кнопки на сервер отправляется запрос формы POST.When the button is selected, a form POST request is sent to the server. По соглашению имя метода обработчика выбирается на основе значения параметра handler в соответствии со схемой 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.

Так как handler — delete в этом примере, метод обработчика OnPostDeleteAsync используется для обработки запроса POST.Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. Если asp-page-handler имеет другое значение, например remove, выбирается метод обработчика с именем OnPostRemoveAsync.If the asp-page-handler is set to a different value, such as remove, a handler method with the name OnPostRemoveAsync is selected.

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:The OnPostDeleteAsync method:

  • Получает id из строки запроса.Gets the id from the query string.
  • Отправляет в базу данных запрос контакта клиента с FindAsync.Queries the database for the customer contact with FindAsync.
  • Если контакт клиента найден, он удаляется, и база данных обновляется.If the customer contact is found, it's removed and the database is updated.
  • Вызывает RedirectToPage для перенаправления на корневую страницу индекса (/Index).Calls RedirectToPage to redirect to the root Index page (/Index).

Файл Edit.cshtmlThe Edit.cshtml file

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


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

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

Первая строка содержит директиву @page "{id:int}".The first line contains the @page "{id:int}" directive. Ограничение маршрутизации "{id:int}" указывает, что страница должна принимать обращенные к ней запросы, которые содержат данные о маршруте int.The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. Если запрос к странице не содержит данные о маршруте, которые можно конвертировать в int, среда выполнения возвращает ошибку HTTP 404 (не найдено).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. Чтобы сделать идентификатор необязательным, добавьте ? к ограничению маршрута:To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

Файл Edit.cshtml.cs:The Edit.cshtml.cs file:

public class EditModel : PageModel
{
    private readonly CustomerDbContext _context;

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

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

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

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

        return Page();
    }

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

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

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

        return RedirectToPage("./Index");
    }

}

ПроверкаValidation

Правила проверки:Validation rules:

  • Декларативно задаются в классе Model.Are declaratively specified in the model class.
  • Применяются везде в приложении.Are enforced everywhere in the app.

Пространство имен System.ComponentModel.DataAnnotations предоставляет набор встроенных атрибутов проверки, которые декларативно применяются к классу или свойству.The System.ComponentModel.DataAnnotations namespace provides a set of built-in validation attributes that are applied declaratively to a class or property. Кроме того, DataAnnotations содержит атрибуты форматирования (такие как [DataType]), которые обеспечивают форматирование и не предназначены для проверки.DataAnnotations also contains formatting attributes like [DataType] that help with formatting and don't provide any validation.

Рассмотрим модель Customer:Consider the Customer model:

using System.ComponentModel.DataAnnotations;

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

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

Используя следующий файл представления Create.cshtml:Using the following Create.cshtml view file:

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

Предыдущий код:The preceding code:

  • Включает jQuery и скрипты проверки jQuery.Includes jQuery and jQuery validation scripts.

  • Использует вспомогательные функции тегов <div /> и <span /> для следующих задач:Uses the <div /> and <span /> Tag Helpers to enable:

    • Проверка на стороне клиента.Client-side validation.
    • Отображение ошибок при проверке.Validation error rendering.
  • Генерирует следующий HTML:Generates the following 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>
    

При публикации формы создания без значения имени в форме отображается сообщение об ошибке: "Поле имени является обязательным".Posting the Create form without a name value displays the error message "The Name field is required." в форме.on the form. Если на клиенте включен JavaScript, браузер отображает ошибку без отправки на сервер.If JavaScript is enabled on the client, the browser displays the error without posting to the server.

Атрибут [StringLength(10)] создает data-val-length-max="10" в отображаемом HTML-коде.The [StringLength(10)] attribute generates data-val-length-max="10" on the rendered HTML. data-val-length-max не дает браузерам ввести больше заданной максимальной длины.data-val-length-max prevents browsers from entering more than the maximum length specified. Если для изменения и воспроизведения записи используется средство, например Fiddler, выполните следующие действия:If a tool such as Fiddler is used to edit and replay the post:

  • С именем, превышающим 10.With the name longer than 10.
  • Возвращается сообщение об ошибке: "Имя поля должно быть строкой с максимальной длиной 10".The error message "The field Name must be a string with a maximum length of 10." .is returned.

Рассмотрим следующую модель Movie:Consider the following Movie model:

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]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; }
    }

Атрибуты проверки определяют поведение для свойств модели, к которым они применяются:The validation attributes specify behavior to enforce on the model properties they're applied to:

  • Атрибуты Required и MinimumLength указывают, что свойство должно иметь значение. Тем не менее, чтобы удовлетворить требованиям проверки, пользователю достаточно ввести пробел.The Required and MinimumLength attributes indicate that a property must have a value, but nothing prevents a user from entering white space to satisfy this validation.

  • Атрибут RegularExpression ограничивает набор допустимых для ввода символов.The RegularExpression attribute is used to limit what characters can be input. В приведенном выше коде в Genre:In the preceding code, "Genre":

    • должны использоваться только буквы;Must only use letters.
    • первая буква должна быть прописной;The first letter is required to be uppercase. пробелы, цифры и специальные символы не допускаются.White space, numbers, and special characters are not allowed.
  • В RegularExpression Rating:The RegularExpression "Rating":

    • первый символ должен быть прописной буквой;Requires that the first character be an uppercase letter.
    • допускаются специальные символы и цифры, а также последующие пробелы.Allows special characters and numbers in subsequent spaces. Значение "PG-13" допустимо для рейтинга, но недопустимо для жанра."PG-13" is valid for a rating, but fails for a "Genre".
  • Атрибут Range ограничивает диапазон значений.The Range attribute constrains a value to within a specified range.

  • Атрибут StringLength задает максимальную и при необходимости минимальную длину строкового свойства.The StringLength attribute sets the maximum length of a string property, and optionally its minimum length.

  • Типы значений (например, decimal, int, float, DateTime) по своей природе являются обязательными и не требуют атрибута [Required].Value types (such as decimal, int, float, DateTime) are inherently required and don't need the [Required] attribute.

На странице создания для модели Movie отображаются ошибки с недопустимыми значениями:The Create page for the Movie model shows displays errors with invalid values:

Форма просмотра фильма с несколькими ошибками проверки jQuery на стороне клиента

Дополнительные сведения можно найти в разделеFor more information, see:

Обработка запросов HEAD с помощью вызова резервного обработчика OnGetHandle HEAD requests with an OnGet handler fallback

Запросы HEAD позволяют получать заголовки для определенного ресурса.HEAD requests allow retrieving the headers for a specific resource. В отличие от запросов GET запросы HEADне возвращают текст ответа.Unlike GET requests, HEAD requests don't return a response body.

Обработчик OnHead обычно создается и вызывается для выполнения запросов HEAD:Ordinarily, an OnHead handler is created and called for HEAD requests:

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

Если обработчик OnHead не определен, Razor Pages выполнит вызов обработчика OnGet.Razor Pages falls back to calling the OnGet handler if no OnHead handler is defined.

XSRF/CSRF и Razor PagesXSRF/CSRF and Razor Pages

В Razor Pages реализована проверка для защиты от подделки.Razor Pages are protected by Antiforgery validation. FormTagHelper вставляет маркеры защиты от подделки в элементы HTML-форм.The FormTagHelper injects antiforgery tokens into HTML form elements.

Использование макетов, частичных реплик, шаблонов и вспомогательных функций тегов с Razor PagesUsing Layouts, partials, templates, and Tag Helpers with Razor Pages

Pages работает со всеми функциями подсистемы просмотра Razor.Pages work with all the capabilities of the Razor view engine. Макеты, частичные реплики, шаблоны, вспомогательные функции тегов, а также файлы _ViewStart.cshtml и _ViewImports.cshtml работают точно так же, как и в стандартных представлениях Razor.Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, and _ViewImports.cshtml work in the same way they do for conventional Razor views.

Давайте упростим нашу страницу с помощью некоторых из этих функций.Let's declutter this page by taking advantage of some of those capabilities.

Добавим макет страницы в файл Pages/Shared/_Layout.cshtml.Add a layout page to 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>

Этот макет:The Layout:

  • управляет макетом каждой страницы (кроме страниц с отказом от макета);Controls the layout of each page (unless the page opts out of layout).
  • импортирует HTML-структуры, такие как JavaScript и таблицы стилей.Imports HTML structures such as JavaScript and stylesheets.
  • Содержимое страницы Razor отображается в том месте, где вызывается @RenderBody().The contents of the Razor page are rendered where @RenderBody() is called.

Дополнительные сведения см. здесь.For more information, see layout page.

Свойство Layout определяется в файле Pages/_ViewStart.cshtml:The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Макет хранится в папке Pages/Shared.The layout is in the Pages/Shared folder. Pages ищет другие представления (макеты, шаблоны, частичные реплики) в иерархическом порядке, начиная с той папки, где находится текущая страница.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Макет в папке Pages/Shared можно использовать на любой странице Razor, которая находится в папке Pages.A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

Файл макета следует поместить в папку Pages/Shared.The layout file should go in the Pages/Shared folder.

Корпорация Майкрософт рекомендует не размещать файл макета в папке Views/Shared.We recommend you not put the layout file in the Views/Shared folder. Views/Shared — это шаблон представлений MVC.Views/Shared is an MVC views pattern. Razor Pages опирается на иерархию папок, а не на условные обозначения путей.Razor Pages are meant to rely on folder hierarchy, not path conventions.

Поиск представлений в Razor Pages охватывает папку Pages.View search from a Razor Page includes the Pages folder. Макеты, шаблоны и частичные реплики работают с контроллерами MVC и стандартными представлениями Razor.The layouts, templates, and partials used with MVC controllers and conventional Razor views just work.

Добавим файл Pages/_ViewImports.cshtml:Add a Pages/_ViewImports.cshtml file:

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

@namespace описывается далее в этом руководстве.@namespace is explained later in the tutorial. Директива @addTagHelper добавляет встроенные вспомогательные теги на все страницы в папке Pages.The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

Директива @namespace, заданная на странице:The @namespace directive set on a page:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

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

Директива @namespace задает пространство имен для страницы.The @namespace directive sets the namespace for the page. Включать пространство имен в директиву @model не требуется.The @model directive doesn't need to include the namespace.

Если директива @namespace содержится в файле _ViewImports.cshtml, указанное пространство имен определяет префикс для созданного в Pages пространства имен, куда импортируется директива @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. Остальная часть созданного пространства имен (суффикс) представляет собой разделенный точками относительный путь между папкой с файлом _ViewImports.cshtml и папкой, содержащей страницу.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.

Например, класс PageModel в файле Pages/Customers/Edit.cshtml.cs задает пространство имен явно.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.

Файл Pages/_ViewImports.cshtml задает следующее пространство имен:The Pages/_ViewImports.cshtml file sets the following namespace:

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

Сформированное пространство имен для файла Pages/Customers/Edit.cshtml Razor Pages совпадает с пространством имен класса PageModel.The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace также работает со стандартными представлениями Razor.@namespace also works with conventional Razor views.

Рассмотрим файл представления Pages/Create.cshtml:Consider the Pages/Create.cshtml view file:

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

Обновленный файл представления Pages/Create.cshtml с _ViewImports.cshtml и предыдущим файлом макета:The updated Pages/Create.cshtml view file with _ViewImports.cshtml and the preceding layout file:

@page
@model CreateModel

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

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

В приведенном выше коде _ViewImports. cshtml импортировал пространство имен и вспомогательные функции тегов.In the preceding code, the _ViewImports.cshtml imported the namespace and Tag Helpers. Файл макета импортировал файлы JavaScript.The layout file imported the JavaScript files.

Начальный проект Razor Pages содержит файл Pages/_ValidationScriptsPartial.cshtml, который подключает проверку на стороне клиента.The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

Дополнительные сведения о частичных представлениях см. в Частичные представления в ASP.NET Core.For more information on partial views, see Частичные представления в ASP.NET Core.

Формирование URL-адресов для страницURL generation for Pages

На представленной выше странице Create используется RedirectToPage:The Create page, shown previously, uses 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");
    }
}

Это приложение имеет следующую структуру файлов и папок:The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • Privacy.cshtmlPrivacy.cshtml

    • /Customers/Customers

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

После успешного выполнения страницы Pages/Customers/Create.cshtml и Pages/Customers/Edit.cshtml перенаправляются на страницу Pages/Customers/Index.cshtml.The Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Customers/Index.cshtml after success. Строка ./Index — это относительное имя страницы, используемое для доступа к предыдущей странице.The string ./Index is a relative page name used to access the preceding page. Эта строка позволяет создавать URL-адреса для страницы Pages/Customers/Index.cshtml.It is used to generate URLs to the Pages/Customers/Index.cshtml page. Пример:For example:

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

С помощью абсолютного имени страницы /Index создаются URL-адреса страницы Pages/Index.cshtml.The absolute page name /Index is used to generate URLs to the Pages/Index.cshtml page. Пример:For example:

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

Имя страницы — это путь к странице из корневой папки /Pages, включая начальный символ / (например, /Index).The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). Предыдущие образцы создания URL-адреса обеспечивают расширенные параметры и функциональные возможности по сравнению с жестким заданием URL-адреса.The preceding URL generation samples offer enhanced options and functional capabilities over hard-coding a URL. Формирование URL-адресов включает маршрутизацию и позволяет генерировать и включать в код параметры в зависимости от того, как определяется маршрут в пути назначения.URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

Формирование URL-адресов для страниц поддерживает относительные имена.URL generation for pages supports relative names. В приведенной ниже таблице показано, какая страница индекса выбирается с помощью разных параметров RedirectToPage в Pages/Customers/Create.cshtml.The following table shows which Index page is selected using different RedirectToPage parameters in Pages/Customers/Create.cshtml.

RedirectToPage(x)RedirectToPage(x) СтраницаPage
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") и RedirectToPage("../Index") — это относительные имена.RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. Для получения имени целевой страницы параметр RedirectToPageкомбинируется с путем текущей страницы.The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

Привязка относительных имен полезна при создании сайтов со сложной структурой.Relative name linking is useful when building sites with a complex structure. Если относительные имена используются для связи между страницами в папке:When relative names are used to link between pages in a folder:

  • Переименование папки не нарушает относительные ссылки.Renaming a folder doesn't break the relative links.
  • Ссылки не нарушаются, так как не содержат имя папки.Links are not broken because they don't include the folder name.

Чтобы выполнить перенаправление на страницу в другой области, укажите эту область:To redirect to a page in a different Area, specify the area:

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

Дополнительные сведения см. в разделах Области в ASP.NET Core и Соглашения для маршрутов и приложений Razor Pages в ASP.NET Core.For more information, see Области в ASP.NET Core and Соглашения для маршрутов и приложений Razor Pages в ASP.NET Core.

Атрибут ViewDataViewData attribute

Данные могут передаваться на страницу с помощью атрибута ViewDataAttribute.Data can be passed to a page with ViewDataAttribute. Значения свойств с атрибутом [ViewData] хранятся в ViewDataDictionary и загружаются из него.Properties with the [ViewData] attribute have their values stored and loaded from the ViewDataDictionary.

В следующем примере AboutModel применяет атрибут [ViewData] к свойству Title:In the following example, the AboutModel applies the [ViewData] attribute to the Title property:

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

    public void OnGet()
    {
    }
}

На странице About доступ к свойству Title осуществляется как доступ к свойству модели.In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

В макете заголовок считывается из словаря 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 предоставляет TempData.ASP.NET Core exposes the TempData. Это свойство хранит данные до тех пор, пока они не будут прочитаны.This property stores data until it's read. Для проверки данных без удаления можно использовать методы Keep и Peek.The Keep and Peek methods can be used to examine the data without deletion. TempData удобно использовать для перенаправления, когда данные требуются больше, чем для одного запроса.TempData is useful for redirection, when data is needed for more than a single request.

В следующем коде значение Message задается с помощью 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");
    }
}

Представленная ниже разметка в файле Pages/Customers/Index.cshtml отображает значение Message с помощью TempData.The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

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

Модель страницы Pages/Customers/Index.cshtml.cs применяет атрибут [TempData] к свойству Message.The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

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

Дополнительные сведения см. в разделе TempData.For more information, see TempData.

Несколько обработчиков на страницуMultiple handlers per page

Следующая страница формирует разметку для двух обработчиков с помощью вспомогательной функции тегов asp-page-handler:The following page generates markup for two 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>

Форма в предыдущем примере включает две кнопки отправки, каждая из которых отправляет данные на отдельный URL-адрес с помощью FormActionTagHelper.The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. Атрибут asp-page-handler является дополнением к asp-page.The asp-page-handler attribute is a companion to asp-page. Атрибут asp-page-handler формирует URL-адреса, ,которые используются для отправки данных в каждый из методов обработчиков, определенных страницей.asp-page-handler generates URLs that submit to each of the handler methods defined by a page. asp-page не задается, так как пример сопоставлен с текущей страницей.asp-page isn't specified because the sample is linking to the current 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?.ToUpperInvariant();
            return await OnPostJoinListAsync();
        }
    }
}

В представленном выше коде используются именованные методы обработчика.The preceding code uses named handler methods. Именованные методы обработчика создаются путем размещения определенного текста в имени после On<HTTP Verb> и перед Async (если есть).Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). В приведенном выше примере использовались методы страницы OnPost JoinList Async и OnPost JoinListUC Async.In the preceding example, the page methods are OnPost JoinList Async and OnPost JoinListUC Async. Если убрать OnPost и Async, имена обработчиков будут выглядеть как JoinList и 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" />

При использовании представленного выше кода URL-путь для отправки данных в OnPostJoinListAsync будет выглядеть как https://localhost:5001/Customers/CreateFATH?handler=JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinList. URL-путь для отправки данных в OnPostJoinListUCAsync будет иметь вид https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Пользовательские маршрутыCustom routes

С помощью директивы @page можно сделать следующее.Use the @page directive to:

  • Указать пользовательский маршрут к странице.Specify a custom route to a page. Например, можно задать маршрут к странице "Сведения" /Some/Other/Path: @page "/Some/Other/Path".For example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • Добавить сегменты к маршруту страницы по умолчанию.Append segments to a page's default route. Например, к такому маршруту можно добавить сегмент item: @page "item".For example, an "item" segment can be added to a page's default route with @page "item".
  • Добавить параметры к маршруту страницы по умолчанию.Append parameters to a page's default route. Например, для страницы с @page "{id}" может потребоваться параметр идентификатора id.For example, an ID parameter, id, can be required for a page with @page "{id}".

Поддерживается путь относительно корня, заданный знаком тильды (~) в начале пути.A root-relative path designated by a tilde (~) at the beginning of the path is supported. Например, @page "~/Some/Other/Path" равносильно @page "/Some/Other/Path".For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

Если вы не хотите, чтобы в URL-адресе отображалась строка запроса ?handler=JoinList, измените маршрут так, чтобы в качестве пути в URL-адресе указывалось имя обработчика.If you don't like the query string ?handler=JoinList in the URL, change the route to put the handler name in the path portion of the URL. Для настройки маршрута добавьте после директивы @page шаблон маршрута, заключенный в двойные кавычки.The route can be customized 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>

При использовании представленного выше кода URL-путь для отправки данных в OnPostJoinListAsync будет выглядеть как https://localhost:5001/Customers/CreateFATH/JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH/JoinList. URL-путь для отправки данных в OnPostJoinListUCAsync будет иметь вид https://localhost:5001/Customers/CreateFATH/JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH/JoinListUC.

Символ ? после handler означает, что параметр маршрута является необязательным.The ? following handler means the route parameter is optional.

Расширенная конфигурация и параметрыAdvanced configuration and settings

Конфигурация и параметры в следующих разделах не требуются большинству приложений.The configuration and settings in following sections is not required by most apps.

Для настройки расширенных параметров используйте перегрузку AddRazorPages, которая настраивает RazorPagesOptions:To configure advanced options, use the AddRazorPages overload that configures RazorPagesOptions:

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

Используйте RazorPagesOptions, чтобы задавать корневой каталог для страниц и добавлять для них соглашения моделей приложений.Use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. Дополнительные сведения о соглашениях см. в разделе Соглашения об авторизации Razor Pages.For more information on conventions, see Razor Pages authorization conventions.

Сведения о предварительной компиляции представлений см. на странице Компиляция представлений Razor.To precompile views, see Razor view compilation.

Указание местонахождения Razor Pages в корне каталогаSpecify that Razor Pages are at the content root

По умолчанию Razor Pages находится в корне каталога /Pages.By default, Razor Pages are rooted in the /Pages directory. Добавьте WithRazorPagesAtContentRoot, чтобы указать, что Razor Pages находится в корневой папке содержимого (ContentRootPath) приложения:Add WithRazorPagesAtContentRoot to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

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

Указание местонахождения Razor Pages в пользовательском корневом каталогеSpecify that Razor Pages are at a custom root directory

Добавьте WithRazorPagesRoot, чтобы указать, что Razor Pages находится в пользовательском корневом каталоге в приложении (укажите относительный путь):Add WithRazorPagesRoot to specify that Razor Pages are at a custom root directory in the app (provide a relative path):

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

Дополнительные ресурсыAdditional resources

Предупреждение

Если вы используете Visual Studio 2017, см. обсуждение dotnet/sdk issue #3124 для получения сведений о версиях пакета SDK для .NET Core, которые не работают с Visual Studio.If you use Visual Studio 2017, see dotnet/sdk issue #3124 for information about .NET Core SDK versions that don't work with Visual Studio.

Создание проекта Razor PagesCreate a Razor Pages project

Подробные инструкции по созданию проекта Razor Pages см. в статье Начало работы с Razor Pages.See Get started with Razor Pages for detailed instructions on how to create a Razor Pages project.

Razor PagesRazor Pages

Razor Pages активируется в файле 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();
    }
}

Рассмотрим простую страницу: Consider a basic page:

@page

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

Приведенный выше код выглядит как файл представления Razor, используемый в приложениях ASP.NET Core с контроллерами и представлениями.The preceding code looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. и отличается от него только директивой @page.What makes it different is the @page directive. Директива @page превращает файл в действие MVC, а значит обрабатывает запросы напрямую, минуя контроллер.@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page должна быть первой директивой Razor на странице.@page must be the first Razor directive on a page. @page влияет на поведение всех остальных конструкций Razor.@page affects the behavior of other Razor constructs.

Похожая страница с использованием класса PageModel показана в следующих двух файлах.A similar page, using a PageModel class, is shown in the following two files. Файл Pages/Index2.cshtml:The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model IndexModel2

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

Модель страницы 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 }";
        }
    }
}

Как правило, файл класса PageModel называется так же, как файл Razor Pages, но с расширением CS.By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. Например, представленная выше страница Razor Pages называется Pages/Index2.cshtml.For example, the previous Razor Page is Pages/Index2.cshtml. Файл, содержащий класс PageModel, называется Pages/Index2.cshtml.cs.The file containing the PageModel class is named Pages/Index2.cshtml.cs.

Сопоставления URL-адресов со страницами определяются расположением конкретной страницы в файловой системе.The associations of URL paths to pages are determined by the page's location in the file system. В приведенной ниже таблице показаны пути Razor Pages и соответствующие URL-адреса.The following table shows a Razor Page path and the matching URL:

Имя файла и путьFile name and path Соответствующий URLmatching URL
/Pages/index.cshtml/Pages/Index.cshtml / или /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 или /Store/Index/Store or /Store/Index

Примечания.Notes:

  • Среда выполнения по умолчанию ищет файлы Razor Pages в папке Pages.The runtime looks for Razor Pages files in the Pages folder by default.
  • Если в URL-адресе не указана конкретная страница, по умолчанию открывается страница Index.Index is the default page when a URL doesn't include a page.

Создание простой формыWrite a basic form

Razor Pages предназначена для упрощения реализации типовых шаблонов, которые используются в браузерах, при создании приложения.Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. Привязки модели, вспомогательные функции тегов и вспомогательные методы HTML отлично работают со свойствами, определенными в классе Razor Pages.Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Рассмотрим страницу с простой формой связи для модели Contact.Consider a page that implements a basic "contact us" form for the Contact model:

В представленных в этой статье примерах DbContext инициализируется в файле 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();
        }
    }
}

Модель данных: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; }
    }
}

Контекст базы данных: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; }
    }
}

Файл представления 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>

Модель страницы 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");
        }
    }
}

Как правило, класс PageModel называется <PageName>Model и находится в том же пространстве имен, что и страница.By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

Класс PageModel позволяет разделять логику страницы и ее представление.The PageModel class allows separation of the logic of a page from its presentation. Он определяет обработчики страницы для запросов, отправляемых на страницу, а также данные для ее визуализации.It defines page handlers for requests sent to the page and the data used to render the page. Это разделение позволяет:This separation allows:

Страница содержит метод обработчика OnPostAsync, который выполняется по запросам POST (когда пользователь публикует форму).The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). Методы обработчика можно добавить для любой HTTP-команды.You can add handler methods for any HTTP verb. Наиболее распространенные обработчикиThe most common handlers are:

  • OnGet — инициализация необходимого для страницы состояния.OnGet to initialize state needed for the page. Пример обработчика OnGet.OnGet sample.
  • OnPost — обработка отправленных через форму данных.OnPost to handle form submissions.

Суффикс Async не является обязательным, но часто используется для асинхронных функций.The Async naming suffix is optional but is often used by convention for asynchronous functions. Этот код типичен для Razor Pages.The preceding code is typical for Razor Pages.

Если вы знакомы с приложениями ASP.NET, использующими контроллеры и представления:If you're familiar with ASP.NET apps using controllers and views:

  • Код OnPostAsync в предыдущем примере похож на стандартный код контроллера.The OnPostAsync code in the preceding example looks similar to typical controller code.
  • Большинство примитивов MVC, включая привязку модели, проверку, проверку и результаты действий, являются общими.Most of the MVC primitives like model binding, validation, Validation, and action results are shared.

Предыдущий метод OnPostAsync:The previous OnPostAsync method:

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

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

Простая схема OnPostAsync:The basic flow of OnPostAsync:

Проверка на наличие ошибок проверки.Check for validation errors.

  • Если ошибок нет, сохранение данных и перенаправление.If there are no errors, save the data and redirect.
  • Если есть ошибки, отображение страницы с сообщениями проверки.If there are errors, show the page again with validation messages. Проверка на стороне клиента выполняется точно так же, как в традиционных приложениях MVC ASP.NET Core.Client-side validation is identical to traditional ASP.NET Core MVC applications. Во многих случаях ошибки проверки выявляются на клиентском компьютере и на сервер не отправляются.In many cases, validation errors would be detected on the client, and never submitted to the server.

После успешного ввода данных метод обработчика OnPostAsync вызывает метод обработчика RedirectToPage, чтобы получить экземпляр RedirectToPageResult.When the data is entered successfully, the OnPostAsync handler method calls the RedirectToPage helper method to return an instance of RedirectToPageResult. RedirectToPage — это новый результат действия, аналогичный RedirectToAction или RedirectToRoute, но настроенный для страниц.RedirectToPage is a new action result, similar to RedirectToAction or RedirectToRoute, but customized for pages. В приведенном выше примере он выполняет перенаправление на корневую страницу индекса (/Index).In the preceding sample, it redirects to the root Index page (/Index). Более подробно RedirectToPage рассматривается в разделе Создание URL для страниц.RedirectToPage is detailed in the URL generation for Pages section.

Если в отправленной форме имеются ошибки проверки (переданные на сервер), метод обработчика OnPostAsync вызывает метод обработчика Page.When the submitted form has validation errors (that are passed to the server), theOnPostAsync handler method calls the Page helper method. Page возвращает экземпляр PageResult.Page returns an instance of PageResult. Возвращение Page аналогично тому, как действия в контроллерах возвращают View.Returning Page is similar to how actions in controllers return View. PageResult — тип возвращаемого значения по умолчанию для метода обработчика.PageResult is the default return type for a handler method. Метод обработчика, вернувший void, визуализирует страницу.A handler method that returns void renders the page.

Для указания согласия на привязку модели в свойстве Customer используется атрибут [BindProperty].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");
    }
}

По умолчанию Razor Pages привязывает свойства ко всем командам, кроме GET.Razor Pages, by default, bind properties only with non-GET verbs. Привязка к свойствам позволяет сократить объем необходимого кода.Binding to properties can reduce the amount of code you have to write. Привязка уменьшает код за счет того, что для визуализации полей формы (<input asp-for="Customer.Name">) и получения входных данных используется одно и то же свойство.Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name">) and accept the input.

Предупреждение

В целях безопасности вам следует задать привязку данных запроса GET к свойствам страничной модели.For security reasons, you must opt in to binding GET request data to page model properties. Проверьте введенные данные пользователя, прежде чем сопоставлять их со свойствами.Verify user input before mapping it to properties. Привязка GET полезна при обращении к сценариям, использующим строку запроса или значения маршрутов.Opting into GET binding is useful when addressing scenarios that rely on query string or route values.

Чтобы привязать свойство к запросам GET, задайте для свойства SupportsGet атрибута [BindProperty] значение true:To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true:

[BindProperty(SupportsGet = true)]

Дополнительные сведения см. в видео, посвященном обсуждению привязки к запросу GET, на канале YouTube.For more information, see ASP.NET Core Community Standup: Bind on GET discussion (YouTube).

Домашняя страница (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>

Связанный класс PageModel (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();
        }
    }
}

Файл Index.cshtml содержит следующую разметку для создания ссылки на правку для каждого контактного лица.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>

Вспомогательный тег привязки <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> использовал атрибут asp-route-{value} для создания ссылки на страницу редактирования.The <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. Эта ссылка содержит данные о маршруте с идентификатором контактного лица.The link contains route data with the contact ID. Например, https://localhost:5001/Edit/1.For example, https://localhost:5001/Edit/1. Вспомогательные функции тегов позволяют серверному коду участвовать в создании и отображении HTML-элементов в файлах Razor.Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files. Вспомогательные функции тегов включены с помощью @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpersTag Helpers are enabled by @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Файл 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>

Первая строка содержит директиву @page "{id:int}".The first line contains the @page "{id:int}" directive. Ограничение маршрутизации "{id:int}" указывает, что страница должна принимать обращенные к ней запросы, которые содержат данные о маршруте int.The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. Если запрос к странице не содержит данные о маршруте, которые можно конвертировать в int, среда выполнения возвращает ошибку HTTP 404 (не найдено).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. Чтобы сделать идентификатор необязательным, добавьте ? к ограничению маршрута:To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

Файл 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");
        }
    }
}

Файл Index.cshtml также содержит разметку для создания кнопки удаления у каждого контакта клиента: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>

Во время обработки кнопки удаления в HTML ее formaction включает параметры для следующего:When the delete button is rendered in HTML, its formaction includes parameters for:

  • Идентификатор контакта клиента, указанный атрибутом asp-route-id.The customer contact ID specified by the asp-route-id attribute.
  • Параметр handler, указанный атрибутом asp-page-handler.The handler specified by the asp-page-handler attribute.

Пример отображенной кнопки удаления с идентификатором контакта клиента 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>

При выборе кнопки на сервер отправляется запрос формы POST.When the button is selected, a form POST request is sent to the server. По соглашению имя метода обработчика выбирается на основе значения параметра handler в соответствии со схемой 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.

Так как handler — delete в этом примере, метод обработчика OnPostDeleteAsync используется для обработки запроса POST.Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. Если asp-page-handler имеет другое значение, например remove, выбирается метод обработчика с именем OnPostRemoveAsync.If the asp-page-handler is set to a different value, such as remove, a handler method with the name OnPostRemoveAsync is selected. В приведенном ниже коде показан обработчик OnPostDeleteAsync:The following code shows the OnPostDeleteAsync handler:

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

Метод OnPostDeleteAsync:The OnPostDeleteAsync method:

  • Принимает id из строки запроса.Accepts the id from the query string. Если директива страницы index.cshtml содержит ограничение маршрутизации "{id:int?}", то id будет получено из данных маршрута.If the Index.cshtml page directive contained routing constraint "{id:int?}", id would come from route data. Данные маршрута для id указываются в универсальном коде ресурса (URI), например https://localhost:5001/Customers/2.The route data for id is specified in the URI such as https://localhost:5001/Customers/2.
  • Отправляет в базу данных запрос контакта клиента с FindAsync.Queries the database for the customer contact with FindAsync.
  • Если контакт клиента найден, он удаляется из списка контактов.If the customer contact is found, they're removed from the list of customer contacts. База данных обновляется.The database is updated.
  • Вызывает RedirectToPage для перенаправления на корневую страницу индекса (/Index).Calls RedirectToPage to redirect to the root Index page (/Index).

Маркировка свойств страницы как обязательныхMark page properties as required

Свойства класса PageModel можно отметить атрибутом Required:Properties on a PageModel can be marked 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");
        }
    }
}

Дополнительные сведения см. в статье Проверка модели.For more information, see Model validation.

Обработка запросов HEAD с помощью вызова резервного обработчика OnGetHandle HEAD requests with an OnGet handler fallback

Запросы HEAD позволяют получать заголовки для определенного ресурса.HEAD requests allow you to retrieve the headers for a specific resource. В отличие от запросов GET запросы HEADне возвращают текст ответа.Unlike GET requests, HEAD requests don't return a response body.

Обработчик OnHead обычно создается и вызывается для выполнения запросов HEAD:Ordinarily, an OnHead handler is created and called for HEAD requests:

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

Если в ASP.NET Core 2.1 или более поздней версии обработчик OnHead не определен, Razor Pages выполнит вызов резервного обработчика OnGet.In ASP.NET Core 2.1 or later, Razor Pages falls back to calling the OnGet handler if no OnHead handler is defined. Это поведение включается путем вызова SetCompatibilityVersion в Startup.ConfigureServices:This behavior is enabled by the call to SetCompatibilityVersion in Startup.ConfigureServices:

services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Шаблоны по умолчанию создают вызов SetCompatibilityVersion в ASP.NET Core 2.1 и 2.2.The default templates generate the SetCompatibilityVersion call in ASP.NET Core 2.1 and 2.2. SetCompatibilityVersion задает для параметра AllowMappingHeadRequestsToGetHandler Razor Pages значение true.SetCompatibilityVersion effectively sets the Razor Pages option AllowMappingHeadRequestsToGetHandler to true.

Вместо включения всех возможных поведений с помощью метода SetCompatibilityVersion вы можете задать конкретное поведение.Rather than opting in to all behaviors with SetCompatibilityVersion, you can explicitly opt in to specific behaviors. Следующий код позволяет сопоставлять запросы HEAD с обработчиком OnGet:The following code opts in to allowing HEAD requests to be mapped to the OnGet handler:

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

XSRF/CSRF и Razor PagesXSRF/CSRF and Razor Pages

Вам не придется писать отдельный код для проверки подлинности запросов.You don't have to write any code for antiforgery validation. Razor Pages включает создание и проверку маркеров защиты от подделок по умолчанию.Antiforgery token generation and validation are automatically included in Razor Pages.

Использование макетов, частичных реплик, шаблонов и вспомогательных функций тегов с Razor PagesUsing Layouts, partials, templates, and Tag Helpers with Razor Pages

Pages работает со всеми функциями подсистемы просмотра Razor.Pages work with all the capabilities of the Razor view engine. Макеты, частичные реплики, шаблоны, вспомогательные функции тегов, а также файлы _ViewStart.cshtml и _ViewImports.cshtml работают точно так же, как и в стандартных представлениях Razor.Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, _ViewImports.cshtml work in the same way they do for conventional Razor views.

Давайте упростим нашу страницу с помощью некоторых из этих функций.Let's declutter this page by taking advantage of some of those capabilities.

Добавим макет страницы в файл Pages/Shared/_Layout.cshtml.Add a layout page to Pages/Shared/_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>

Этот макет:The Layout:

  • управляет макетом каждой страницы (кроме страниц с отказом от макета);Controls the layout of each page (unless the page opts out of layout).
  • импортирует HTML-структуры, такие как JavaScript и таблицы стилей.Imports HTML structures such as JavaScript and stylesheets.

Дополнительные сведения см. в статье о макете.See layout page for more information.

Свойство Layout определяется в файле Pages/_ViewStart.cshtml:The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Макет хранится в папке Pages/Shared.The layout is in the Pages/Shared folder. Pages ищет другие представления (макеты, шаблоны, частичные реплики) в иерархическом порядке, начиная с той папки, где находится текущая страница.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Макет в папке Pages/Shared можно использовать на любой странице Razor, которая находится в папке Pages.A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

Файл макета следует поместить в папку Pages/Shared.The layout file should go in the Pages/Shared folder.

Корпорация Майкрософт рекомендует не размещать файл макета в папке Views/Shared.We recommend you not put the layout file in the Views/Shared folder. Views/Shared — это шаблон представлений MVC.Views/Shared is an MVC views pattern. Razor Pages опирается на иерархию папок, а не на условные обозначения путей.Razor Pages are meant to rely on folder hierarchy, not path conventions.

Поиск представлений в Razor Pages охватывает папку Pages.View search from a Razor Page includes the Pages folder. Макеты, шаблоны и частичные реплики работают с контроллерами MVC и стандартными представлениями Razor.The layouts, templates, and partials you're using with MVC controllers and conventional Razor views just work.

Добавим файл Pages/_ViewImports.cshtml:Add a Pages/_ViewImports.cshtml file:

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

@namespace описывается далее в этом руководстве.@namespace is explained later in the tutorial. Директива @addTagHelper добавляет встроенные вспомогательные теги на все страницы в папке Pages.The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

Явное применение директивы @namespace на странице: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>

Данная директива задает пространство имен для страницы.The directive sets the namespace for the page. Включать пространство имен в директиву @model не требуется.The @model directive doesn't need to include the namespace.

Если директива @namespace содержится в файле _ViewImports.cshtml, указанное пространство имен определяет префикс для созданного в Pages пространства имен, куда импортируется директива @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. Остальная часть созданного пространства имен (суффикс) представляет собой разделенный точками относительный путь между папкой с файлом _ViewImports.cshtml и папкой, содержащей страницу.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.

Например, класс PageModel в файле Pages/Customers/Edit.cshtml.cs задает пространство имен явно.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.

Файл Pages/_ViewImports.cshtml задает следующее пространство имен:The Pages/_ViewImports.cshtml file sets the following namespace:

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

Сформированное пространство имен для файла Pages/Customers/Edit.cshtml Razor Pages совпадает с пространством имен класса PageModel.The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace также работает со стандартными представлениями Razor.@namespace also works with conventional Razor views.

Исходный файл представления Pages/Create.cshtml: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>

Обновленный файл представления Pages/Create.cshtml: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>

Начальный проект Razor Pages содержит файл Pages/_ValidationScriptsPartial.cshtml, который подключает проверку на стороне клиента.The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

Дополнительные сведения о частичных представлениях см. в Частичные представления в ASP.NET Core.For more information on partial views, see Частичные представления в ASP.NET Core.

Формирование URL-адресов для страницURL generation for Pages

На представленной выше странице Create используется 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");
}

Это приложение имеет следующую структуру файлов и папок:The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • /Customers/Customers

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

После успешного выполнения страницы Pages/Customers/Create.cshtml и Pages/Customers/Edit.cshtml перенаправляются на страницу Pages/Index.cshtml.The Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Index.cshtml after success. Строка /Index составляет часть URL-адреса для доступа к указанной выше странице.The string /Index is part of the URI to access the preceding page. Строка /Index позволяет создавать URL-адреса для страницы Pages/Index.cshtml.The string /Index can be used to generate URIs to the Pages/Index.cshtml page. Пример:For example:

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

Имя страницы — это путь к странице из корневой папки /Pages, включая начальный символ / (например, /Index).The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). Предыдущие образцы создания URL-адреса обеспечивают расширенные параметры и функциональные возможности по сравнению с жестким заданием URL-адреса.The preceding URL generation samples offer enhanced options and functional capabilities over hardcoding a URL. Формирование URL-адресов включает маршрутизацию и позволяет генерировать и включать в код параметры в зависимости от того, как определяется маршрут в пути назначения.URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

Формирование URL-адресов для страниц поддерживает относительные имена.URL generation for pages supports relative names. В приведенной ниже таблице показано, какая страница индекса выбирается с каждым параметром RedirectToPage в 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) СтраницаPage
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") и RedirectToPage("../Index") — это относительные имена.RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. Для получения имени целевой страницы параметр RedirectToPageкомбинируется с путем текущей страницы.The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

Привязка относительных имен полезна при создании сайтов со сложной структурой.Relative name linking is useful when building sites with a complex structure. Если для связи между страницами в определенной папке используются относительные имена, эту папку можно переименовать.If you use relative names to link between pages in a folder, you can rename that folder. При этом все ссылки останутся рабочими (так как не включают имя папки).All the links still work (because they didn't include the folder name).

Чтобы выполнить перенаправление на страницу в другой области, укажите эту область:To redirect to a page in a different Area, specify the area:

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

Для получения дополнительной информации см. Области в ASP.NET Core.For more information, see Области в ASP.NET Core.

Атрибут ViewDataViewData attribute

Данные могут передаваться на страницу с помощью атрибута ViewDataAttribute.Data can be passed to a page with ViewDataAttribute. Свойства в контроллерах или моделях Razor Page, отмеченные атрибутом [ViewData], обладают собственными значениями, загружаемыми из ViewDataDictionary.Properties on controllers or Razor Page models with the [ViewData] attribute have their values stored and loaded from the ViewDataDictionary.

В следующем примере класс AboutModel содержит свойство Title, отмеченное атрибутом [ViewData].In the following example, the AboutModel contains a Title property marked with [ViewData]. Свойство Title задает заголовок страницы About.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()
    {
    }
}

На странице About доступ к свойству Title осуществляется как доступ к свойству модели.In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

В макете заголовок считывается из словаря 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 позволяет использовать свойство TempData в контроллере.ASP.NET Core exposes the TempData property on a controller. Это свойство хранит данные до тех пор, пока они не будут прочитаны.This property stores data until it's read. Для проверки данных без удаления можно использовать методы Keep и Peek.The Keep and Peek methods can be used to examine the data without deletion. TempData удобно использовать для перенаправления, когда данные требуются больше чем для одного запроса.TempData is useful for redirection, when data is needed for more than a single request.

В следующем коде значение Message задается с помощью 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");
    }
}

Представленная ниже разметка в файле Pages/Customers/Index.cshtml отображает значение Message с помощью TempData.The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

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

Модель страницы Pages/Customers/Index.cshtml.cs применяет атрибут [TempData] к свойству Message.The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

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

Дополнительные сведения см. в разделе TempData.For more information, see TempData .

Несколько обработчиков на страницуMultiple handlers per page

Следующая страница формирует разметку для двух обработчиков с помощью вспомогательной функции тегов asp-page-handler:The following page generates markup for two 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>

Форма в предыдущем примере включает две кнопки отправки, каждая из которых отправляет данные на отдельный URL-адрес с помощью FormActionTagHelper.The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. Атрибут asp-page-handler является дополнением к asp-page.The asp-page-handler attribute is a companion to asp-page. Атрибут asp-page-handler формирует URL-адреса, ,которые используются для отправки данных в каждый из методов обработчиков, определенных страницей.asp-page-handler generates URLs that submit to each of the handler methods defined by a page. asp-page не задается, так как пример сопоставлен с текущей страницей.asp-page isn't specified because the sample is linking to the current 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?.ToUpperInvariant();
            return await OnPostJoinListAsync();
        }
    }
}

В представленном выше коде используются именованные методы обработчика.The preceding code uses named handler methods. Именованные методы обработчика создаются путем размещения определенного текста в имени после On<HTTP Verb> и перед Async (если есть).Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). В приведенном выше примере использовались методы страницы OnPost JoinList Async и OnPost JoinListUC Async.In the preceding example, the page methods are OnPost JoinList Async and OnPost JoinListUC Async. Если убрать OnPost и Async, имена обработчиков будут выглядеть как JoinList и 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" />

При использовании представленного выше кода URL-путь для отправки данных в OnPostJoinListAsync будет выглядеть как https://localhost:5001/Customers/CreateFATH?handler=JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinList. URL-путь для отправки данных в OnPostJoinListUCAsync будет иметь вид https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Пользовательские маршрутыCustom routes

С помощью директивы @page можно сделать следующее.Use the @page directive to:

  • Указать пользовательский маршрут к странице.Specify a custom route to a page. Например, можно задать маршрут к странице "Сведения" /Some/Other/Path: @page "/Some/Other/Path".For example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • Добавить сегменты к маршруту страницы по умолчанию.Append segments to a page's default route. Например, к такому маршруту можно добавить сегмент item: @page "item".For example, an "item" segment can be added to a page's default route with @page "item".
  • Добавить параметры к маршруту страницы по умолчанию.Append parameters to a page's default route. Например, для страницы с @page "{id}" может потребоваться параметр идентификатора id.For example, an ID parameter, id, can be required for a page with @page "{id}".

Поддерживается путь относительно корня, заданный знаком тильды (~) в начале пути.A root-relative path designated by a tilde (~) at the beginning of the path is supported. Например, @page "~/Some/Other/Path" равносильно @page "/Some/Other/Path".For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

Если вы не хотите, чтобы в URL-адресе отображалась строка запроса ?handler=JoinList, измените маршрут так, чтобы в качестве пути в URL-адресе указывалось имя обработчика.If you don't like the query string ?handler=JoinList in the URL, change the route to put the handler name in the path portion of the URL. Для настройки маршрута добавьте после директивы @page шаблон маршрута, заключенный в двойные кавычки.The route can be customized 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>

При использовании представленного выше кода URL-путь для отправки данных в OnPostJoinListAsync будет выглядеть как https://localhost:5001/Customers/CreateFATH/JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH/JoinList. URL-путь для отправки данных в OnPostJoinListUCAsync будет иметь вид https://localhost:5001/Customers/CreateFATH/JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH/JoinListUC.

Символ ? после handler означает, что параметр маршрута является необязательным.The ? following handler means the route parameter is optional.

Конфигурация и параметрыConfiguration and settings

Чтобы настроить дополнительные параметры, воспользуйтесь методом расширения AddRazorPagesOptions в построителе 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");
        });
}

В настоящее время RazorPagesOptions позволяет задавать корневую папку для страниц и добавлять для них обозначения моделей приложений.Currently you can use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. В дальнейшем мы расширим спектр его возможностей.We'll enable more extensibility this way in the future.

Сведения о предварительной компиляции представлений см. на странице Компиляция представлений Razor.To precompile views, see Razor view compilation .

Загрузить или просмотреть пример кода.Download or view sample code.

Дополнительные общие сведения см. на странице Учебник. Начало работы с Razor Pages в ASP.NET Core.See Get started with Razor Pages, which builds on this introduction.

Указание местонахождения Razor Pages в корне каталогаSpecify that Razor Pages are at the content root

По умолчанию Razor Pages находится в корне каталога /Pages.By default, Razor Pages are rooted in the /Pages directory. Добавьте WithRazorPagesAtContentRoot в AddMvc, чтобы указать, что Razor Pages находится в корневой папке содержимого (ContentRootPath) приложения:Add WithRazorPagesAtContentRoot to AddMvc to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

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

Указание местонахождения Razor Pages в пользовательском корневом каталогеSpecify that Razor Pages are at a custom root directory

Добавьте WithRazorPagesRoot в AddMvc, чтобы указать, что Razor Pages находится в пользовательской корневой папке приложения (укажите относительный путь):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");

Дополнительные ресурсыAdditional resources