Examen des méthodes et des vues d’action d’édition pour le contrôleur de film

par Rick Anderson

Notes

Une version mise à jour de ce didacticiel est disponible ici à l’aide de la dernière version de Visual Studio. Le nouveau didacticiel utilise ASP.NET Core MVC, qui fournit de nombreuses améliorations par rapport à 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 les pages qui rend la création de l’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 ;
  • Il s’agit de l’approche recommandée pour le développement de nouvelles applications.

Dans cette section, vous allez examiner les méthodes d’action générées Edit et les vues du contrôleur de film. Mais tout d’abord, nous allons prendre une courte déviation pour rendre la date de publication plus belle. Ouvrez le fichier Models\Movie.cs et ajoutez les lignes en surbrillance 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 sont pas affichées. 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é.

EditLink_sm

Le lien Modifier 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 })

Html.ActionLink

L’objet Html est une assistance qui est exposée à 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 liés aux méthodes d’action sur les contrôleurs. Le premier argument de la ActionLink méthode est le texte de 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 ). Le dernier argument est un objet anonyme qui génère les données d’itinéraire (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 par http://localhost:1234/Movies/Edit/4 une requête à la Edit méthode 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 permet d’acheminer les requêtes HTTP vers le contrôleur et la méthode d’action appropriés, et de fournir le paramètre ID facultatif. La méthode MapRoute est également utilisée par les HtmlHelpers , par exemple ActionLink pour générer des URL en fonction du contrôleur, de la méthode d’action et des données d’itinéraire.

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.

EditQueryString

Ouvrez le Movies contrôleur. Les deux Edit méthodes d’action sont présenté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 ce n’est pas nécessaire, car il s’agit de la méthode par défaut. (Nous allons faire référence aux 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 dans votre modèle. Vous devez inclure uniquement des propriétés dans l’attribut bind que vous souhaitez modifier. Vous pouvez en savoir plus sur le surpostage et l’attribut bind dans ma note de sécurité sur le 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 forme masqué qui doit correspondre à la Edit méthode du Movies contrôleur. Vous pouvez en savoir plus sur la falsification de requêtes intersites (également appelée XSRF ou CSRF) dans mon tutoriel Prévention XSRF/CSRF dans MVC.

La HttpGetEdit méthode accepte le paramètre d’ID de film, recherche le film à l’aide de la méthode Entity Framework Find et retourne le film sélectionné en mode Edition. 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 pour le modèle d’affichage 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 tous 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 form est indiqué 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 du formulaire sont publiées sur le serveur lorsque l’utilisateur clique sur le bouton Enregistrer . 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 ASP.NET MVC prend les valeurs de formulaire publiées et crée un Movie objet qui est passé en tant que movie paramètre. Vérifie ModelState.IsValid que les données envoyées 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 ( dbMovieDBContext instance). 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 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 assistances du modèle de vue Edit.cshtml s’occupent d’afficher les messages d’erreur appropriés.

abcNotValid

Toutes les HttpGet méthodes suivent un modèle similaire. Ils obtiennent un objet vidéo (ou une liste d’objets, dans le cas de Index), et passent le modèle à la vue. La Create méthode passe un objet vidéo vide à la vue Créer. 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é, comme décrit dans l’entrée de billet de blog ASP.NET conseil MVC #46 - Ne pas utiliser Supprimer les liens, car ils créent des failles de sécurité. La modification des 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 modifier 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-English, vous pouvez ignorer cette section et passer au tutoriel suivant. Vous pouvez télécharger la version Globalize de ce tutoriel ici. Pour obtenir un excellent tutoriel en deux parties sur l’internationalisation, consultez internationalisation ASP.NET MVC 5 de Nadeem.

Notes

Pour prendre en charge la validation jQuery pour les paramètres régionaux non anglais qui utilisent une virgule (« , ») pour les formats de date non US-English, vous devez inclure globalize.js et votre fichier de cultures/globalize.cultures.js spécifiques (à partir de https://github.com/jquery/globalize ) et JavaScript pour 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.)

  1. Dans le menu Outils , cliquez sur Gestionnaire de package NuGet, puis sur Gérer les packages NuGet pour la solution.

    Capture d’écran du menu Outils pour commencer la validation jQuery pour les paramètres régionaux non anglais.

  2. Dans le volet gauche, sélectionnez Parcourir*. *(Voir l’image ci-dessous.)

  3. Dans la zone d’entrée, entrez Globalize*.

    Capture d’écran de la zone d’entrée pour entrer Globalize.

    Choisissez jQuery.Validation.Globalize, choisissez MvcMovie , 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 vue Édition, vous pouvez le déplacer vers le fichier de disposition. Pour optimiser le téléchargement du script, consultez mon tutoriel Regroupement 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 parvenez pas à faire fonctionner la 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 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 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.