Examen des méthodes et vues d’action d’édition pour le contrôleur de film
par Rick Anderson
Remarque
Une version mise à jour de ce didacticiel est disponible ici à l’aide de la dernière version de Visual Studio. Le nouveau tutoriel utilise ASP.NET Core MVC, qui fournit de nombreuses améliorations sur ce didacticiel.
Ce didacticiel décrit ASP.NET Core MVC avec des contrôleurs et des vues. Razor Pages est une nouvelle alternative dans ASP.NET Core, un modèle de programmation basé sur des pages qui facilite la création d’une interface utilisateur web plus facile et plus productive. Nous vous recommandons de suivre le didacticiel sur les pages Razor avant la version MVC. Le didacticiel sur les pages Razor :
- est plus facile à suivre ;
- couvre davantage de fonctionnalités ;
- Est l’approche recommandée pour le développement d’applications.
Dans cette section, vous allez examiner les méthodes et vues d’action générées Edit
pour le contrôleur de film. Mais tout d’abord, nous allons prendre un court détour pour améliorer la date de publication. Ouvrez le fichier Models\Movie.cs et ajoutez les lignes en surbrillance indiquées ci-dessous :
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; }
}
}
Vous pouvez également rendre la culture de date spécifique comme suit :
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
Nous aborderons DataAnnotations dans le prochain didacticiel. L’attribut Display spécifie les éléments à afficher pour le nom d’un champ (dans le cas présent, « Release Date » au lieu de « ReleaseDate »). L’attribut DataType spécifie le type des données, dans ce cas il s’agit d’une date, de sorte que les informations d’heure stockées dans le champ ne s’affichent pas. L’attribut DisplayFormat est nécessaire pour un bogue dans le navigateur Chrome qui restitue les formats de date de manière incorrecte.
Exécutez l’application et accédez au Movies
contrôleur. Maintenez le pointeur de la souris sur un lien Modifier pour afficher l’URL vers laquelle il est lié.
Le lien Edit a été généré par la Html.ActionLink
méthode dans la vue Views\Movies\Index.cshtml :
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
L’objet Html
est un assistance exposé à l’aide d’une propriété sur la classe de base System.Web.Mvc.WebViewPage . La ActionLink
méthode de l’assistance facilite la génération dynamique de liens hypertexte HTML qui relient les méthodes d’action sur les contrôleurs. Le premier argument de la ActionLink
méthode est le texte du lien à afficher (par exemple). <a>Edit Me</a>
Le deuxième argument est le nom de la méthode d’action à appeler (dans ce cas, l’action Edit
). L’argument final est un objet anonyme qui génère les données de routage (dans ce cas, l’ID de 4).
Le lien généré affiché dans l’image précédente est http://localhost:1234/Movies/Edit/4
. L’itinéraire par défaut (établi dans App_Start\RouteConfig.cs) prend le modèle {controller}/{action}/{id}
d’URL. Par conséquent, ASP.NET se traduit http://localhost:1234/Movies/Edit/4
en une requête en méthode Edit
d’action du Movies
contrôleur avec le paramètre ID
égal à 4. Examinez le code suivant à partir du fichier App_Start\RouteConfig.cs . La méthode MapRoute est utilisée pour router les requêtes HTTP vers le contrôleur et la méthode d’action correctes et fournir le paramètre d’ID facultatif. La méthode MapRoute est également utilisée par htmlHelpers, comme ActionLink
pour générer des URL en fonction du contrôleur, de la méthode d’action et de toutes les données de routage.
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 }
);
}
Vous pouvez également passer des paramètres de méthode d’action à l’aide d’une chaîne de requête. Par exemple, l’URL http://localhost:1234/Movies/Edit?ID=3
transmet également le paramètre ID
3 à la Edit
méthode d’action du Movies
contrôleur.
Ouvrez le Movies
contrôleur. Les deux Edit
méthodes d’action sont indiquées ci-dessous.
// 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);
}
Notez que la deuxième méthode d’action Edit
est précédée de l’attribut HttpPost
. Cet attribut spécifie que la surcharge de la Edit
méthode peut être appelée uniquement pour les requêtes POST. Vous pouvez appliquer l’attribut HttpGet
à la première méthode d’édition, mais cela n’est pas nécessaire, car il s’agit de la valeur par défaut. (Nous allons faire référence à des méthodes d’action qui sont implicitement affectées à l’attribut HttpGet
en tant que HttpGet
méthodes.) L’attribut Bind est un autre mécanisme de sécurité important qui empêche les pirates de sur-publier des données sur votre modèle. Vous devez inclure uniquement des propriétés dans l’attribut de liaison que vous souhaitez modifier. Vous pouvez en savoir plus sur le surpostage et l’attribut de liaison dans ma note de sécurité de surpostage. Dans le modèle simple utilisé dans ce tutoriel, nous allons lier toutes les données du modèle. L’attribut ValidateAntiForgeryToken est utilisé pour empêcher la falsification d’une requête et est associé @Html.AntiForgeryToken()
dans le fichier d’affichage d’édition (Views\Movies\Edit.cshtml), une partie est illustrée ci-dessous :
@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()
génère un jeton anti-falsification de formulaire masqué qui doit correspondre à la Edit
méthode du Movies
contrôleur. Vous pouvez en savoir plus sur la falsification de demandes intersite (également appelée XSRF ou CSRF) dans mon tutoriel XSRF/CSRF Prevention dans MVC.
La HttpGet
Edit
méthode prend le paramètre ID de film, recherche le film à l’aide de la méthode Entity Framework Find
et retourne le film sélectionné en mode Édition. Si un film est introuvable, HttpNotFound est retourné. Quand le système de génération de modèles automatique a créé la vue Edit, il a examiné la classe Movie
et a créé le code pour restituer les éléments <label>
et <input>
de chaque propriété de la classe. L’exemple suivant montre la vue Edit qui a été générée par le système de génération de modèles automatique de 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")
}
Notez que le modèle d’affichage a une @model MvcMovie.Models.Movie
instruction en haut du fichier . Cela spécifie que la vue s’attend à ce que le modèle de vue soit de type Movie
.
Le code généré automatiquement utilise plusieurs méthodes d’assistance pour simplifier le balisage HTML. L’assistance Html.LabelFor
affiche le nom du champ (« Title », « ReleaseDate », « Genre » ou « Price »). L’assistance Html.EditorFor
restitue un élément HTML <input>
. L’assistance Html.ValidationMessageFor
affiche les messages de validation associés à cette propriété.
Exécutez l’application et accédez à l’URL /Movies . Cliquez sur un lien Edit. Dans le navigateur, affichez la source de la page. Le code HTML de l’élément de formulaire est illustré ci-dessous.
<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>
Les <input>
éléments se trouvent dans un élément HTML <form>
dont action
l’attribut est défini pour publier sur l’URL /Movies/Edit . Les données de formulaire sont publiées sur le serveur lorsque le bouton Enregistrer est cliqué. La deuxième ligne affiche le jeton XSRF masqué généré par l’appel@Html.AntiForgeryToken()
.
Traitement de la requête POST
Le code suivant montre la version HttpPost
de la méthode d’action 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);
}
L’attribut ValidateAntiForgeryToken valide le jeton XSRF généré par l’appel @Html.AntiForgeryToken()
dans la vue.
Le classeur de modèles MVC ASP.NET prend les valeurs de formulaire publiées et crée un Movie
objet passé en tant que movie
paramètre. Vérifie ModelState.IsValid
que les données soumises dans le formulaire peuvent être utilisées pour modifier (modifier ou mettre à jour) un Movie
objet. Si les données sont valides, les données de film sont enregistrées dans la Movies
collection de l’instancedb
.MovieDBContext
Les nouvelles données de film sont enregistrées dans la base de données en appelant la SaveChanges
méthode de MovieDBContext
. Après avoir enregistré les données, le code redirige l’utilisateur vers la méthode d’action Index
de la classe MoviesController
, qui affiche la collection de films, avec notamment les modifications qui viennent d’être apportées.
Dès que la validation côté client détermine que la valeur d’un champ n’est pas valide, un message d’erreur s’affiche. Si JavaScript est désactivé, la validation côté client est désactivée. Toutefois, le serveur détecte que les valeurs publiées ne sont pas valides et que les valeurs de formulaire sont réaffichées avec des messages d’erreur.
La validation est examinée plus en détail plus loin dans le tutoriel.
Les Html.ValidationMessageFor
helpers du modèle d’affichage Edit.cshtml s’occupent de l’affichage des messages d’erreur appropriés.
Toutes les HttpGet
méthodes suivent un modèle similaire. Ils obtiennent un objet vidéo (ou liste d’objets, dans le cas de Index
), et passent le modèle à la vue. La Create
méthode transmet un objet vidéo vide à l’affichage Create. Toutes les méthodes qui créent, modifient, suppriment ou changent d’une quelconque manière des données le font dans la surcharge HttpPost
de la méthode. La modification de données dans une méthode HTTP GET est un risque de sécurité. La modification de données dans une méthode GET enfreint également les meilleures pratiques HTTP et le modèle REST architectural, qui spécifie que les requêtes GET ne doivent pas changer l’état de votre application. En d’autres termes, une opération GET doit être sûre, ne doit avoir aucun effet secondaire et ne doit pas modifier vos données persistantes.
validation jQuery pour les paramètres régionaux non anglais
Si vous utilisez un ordinateur us-anglais, vous pouvez ignorer cette section et accéder au tutoriel suivant. Vous pouvez télécharger la version Globalize de ce didacticiel ici. Pour obtenir un excellent didacticiel en deux parties sur l’internationalisation, consultez la ASP.NET MVC 5 Internationalization de Nadeem.
Remarque
pour prendre en charge la validation jQuery pour les paramètres régionaux non anglais qui utilisent une virgule (« , ») pour un format de date décimal et non anglais aux États-Unis, vous devez inclure globalize.js et vos cultures/fichiers globalize.cultures.js spécifiques (à partir de https://github.com/jquery/globalize ) et JavaScript à utiliser Globalize.parseFloat
. Vous pouvez obtenir la validation jQuery non anglaise à partir de NuGet. (N’installez pas Globalize si vous utilisez des paramètres régionaux anglais.)
Dans le menu Outils, cliquez sur NuGet Gestionnaire de package, puis sur Gérer les packages NuGet pour la solution.
Dans le volet gauche, sélectionnez Parcourir*.*(Voir l’image ci-dessous.)
Dans la zone d’entrée, entrez Globalize*.
Choisissez
jQuery.Validation.Globalize
, choisissezMvcMovie
, puis cliquez sur Installer. Le fichier Scripts\jquery.globalize\globalize.js sera ajouté à votre projet. Le dossier *Scripts\jquery.globalize\cultures* contient de nombreux fichiers JavaScript de culture. Notez que l’installation de ce package peut prendre cinq minutes.Le code suivant montre les modifications apportées au fichier 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>
}
Pour éviter de répéter ce code dans chaque mode Édition, vous pouvez le déplacer vers le fichier de disposition. Pour optimiser le téléchargement du script, consultez mon didacticiel Groupdling et Minification.
Pour plus d’informations, consultez ASP.NET internationalisation MVC 3 et ASP.NET internationalisation MVC 3 - Partie 2 (NerdDinner).
En guise de correctif temporaire, si vous ne pouvez pas obtenir de validation dans vos paramètres régionaux, vous pouvez forcer votre ordinateur à utiliser l’anglais américain ou vous pouvez désactiver JavaScript dans votre navigateur. Pour forcer votre ordinateur à utiliser l’anglais américain, vous pouvez ajouter l’élément de globalisation au fichier web.config racine des projets. Le code suivant montre l’élément de globalisation avec la culture définie sur États-Unis l’anglais.
<system.web>
<globalization culture ="en-US" />
<!--elements removed for clarity-->
</system.web>
Dans le tutoriel suivant, nous allons implémenter la fonctionnalité de recherche.