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


Изучение методов действия редактирования и представлений для контроллера фильма

Рик Андерсон

Примечание.

Обновленная версия этого руководства доступна здесь , используя последнюю версию Visual Studio. В новом руководстве используется ASP.NET Core MVC, что обеспечивает множество улучшений в этом руководстве.

В этом руководстве описывается модель MVC ASP.NET Core с контроллерами и представлениями. Razor Pages — это новая альтернатива в ASP.NET Core, модель программирования на основе страниц, которая упрощает создание веб-интерфейса и повышает производительность. Рекомендуем вам сначала попробовать изучить руководство по Razor Pages, прежде чем использовать версию для MVC. Руководство по Razor Pages:

  • проще для выполнения;
  • охватывает дополнительные возможности;
  • Предпочтительный подход для разработки новых приложений.

В этом разделе вы изучите созданные Edit методы действий и представления для контроллера фильма. Но сначала мы рассмотрим короткую диверсию, чтобы сделать дату выпуска лучше. Откройте файл Models\Movie.cs и добавьте выделенные строки, показанные ниже:

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }

    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
}

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

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

Пространство имен DataAnnotations будет рассмотрено в следующем учебнике. Атрибут Display определяет отображаемое имя поля (в этом случае "Release Date" вместо "ReleaseDate"). Атрибут DataType указывает тип данных, в данном случае это дата, поэтому сведения о времени, хранящиеся в поле, не отображаются. Атрибут DisplayFormat необходим для ошибки в браузере Chrome, который неправильно отображает форматы дат.

Запустите приложение и перейдите к контроллеру Movies . Удерживайте указатель мыши на ссылку "Изменить ", чтобы просмотреть URL-адрес, на который он ссылается.

EditLink_sm

Ссылка "Изменить" была создана методом Html.ActionLink в представлении Views\Movies\Index.cshtml:

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

Html.ActionLink

Объект Html является вспомогательным объектом, который предоставляется с помощью свойства в базовом классе System.Web.Mvc.WebViewPage . Метод ActionLink вспомогательного средства упрощает динамическое создание гиперссылок HTML, которые связываются с методами действий на контроллерах. Первым аргументом метода ActionLink является текст ссылки для отрисовки (например, <a>Edit Me</a>). Второй аргумент — это имя вызываемого метода действия (в данном случае Edit действие). Последним аргументом является анонимный объект , который создает данные маршрута (в данном случае — идентификатор 4).

Созданная ссылка, показанная на предыдущем изображении http://localhost:1234/Movies/Edit/4. Маршрут по умолчанию (установленный в App_Start\RouteConfig.cs) принимает шаблон {controller}/{action}/{id}URL-адреса. Таким образом, ASP.NET преобразуется http://localhost:1234/Movies/Edit/4 в запрос Edit к методу действия контроллера Movies с параметром ID , равным 4. Изучите следующий код из файла App_Start\RouteConfig.cs . Метод MapRoute используется для маршрутизации HTTP-запросов к правильному методу контроллера и действия и предоставления необязательного параметра идентификатора. Метод MapRoute также используется HtmlHelpers , например ActionLink для создания URL-адресов, заданных контроллером, методом действия и любыми данными маршрута.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", 
            id = UrlParameter.Optional }
    );
}

Можно также передать параметры метода действия с помощью строки запроса. Например, URL-адрес http://localhost:1234/Movies/Edit?ID=3 также передает параметр ID 3 методу Edit действия контроллера Movies .

EditQueryString

Movies Откройте контроллер. Ниже показаны два Edit метода действия.

// GET: /Movies/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }
    return View(movie);
}

// POST: /Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

Обратите внимание на второй метод действия Edit, которому предшествует атрибут HttpPost. Этот атрибут указывает, что перегрузка Edit метода может вызываться только для запросов POST. Атрибут можно применить HttpGet к первому методу редактирования, но это не обязательно, так как это значение по умолчанию. (Мы будем ссылаться на методы действий, неявно назначенные атрибуту HttpGet в качестве HttpGet методов.) Атрибут Bind — это еще один важный механизм безопасности, который позволяет хакерам от чрезмерной публикации данных в модель. Необходимо включить только свойства в атрибут привязки, который требуется изменить. Вы можете ознакомиться с перепоступом и атрибутом привязки в моей заметке о перепоставке безопасности. В простой модели, используемой в этом руководстве, мы привязаем все данные в модели. Атрибут ValidateAntiForgeryToken используется для предотвращения подделки запроса и связывается с @Html.AntiForgeryToken() файлом представления редактирования (Views\Movies\Edit.cshtml), часть показана ниже:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

