Часть 3. Razor Pages, созданные путем формирования шаблонов, в ASP.NET Core

Примечание.

Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.

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

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.

Автор: Рик Андерсон (Rick Anderson)

Этот учебник описывает Razor Pages, созданные путем формирования шаблонов в предыдущем учебнике.

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Страницы являются производными от PageModel. Как правило, класс, производный от PageModel, называется PageNameModel. Например, страница Index называется IndexModel.

Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с помощью Entity Framework см. в асинхронном коде.

GET При выполнении запроса на страницу метод возвращает список фильмов на страницу OnGetAsyncRazor. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращается void, или OnGetAsync возвращается Task, не используйте оператор return. Например, обратитесь к Privacy Page:

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

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }

}

Если возвращаемый тип - это IActionResult или Task<IActionResult>, необходимо указать оператор return. Например, метод Pages/Movies/Create.cshtml.cs OnPostAsync:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Проверьте страницу Pages/Movies/Index.cshtmlRazor :

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. @ Если за символом следует зарезервированное Razor ключевое слово, он переходит в Razorразметку с конкретными атрибутами. В противном случае он переходит в C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в разделе синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что нет нарушения доступа, когда model, model.Movie, или model.Movie[0] является null или пустым. При оценке лямбда-выражения, например с помощью использования @Html.DisplayFor(modelItem => item.Title), вычисляются значения свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Файл Pages/Shared/_Layout.cshtml реализует макет меню.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макетов позволяют:

  • Укажите макет контейнера HTML в одном месте.
  • Примените макет к нескольким страницам на сайте.

Найдите строку @RenderBody(). RenderBody — это заполнитель, в котором отображаются все представления, специфичные для страницы, обёрнутые в страницу макета. Например, при выборе ссылки Privacy представление Pages/Privacy.cshtml отображается внутри метода RenderBody.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый PageModelViewData класс содержит свойство словаря, которое можно использовать для передачи данных в представление. Добавьте объекты в ViewData словарь с помощью паттерна ключ-значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Movie Movie { get; set; } = default!;

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync запускается при публикации данных формы страницы:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок модели перехватываются на клиенте до отправки формы. Пример ошибки модели — публикация значения поля даты, которое невозможно преобразовать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Вид страницы Create.cshtml в Visual Studio с выделением вспомогательного объекта тегов.

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Движок формирования шаблонов создает разметку Razor для каждого поля модели, кроме идентификатора, что аналогично следующему коду:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогатель тегов ввода (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages являются производными от класса PageModel. Как правило, класс, производный от PageModel, называется PageNameModel. Например, страница Index называется IndexModel.

Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с использованием Entity Framework см. в разделе Асинхронный код.

GET При выполнении запроса на страницу метод возвращает список фильмов на страницу OnGetAsyncRazor. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращает void или OnGetAsync возвращает Task, оператор return не используется. Например, обратитесь к Privacy Page:

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

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }

}

Если возвращаемый тип — IActionResult или Task<IActionResult>, необходимо предоставить оператор return. Например, метод Pages/Movies/Create.cshtml.cs OnPostAsync:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Проверьте страницу Pages/Movies/Index.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. Если за символом @ следует зарезервированное ключевое слово Razor, он переходит на разметку Razor, а если нет, то на C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в статье Синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что в случае, если model, model.Movie или model.Movie[0] имеют значение null или пусты, права доступа не нарушаются. При вычислении лямбда-выражения, например с помощью @Html.DisplayFor(modelItem => item.Title), вычисляются значения для свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Макет меню реализуется в Pages/Shared/_Layout.cshtml файле.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макета позволяют сделать следующее для макета контейнера HTML:

  • указать его в одном расположении;
  • применить его на нескольких страницах сайта.

