Nicolas Moyère
Gérer simplement les boutons avec ASP.NET MVC
La gestion de formulaires avec plusieurs boutons n’est pas naturelle dans ASP.NET MVC. Cette astuce montre comment faciliter cette implémentation.
Par Nicolas Moyère publié le 19/01/2009 à 22:49, lu 7016 fois,
J’imagine déjà les irréductibles des Web Forms se dire « Comment ça ? On ne peut même pas avoir plusieurs boutons dans une page MVC ? ».
Dans du HTML classique, nous avons un tag form dont l’attribut action contient l’URL invoquée lors de la soumission du formulaire. Quel que soit le bouton activé dans la page, c’est toujours la même URL qui est appelée.
Par défaut, ASP.NET MVC associe une URL à une méthode d’un contrôleur. Dans notre cas, tous les boutons sont redirigés vers une seule méthode.
Par exemple, utilisons le formulaire suivant :

< form action="<%= Url.Action("Update", new {id=ViewData["id"]}) %>" method="post">

< input type="submit" name="Apply" value="Appliquer" />

< input type="submit" name="FinishLater" value="Finir plus tard" />

</ form >

C’est un exemple classique d’utilisation d’un workflow où plusieurs décisions peuvent être appliquées sur les mêmes données.
Coté contrôleur, les attributs fournis avec ASP.NET MVC nous permettent de séparer l’affichage initial du formulaire et sa validation. Néanmoins, le traitement des deux boutons doit rester dans la même méthode.

[ActionName("Update"), AcceptVerbs(HttpVerbs.Get)]

public ActionResult Edit(string id)

{

    // Affiche le formulaire d'édition vide

}

 

[ActionName("Update"), AcceptVerbs(HttpVerbs.Post)]

public ActionResult Update(string id)

{

    if (ControllerContext.HttpContext.Request.Form["Apply"] != null)

    {

        // Code pour le premier bouton

        return UpdateNow(id);

    }

    else if (ControllerContext.HttpContext.Request.Form["FinishLater"] != null)

    {

        // Code pour l'autre bouton

        return UpdateLater(id);

    }

}

Quand un formulaire est validé, le navigateur ajoute le nom du bouton activé en paramètre de la requête. Dans la méthode du contrôleur, cela nous permet d’effectuer un traitement spécifique à chaque bouton.
Il y a plusieurs inconvénients. D’une part, nous devons coder à la main un aiguillage pas très élégant. D’autre part, le contrôleur utilise explicitement l’objet Request et cela rend plus difficile l’écriture de tests unitaires.
ASP.NET MVC traite les requêtes HTTP selon le cycle suivant :
  • L’URL est prise en charge par le module d’ASP.NET Routing. Il déduit les noms du contrôleur et de l’action.
  • Le handler d’ASP.NET MVC instancie le contrôleur demandé.
  • Le handler teste ensuite toutes les méthodes publiques du contrôleur pour retenir celles correspondantes à l’action voulue. Ce filtrage tient compte du nom de la méthode et de l’attribut ActionName qui permet d’avoir plusieurs méthodes pour la même action.
  • Si plusieurs méthodes sont retenues, le handler effectue un second filtrage en utilisant les attributs de type ActionMethodSelector.
  • La méthode restante est invoquée.
L’implémentation la plus connue d’ActionMethodSelector est AcceptVerbs qui permet comme dans l’exemple d’avoir une méthode pour les appels en GET et une autre pour les appels en POST.
L’astuce consiste à factoriser le code d’aiguillage que nous avons écrit dans une nouvelle implémentation d’ActionMethodSelector que nous appelons AcceptButton.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

public sealed class AcceptButtonAttribute : ActionMethodSelectorAttribute

{

    private string name;

 

    public AcceptButtonAttribute(string name)

        : base()

    {

        this.name = name;

    }

 

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)

    {

        return controllerContext.HttpContext.Request.Form[name] != null;

    }

}

Notre pouvons réécrire l’exemple de la façon suivante :

[ActionName("Update"), AcceptVerbs(HttpVerbs.Get)]

public ActionResult Edit(string id)

{

    // Affiche le formulaire d'édition vide

}

 

[ActionName("Update"), AcceptVerbs(HttpVerbs.Post), AcceptButton("Apply")]

public ActionResult UpdateNow(string id)

{

    // Code pour le premier bouton

}

 

[ActionName("Update"), AcceptVerbs(HttpVerbs.Post), AcceptButton("FinishLater")]

public ActionResult UpdateLater(string id)

{

    // Code pour l'autre bouton

}

En utilisant les attributs, nous pouvons séparer chaque action dans une méthode dédiée.
Comme vous le voyez, le code est beaucoup plus clair et maintenable. Et comme nous n’avons plus de dépendance sur l’objet Request, le contrôleur devient plus simple à tester.
 
» Démarrer une discussion
 
 
Discussion démarée par mprosper le 17/05/2009 à 18:41, 2 commentaire(s).
Discussion démarée par Vincent Bourdon le 20/01/2009 à 10:41, 1 commentaire(s).
Discussion démarée par nadal rafa le 24/04/2009 à 20:00, 1 commentaire(s).