@Html.AntiForgeryToken() создает скрытый маркер защиты от подделки формы, который должен соответствовать методу Edit контроллера Movies . Дополнительные сведения о подделке межсайтовых запросов (также известных как XSRF или CSRF) см. в руководстве по предотвращению XSRF/CSRF в MVC.

Метод HttpGet Edit принимает параметр идентификатора фильма, ищет фильм с помощью метода Entity Framework Find и возвращает выбранный фильм в представление "Изменить". Если не удается найти фильм, возвращается httpNotFound . Если в представлении редактирования создана система формирования шаблонов, она проверяет класс Movie и создает код для отображения элементов <label> и <input> для каждого свойства класса. В следующем примере показано представление редактирования, созданное системой формирования шаблонов Visual Studio:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.ReleaseDate, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ReleaseDate)
                @Html.ValidationMessageFor(model => model.ReleaseDate)
            </div>
        </div>
        @*Genre and Price removed for brevity.*@        
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Обратите внимание, что шаблон представления содержит @model MvcMovie.Models.Movie инструкцию в верхней части файла. Это указывает, что представление ожидает, что модель для шаблона представления будет типом Movie.

Шаблонный код использует несколько вспомогательных методов для упрощения разметки HTML. Вспомогателя Html.LabelFor отображается имя поля ("Title", "ReleaseDate", "Жанр" или "Цена"). Вспомогательный Html.EditorFor элемент отображает элемент HTML <input> . Вспомогательный Html.ValidationMessageFor элемент отображает все сообщения проверки, связанные с этим свойством.

Запустите приложение и перейдите по URL-адресу /Movies . Щелкните ссылку Edit (Изменить). Просмотрите исходный код страницы в окне браузера. Html-код для элемента формы показан ниже.

<form action="/movies/Edit/4" method="post">
   <input name="__RequestVerificationToken" type="hidden" value="UxY6bkQyJCXO3Kn5AXg-6TXxOj6yVBi9tghHaQ5Lq_qwKvcojNXEEfcbn-FGh_0vuw4tS_BRk7QQQHlJp8AP4_X4orVNoQnp2cd8kXhykS01" />  <fieldset class="form-horizontal">
      <legend>Movie</legend>

      <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

      <div class="control-group">
         <label class="control-label" for="Title">Title</label>
         <div class="controls">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="GhostBusters" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Title" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="ReleaseDate">Release Date</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-date="The field Release Date must be a date." data-val-required="The Release Date field is required." id="ReleaseDate" name="ReleaseDate" type="date" value="1/1/1984" />
            <span class="field-validation-valid help-inline" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Genre">Genre</label>
         <div class="controls">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Comedy" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Price">Price</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="7.99" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Price" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="form-actions no-color">
         <input type="submit" value="Save" class="btn" />
      </div>
   </fieldset>
</form>

Элементы <input> находятся в html-элементе <form> , атрибут которого action задается для записи в URL-адрес /Movies/Edit . Данные формы будут размещены на сервере при нажатии кнопки "Сохранить ". Вторая строка показывает скрытый маркер XSRF , созданный вызовом @Html.AntiForgeryToken() .

Обработка запроса POST

В следующем листинге демонстрируется версия HttpPost метода действия Edit.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

Атрибут ValidateAntiForgeryToken проверяет маркер XSRF , созданный вызовом @Html.AntiForgeryToken() в представлении.

Привязка модели MVC ASP.NET принимает опубликованные значения формы и создает Movie объект, переданный в качестве movie параметра. Проверяет ModelState.IsValid , можно ли использовать данные, отправленные в форме, для изменения (изменения или обновления) Movie объекта. Если данные допустимы, данные фильма сохраняются в Movies коллекции db(MovieDBContext экземпляра). Новые данные фильма сохраняются в базе данных путем SaveChanges вызова метода MovieDBContext. После сохранения данных код перенаправляет пользователя в метод действия Index класса MoviesController, который отображает коллекцию фильмов с учетом только что внесенных изменений.

Как только проверка на стороне клиента определяет значение поля недействителен, отображается сообщение об ошибке. Если JavaScript отключен, проверка на стороне клиента отключена. Однако сервер обнаруживает, что опубликованные значения недопустимы, а значения формы повторяются с сообщениями об ошибках.