Найдите строку @RenderBody(). RenderBody — это заполнитель для отображения всех представлений определенных страниц, упакованных в страницу макета. Например, выберите ссылку Privacy и Pages/Privacy.cshtml представление отображается внутри RenderBody метода.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый класс PageModel содержит свойство словаря ViewData. Оно позволяет передать данные в представление. Объекты добавляются в словарь ViewData с помощью шаблона ключ — значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Movie Movie { get; set; } = default!;

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync выполняется, когда страница публикует данные формы:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок в модели может быть перехвачено на стороне клиента до публикации формы. Пример ошибки в модели — это публикация значения для поля даты, которое нельзя конвертировать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Представление VS страницы Create.cshtml

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Ядро формирования шаблонов создает разметку Razor для каждого поля в модели (кроме ID) следующего вида:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогательная функция тега Input (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages являются производными от класса PageModel. Как правило, класс, производный от PageModel, называется PageNameModel. Например, страница Index называется IndexModel.

Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с использованием Entity Framework см. в разделе Асинхронный код.

GET При выполнении запроса на страницу метод возвращает список фильмов на страницу OnGetAsyncRazor. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращает void или OnGetAsync возвращает Task, оператор return не используется. Например, обратитесь к Privacy Page:

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

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Если возвращаемый тип — IActionResult или Task<IActionResult>, необходимо предоставить оператор return. Например, метод Pages/Movies/Create.cshtml.cs OnPostAsync:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Проверьте страницу Pages/Movies/Index.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. Если за символом @ следует зарезервированное ключевое слово Razor, он переходит на разметку Razor, а если нет, то на C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в статье Синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что в случае, если model, model.Movie или model.Movie[0] имеют значение null или пусты, права доступа не нарушаются. При вычислении лямбда-выражения, например с помощью @Html.DisplayFor(modelItem => item.Title), вычисляются значения для свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Макет меню реализуется в Pages/Shared/_Layout.cshtml файле.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макета позволяют сделать следующее для макета контейнера HTML:

  • указать его в одном расположении;
  • применить его на нескольких страницах сайта.

Найдите строку @RenderBody(). RenderBody — это заполнитель для отображения всех представлений определенных страниц, упакованных в страницу макета. Например, выберите ссылку Privacy и Pages/Privacy.cshtml представление отображается внутри RenderBody метода.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый класс PageModel содержит свойство словаря ViewData. Оно позволяет передать данные в представление. Объекты добавляются в словарь ViewData с помощью шаблона ключ — значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync выполняется, когда страница публикует данные формы:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок в модели может быть перехвачено на стороне клиента до публикации формы. Пример ошибки в модели — это публикация значения для поля даты, которое нельзя конвертировать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Представление страницы Create.cshtml в VS17

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Ядро формирования шаблонов создает разметку Razor для каждого поля в модели (кроме ID) следующего вида:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогательная функция тега Input (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies;

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    public IList<Movie> Movie { get;set; }  = default!;

    public async Task OnGetAsync()
    {
        if (_context.Movie != null)
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages являются производными от класса PageModel. Как правило, класс, производный от PageModel, называется PageNameModel. Например, страница Index называется IndexModel.

Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с использованием Entity Framework см. в разделе Асинхронный код.

GET При выполнении запроса на страницу метод возвращает список фильмов на страницу OnGetAsyncRazor. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращает void или OnGetAsync возвращает Task, оператор return не используется. Например, обратитесь к Privacy Page:

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

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Если возвращаемый тип — IActionResult или Task<IActionResult>, необходимо предоставить оператор return. Например, метод Pages/Movies/Create.cshtml.cs OnPostAsync:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Проверьте страницу Pages/Movies/Index.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. Если за символом @ следует зарезервированное ключевое слово Razor, он переходит на разметку Razor, а если нет, то на C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в статье Синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что в случае, если model, model.Movie или model.Movie[0] имеют значение null или пусты, права доступа не нарушаются. При вычислении лямбда-выражения, например с помощью @Html.DisplayFor(modelItem => item.Title), вычисляются значения для свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Макет меню реализуется в Pages/Shared/_Layout.cshtml файле.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макета позволяют сделать следующее для макета контейнера HTML:

  • указать его в одном расположении;
  • применить его на нескольких страницах сайта.

Найдите строку @RenderBody(). RenderBody — это заполнитель для отображения всех представлений определенных страниц, упакованных в страницу макета. Например, выберите ссылку Privacy и Pages/Privacy.cshtml представление отображается внутри RenderBody метода.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый класс PageModel содержит свойство словаря ViewData. Оно позволяет передать данные в представление. Объекты добавляются в словарь ViewData с помощью шаблона ключ — значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/RazorPagesMovie.styles.css" asp-append-version="true" />

Строка @*Markup removed for brevity.*@ представляет собой комментарий Razor. В отличие от комментариев HTML <!-- --> комментарии Razor не отправляются клиенту. Дополнительные сведения см . в веб-документах MDN: начало работы с HTML .

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync выполняется, когда страница публикует данные формы:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок в модели может быть перехвачено на стороне клиента до публикации формы. Пример ошибки в модели — это публикация значения для поля даты, которое нельзя конвертировать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Представление страницы Create.cshtml в VS17

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Ядро формирования шаблонов создает разметку Razor для каждого поля в модели (кроме ID) следующего вида:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогательная функция тега Input (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

        public IList<Movie> Movie { get;set; } = default!;

        public async Task OnGetAsync()
        {
            if (_context.Movie != null)
            {
                Movie = await _context.Movie.ToListAsync();
            }
        }
    }
}

Razor Pages являются производными от класса PageModel. Как правило, класс, производный от PageModel, называется PageNameModel. Например, страница Index называется IndexModel.

Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с использованием Entity Framework см. в разделе Асинхронный код.

Когда к странице направляется запрос, метод OnGetAsync возвращает на страницу Razor список фильмов. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращает void или OnGetAsync возвращает Task, оператор return не используется. Например, обратитесь к Privacy Page:

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

namespace RazorPagesMovie.Pages
{
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

Если возвращаемый тип — IActionResult или Task<IActionResult>, необходимо предоставить оператор return. Например, Pages/Movies/Create.cshtml.csOnPostAsync метод:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Проверьте страницу Pages/Movies/Index.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. Если за символом @ следует зарезервированное ключевое слово Razor, он переходит на разметку Razor, а если нет, то на C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в статье Синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что в случае, если model, model.Movie или model.Movie[0] имеют значение null или пусты, права доступа не нарушаются. При вычислении лямбда-выражения, например с помощью @Html.DisplayFor(modelItem => item.Title), вычисляются значения для свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Макет меню реализуется в Pages/Shared/_Layout.cshtml файле.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макета позволяют сделать следующее для макета контейнера HTML:

  • указать его в одном расположении;
  • применить его на нескольких страницах сайта.

Найдите строку @RenderBody(). RenderBody — это заполнитель для отображения всех представлений определенных страниц, упакованных в страницу макета. Например, выберите ссылку Privacy и Pages/Privacy.cshtml представление отображается внутри RenderBody метода.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый класс PageModel содержит свойство словаря ViewData. Оно позволяет передать данные в представление. Объекты добавляются в словарь ViewData с помощью шаблона ключ — значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>

     @*Markup removed for brevity.*@
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />

Строка @*Markup removed for brevity.*@ представляет собой комментарий Razor. В отличие от комментариев HTML <!-- --> комментарии Razor не отправляются клиенту. Дополнительные сведения см . в веб-документах MDN: начало работы с HTML .

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

        [BindProperty]
        public Movie Movie { get; set; } = default!;
        

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Movie == null || Movie == null)
            {
                return Page();
            }

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync выполняется, когда страница публикует данные формы:

public async Task<IActionResult> OnPostAsync()
{
  if (!ModelState.IsValid || _context.Movie == null || Movie == null)
    {
        return Page();
    }

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок в модели может быть перехвачено на стороне клиента до публикации формы. Пример ошибки в модели — это публикация значения для поля даты, которое нельзя конвертировать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Представление страницы Create.cshtml в VS17

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Ядро формирования шаблонов создает разметку Razor для каждого поля в модели (кроме ID) следующего вида:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогательная функция тега Input (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги

Страницы Create, Delete, Details и Edit

Проверьте модель страницы Pages/Movies/Index.cshtml.cs:

// Unused usings removed.
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class IndexModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }
        public IList<Movie> Movie { get;set; }

        public async Task OnGetAsync()
        {
            Movie = await _context.Movie.ToListAsync();
        }
    }
}

Razor Pages являются производными от класса PageModel. Как правило, класс, производный от PageModel, называется <PageName>Model. Используя внедрение зависимостей, конструктор добавляет на страницу RazorPagesMovieContext:

public class IndexModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public IndexModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

Дополнительные сведения об асинхронном программировании с использованием Entity Framework см. в разделе Асинхронный код.

Когда к странице направляется запрос, метод OnGetAsync возвращает на страницу Razor список фильмов. В Razor Page для инициализации состояния страницы вызывается OnGetAsync или OnGet. В этом случае OnGetAsync возвращает список фильмов для отображения.

Когда OnGet возвращает void или OnGetAsync возвращает Task, оператор return не используется. Например, страница Privacy:

public class PrivacyModel : PageModel
{
    private readonly ILogger<PrivacyModel> _logger;

    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
    }
}

