Поделиться через


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

Примечание.

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

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

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

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 9 этой статьи.

Автор: Рик Андерсон (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;
using Microsoft.AspNetCore.Mvc.Rendering;

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 При выполнении запроса на страницу метод возвращает список фильмов на страницу 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.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

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

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во вспомогательных методах HTML @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 с помощью шаблона ключ — значение. В приведенном выше примере в словарь ViewData добавляется свойство Title.

Свойство 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" и его значение создают ссылку на страницу Razor /Movies/Index. Атрибут 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 При выполнении запроса на страницу метод возвращает список фильмов на страницу 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.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

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

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во вспомогательных методах HTML @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 с помощью шаблона ключ — значение. В приведенном выше примере в словарь ViewData добавляется свойство Title.

Свойство 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" и его значение создают ссылку на страницу Razor /Movies/Index. Атрибут 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 При выполнении запроса на страницу метод возвращает список фильмов на страницу 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.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

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

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во вспомогательных методах HTML @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 с помощью шаблона ключ — значение. В приведенном выше примере в словарь ViewData добавляется свойство Title.

Свойство 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" и его значение создают ссылку на страницу Razor /Movies/Index. Атрибут 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.cs 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");
}

Проверьте страницу 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

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

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для Razor Page. Модель используется на странице во вспомогательных методах HTML @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 с помощью шаблона ключ — значение. В приведенном выше примере в словарь ViewData добавляется свойство Title.

Свойство 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" и его значение создают ссылку на страницу Razor /Movies/Index. Атрибут 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.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

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

директиву @model

@page
@model RazorPagesMovie.Pages.Movies.IndexModel

Директива @model определяет тип модели, передаваемой на страницу Razor. В приведенном выше примере строка @model делает класс, производный от PageModel, доступным для страницы Razor. Модель используется на странице во вспомогательных методах HTML @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 с помощью шаблона ключ — значение. В приведенном выше примере в словарь ViewData добавляется свойство Title.

Свойство 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" и его значение создают ссылку на страницу Razor /Movies/Index. Атрибут 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.

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