Проверка подробно рассматривается далее в руководстве.

Вспомогательные Html.ValidationMessageFor элементы в шаблоне представления Edit.cshtml заботятся о отображении соответствующих сообщений об ошибках.

abcNotValid

HttpGet Все методы соответствуют аналогичному шаблону. Они получают объект фильма (или список объектов в случае Index) и передают модель в представление. Метод Create передает пустой объект фильма в представление Create. Все методы, которые создают, редактируют, удаляют или иным образом изменяют данные, делают это в перегрузке метода HttpPost. Изменение данных в методе HTTP GET представляет собой риск безопасности. Изменение данных в методе GET также нарушает рекомендации ПО HTTP и шаблон REST архитектуры, который указывает, что запросы GET не должны изменять состояние приложения. Другими словами, операция GET должна выполняться безопасным способом, то есть не иметь побочных эффектов и не изменять существующие данные.

Проверка jQuery для языковых стандартов, отличных от английского языка

Если вы используете компьютер на английском языке США, вы можете пропустить этот раздел и перейти к следующему руководству. Вы можете скачать версию этого учебника globalize. Отличное руководство по интернационализации см . в ASP.NET MVC 5 Internationalization.

Примечание.

для поддержки проверки jQuery для языковых стандартов, отличных от английского языка, использующих запятую (",") для десятичной запятой и форматов дат, отличных от английского языка, необходимо включить globalize.js и определенные язык и региональные параметры/globalize.cultures.js(из https://github.com/jquery/globalize ) и JavaScript для использованияGlobalize.parseFloat. Вы можете получить проверку jQuery, отличной от английского языка, из NuGet. (Не устанавливайте Globalize, если вы используете языковой стандарт английского языка.)

  1. В меню "Сервис" щелкните NuGet диспетчер пакетов и выберите пункт "Управление пакетами NuGet для решения".

    Снимок экрана: меню

  2. На левой панели нажмите кнопку "Обзор*".*(См. изображение ниже.)

  3. В поле ввода введите Globalize*.

    Снимок экрана: поле ввода для ввода Globalize.

    Выберите jQuery.Validation.Globalizeи MvcMovie нажмите кнопку "Установить". Файл Scripts\jquery.globalize\globalize.js будет добавлен в проект. Папка *Scripts\jquery.globalize\cultures* будет содержать множество файлов JavaScript языка и региональных параметров. Обратите внимание, что для установки этого пакета может потребоваться пять минут.

    В следующем коде показаны изменения файла Views\Movies\Edit.cshtml:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

<script src="~/Scripts/globalize/globalize.js"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.@(System.Threading.Thread.CurrentThread.CurrentCulture.Name).js"></script>
<script>
    $.validator.methods.number = function (value, element) {
        return this.optional(element) ||
            !isNaN(Globalize.parseFloat(value));
    }
    $(document).ready(function () {
        Globalize.culture('@(System.Threading.Thread.CurrentThread.CurrentCulture.Name)');
    });
</script>
<script>
    jQuery.extend(jQuery.validator.methods, {
        range: function (value, element, param) {
            //Use the Globalization plugin to parse the value
            var val = Globalize.parseFloat(value);
            return this.optional(element) || (
                val >= param[0] && val <= param[1]);
        }
    });
    $.validator.methods.date = function (value, element) {
        return this.optional(element) ||
            Globalize.parseDate(value) ||
            Globalize.parseDate(value, "yyyy-MM-dd");
    }
</script>
}

Чтобы избежать повторения этого кода в каждом представлении правки, его можно переместить в файл макета. Чтобы оптимизировать скачивание скрипта, ознакомьтесь с моим руководством по Bundling и Minification.

Дополнительные сведения см. в разделе ASP.NET MVC 3 Internationalization и ASP.NET MVC 3 Internationalization - Part 2 (NerdDinner).

В качестве временного исправления, если вы не можете получить проверку в языковом стандарте, вы можете принудительно использовать английский язык США или отключить JavaScript в браузере. Чтобы принудить компьютер использовать английский язык США, можно добавить элемент глобализации в корневой файл конфигурации проекта web.config . В следующем коде показан элемент глобализации с языком и региональными параметрами, заданными для США английского языка.

<system.web>
    <globalization culture ="en-US" />
    <!--elements removed for clarity-->
  </system.web>

В следующем руководстве мы реализуем функции поиска.