Если возвращаемый тип — IActionResult или Task<IActionResult>, необходимо предоставить оператор return. Например, Pages/Movies/Create.cshtml.csOnPostAsync метод:

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

        _context.Movie.Add(Movie);
        await _context.SaveChangesAsync();

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

Проверьте страницу Pages/Movies/Index.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Movie[0].Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Razor может выполнять переход с HTML на C# или на разметку Razor. Если за символом @ следует зарезервированное ключевое слово Razor, он переходит на разметку Razor, а если нет, то на C#.

директиву @page

Директива @pageRazor преобразует файл в действие MVC, а значит, он может обрабатывать запросы. @page должна быть первой директивой Razor на странице. @page и @model являются примерами перехода на разметку, относящуюся к Razor. Дополнительные сведения см. в статье Синтаксис Razor.

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для страницы Razor. Модель используется на странице во @Html.DisplayNameFor@Html.DisplayFor и .

Проверьте лямбда-выражение, которое используется в следующем вспомогательном методе HTML:

@Html.DisplayNameFor(model => model.Movie[0].Title)

Вспомогательный метод HTML DisplayNameFor проверяет свойство Title, указанное в лямбда-выражении, и определяет отображаемое имя. Лямбда-выражение проверяется, а не вычисляется. Это означает, что в случае, если model, model.Movie или model.Movie[0] имеют значение null или пусты, права доступа не нарушаются. При вычислении лямбда-выражения, например с помощью @Html.DisplayFor(modelItem => item.Title), вычисляются значения для свойств модели.

Страница макета

Выберите ссылки на меню RazorPagesMovie, Homeи Privacy. Меню на каждой странице имеют одинаковый макет. Макет меню реализуется в Pages/Shared/_Layout.cshtml файле.

Откройте файл Pages/Shared/_Layout.cshtml и проверьте его.

Шаблоны макета позволяют сделать следующее для макета контейнера HTML:

  • указать его в одном расположении;
  • применить его на нескольких страницах сайта.

Найдите строку @RenderBody(). RenderBody — это заполнитель для отображения всех представлений определенных страниц, упакованных в страницу макета. Например, выберите ссылку Privacy и Pages/Privacy.cshtml представление отображается внутри RenderBody метода.

ViewData и макет

Рассмотрим следующую разметку Pages/Movies/Index.cshtml из файла:

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

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

Выделенная выше разметка представляет собой пример перехода Razor на C#. Символы { и } ограничивают блок кода C#.

Базовый класс PageModel содержит свойство словаря ViewData. Оно позволяет передать данные в представление. Объекты добавляются в словарь ViewData с помощью шаблона ключ — значение. В приведенном выше примере в словарь Title добавляется свойство ViewData.

Свойство Title используется в файле Pages/Shared/_Layout.cshtml. В следующей _Layout.cshtml разметке показаны первые несколько строк файла.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesMovie</title>

    @*Markup removed for brevity.*@

Строка @*Markup removed for brevity.*@ представляет собой комментарий Razor. В отличие от комментариев HTML <!-- --> комментарии Razor не отправляются клиенту. Дополнительные сведения см . в веб-документах MDN: начало работы с HTML .

Обновление макета

  1. Измените <title> элемент в Pages/Shared/_Layout.cshtml файле, чтобы отобразить Movie , а не RazorPagesMovie.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Movie</title>
    
  2. Найдите следующий элемент привязки Pages/Shared/_Layout.cshtml в файле.

    <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesMovie</a>
    
  3. Измените указанный выше элемент на следующую разметку.

    <a class="navbar-brand" asp-page="/Movies/Index">RpMovie</a>
    

    Указанный выше элемент привязки является вспомогательной функцией тега. В данном случае он является вспомогательной функцией тега привязки. Атрибут вспомогательной функции тега asp-page="/Movies/Index" и его значение создают ссылку на страницу /Movies/IndexRazor. Атрибут asp-area имеет пустое значение, поэтому эта область не используется в ссылке. Дополнительные сведения см. в статье Области.

  4. Сохраните изменения и протестируйте приложение, выбрав ссылку RpMovie. Если у вас возникли проблемы, ознакомьтесь с файлом _Layout.cshtml в GitHub.

  5. Проверьте ссылки Home, RpMovie, Create, Edit и Delete. Каждая страница задает заголовок, который можно увидеть на вкладке браузера. При закладке страницы заголовок используется для закладки.

Примечание.

В поле Price нельзя вводить десятичные запятые. Чтобы обеспечить поддержку проверки jQuery для других языков, кроме английского, используйте вместо десятичной точки запятую (","), а для отображения данных в форматах для других языков, кроме английского, выполните действия, необходимые для глобализации приложения. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.

Свойство Layout задается в файле Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

Представленный выше код задает файл разметки Pages/Shared/_Layout.cshtml для всех файлов Razor в папке Pages. Дополнительные сведения см. в статье о макете.

Страничная модель Create

Изучите Pages/Movies/Create.cshtml.cs модель страницы:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesMovie.Models;
using System;
using System.Threading.Tasks;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

        public CreateModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
        {
            _context = context;
        }

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

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

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

            _context.Movie.Add(Movie);
            await _context.SaveChangesAsync();

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

Метод OnGet инициализирует все состояния, необходимые для страницы. Страница Create не содержит никаких состояний для инициализации, поэтому возвращается Page. Далее в этом руководстве показан пример инициализации состояния OnGet. Метод Page создает PageResult объект, который отрисовывает страницу Create.cshtml .

Для указания согласия на Movie в свойстве используется атрибут [BindProperty]. Когда форма Create публикует свои значения, среда выполнения ASP.NET Core связывает переданные значения с моделью Movie.

Метод OnPostAsync выполняется, когда страница публикует данные формы:

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

    _context.Movie.Add(Movie);
    await _context.SaveChangesAsync();

    return RedirectToPage("./Index");
}

Если в модели есть ошибки, форма отображается снова вместе со всеми опубликованными данными этой формы. Большинство ошибок в модели может быть перехвачено на стороне клиента до публикации формы. Пример ошибки в модели — это публикация значения для поля даты, которое нельзя конвертировать в дату. Проверка на стороне клиента и проверка модели обсуждаются подробнее далее в этом учебнике.

Если нет ошибок модели:

  • Данные сохранены.
  • Браузер перенаправляется на страницу Index.

Страница Razor создания

Проверьте файл страницы Pages/Movies/Create.cshtmlRazor:

@page
@model RazorPagesMovie.Pages.Movies.CreateModel

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

<h1>Create</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Movie.Title" class="control-label"></label>
                <input asp-for="Movie.Title" class="form-control" />
                <span asp-validation-for="Movie.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.ReleaseDate" class="control-label"></label>
                <input asp-for="Movie.ReleaseDate" class="form-control" />
                <span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Genre" class="control-label"></label>
                <input asp-for="Movie.Genre" class="form-control" />
                <span asp-validation-for="Movie.Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Movie.Price" class="control-label"></label>
                <input asp-for="Movie.Price" class="form-control" />
                <span asp-validation-for="Movie.Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

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

Visual Studio выделяет следующие теги полужирным шрифтом, который используется для вспомогательных функций тегов.

  • <form method="post">
  • <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  • <label asp-for="Movie.Title" class="control-label"></label>
  • <input asp-for="Movie.Title" class="form-control" />
  • <span asp-validation-for="Movie.Title" class="text-danger"></span>

Представление страницы Create.cshtml в VS17

Элемент <form method="post"> представляет собой вспомогательную функцию тега Form. Вспомогательная функция тега Form автоматически включает маркер защиты от подделки.

Ядро формирования шаблонов создает разметку Razor для каждого поля в модели (кроме ID) следующего вида:

<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
    <label asp-for="Movie.Title" class="control-label"></label>
    <input asp-for="Movie.Title" class="form-control" />
    <span asp-validation-for="Movie.Title" class="text-danger"></span>
</div>

Вспомогательные функции тегов Validation (<div asp-validation-summary и <span asp-validation-for) отображают ошибки проверки. Более подробно проверка рассматривается далее в этой серии статей.

Вспомогательная функция тега Label (<label asp-for="Movie.Title" class="control-label"></label>) создает подпись к метке и атрибут [for] для свойства Title.

Вспомогательная функция тега Input (<input asp-for="Movie.Title" class="form-control">) использует атрибуты DataAnnotations и создает HTML-атрибуты, необходимые для проверки jQuery на стороне клиента.

Дополнительные сведения о вспомогательных функциях тегов, таких как <form method="post">, см. в статье Вспомогательные функции тегов в ASP.NET Core.

Следующие шаги