75 votes

Comment puis-je maintenir le ModelState avec RedirectToAction ?

Comment puis-je renvoyer le résultat d'une autre action ou faire passer l'utilisateur à une autre action s'il y a une erreur dans mon ModelState sans perdre mon ModelState informations ?

Le scénario est le suivant ; Delete accepte un POST provenant d'un formulaire DELETE rendu par mon action Index Action/Vision. S'il y a une erreur dans le Delete Je veux renvoyer l'utilisateur vers le Index Action/Vue et afficher les erreurs qui sont stockées par l'application Delete l'action dans le ViewData.ModelState . Comment cela peut-il être fait en ASP.NET MVC ?

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Delete)]
public ActionResult Delete([ModelBinder(typeof(RdfUriBinder))] RdfUri graphUri)
{
    if (!ModelState.IsValid)
        return Index(); //this needs to be replaced with something that works :)

    return RedirectToAction("Index");
}

103voto

tvanfosson Points 268301

Stockez vos données de vue dans TempData et l'extraire à partir de là dans votre Index si elle existe.

   ...
   if (!ModelState.IsValid)
       TempData["ViewData"] = ViewData;

   RedirectToAction( "Index" );
}

 public ActionResult Index()
 {
     if (TempData["ViewData"] != null)
     {
         ViewData = (ViewDataDictionary)TempData["ViewData"];
     }

     ...
 }

[EDIT] J'ai vérifié la source en ligne pour MVC et il apparaît que le ViewData dans le contrôleur est paramétrable, il est donc probablement plus simple de transférer toutes les données du ViewData , y compris les ModelState à l'action Index.

0 votes

ViewData.ModelState n'a pas de setter.

0 votes

Je n'ai pas eu accès à VS pour le vérifier à la maison.

0 votes

Vous devrez vérifier la syntaxe de la fonction de copie - encore une fois, pas de VS à la maison.

42voto

bob Points 3408

Utiliser des filtres d'action (modèle PRG) (aussi simple que d'utiliser des attributs)

Mentionné ici y ici .

3 votes

La meilleure réponse à ce problème, selon moi.

2 votes

Oui, c'est la réponse. Je ne sais pas trop pourquoi ces attributs d'action ne sont pas présents dans le cadre MVC, car il s'agit d'un scénario assez courant.

0 votes

ModelState.Merge() est ce que je cherchais. Le lien est également excellent. +1

12voto

Eilon Points 12589

Veuillez noter que la solution de tvanfosson ne fonctionnera pas toujours, mais dans la plupart des cas, elle devrait convenir.

Le problème avec cette solution particulière est que si vous avez déjà des ViewData ou des ModelState, vous finissez par les écraser avec l'état de la requête précédente. Par exemple, la nouvelle requête peut avoir des erreurs d'état de modèle liées à des paramètres invalides passés à l'action, mais ces erreurs finissent par être cachées parce qu'elles sont écrasées.

Une autre situation où cela pourrait ne pas fonctionner comme prévu est celle où vous avez un filtre d'action qui initialise des erreurs ViewData ou ModelState. Là encore, ces erreurs seraient écrasées par ce code.

Nous étudions des solutions pour ASP.NET MVC qui vous permettraient de fusionner plus facilement l'état des deux requêtes, alors restez à l'écoute.

Merci, Eilon

0 votes

Hey Eilon, Est-ce que la réponse de @bob (du blog de Kazi Manzur Rashid) est toujours la meilleure façon de faire cela, ou est-ce que l'équipe MVC recommande une autre méthode actuellement ?

1 votes

@PatrickMcDonald Il n'y a rien de nouveau dans MVC auquel je puisse penser qui résoudrait ce problème. Cependant, je vous déconseille d'écraser complètement toutes les ViewData et d'être plus sélectif sur ce qui est copié de la requête précédente à la nouvelle requête.

0 votes

ModelState possède un Merge fonction.

5voto

Matthew Points 6447

Au cas où cela serait utile à quelqu'un, j'ai utilisé la solution recommandée par @bob en utilisant PRG :

voir point 13 -> lien .

J'ai rencontré un autre problème : les messages transmis dans le VeiwBag à la vue sont écrits et vérifiés/chargés manuellement à partir de TempData dans les actions du contrôleur lors de l'exécution d'une commande RedirectToAction("Action") . Dans un souci de simplification (et de maintenance), j'ai légèrement étendu cette approche pour vérifier et stocker/charger d'autres données. Mes méthodes d'action ressemblaient à quelque chose comme

 [AcceptVerbs(HttpVerbs.Post)]
 [ExportModelStateToTempData]
 public ActionResult ChangePassword(ProfileViewModel pVM) {
      bool result = MyChangePasswordCode(pVM.ChangePasswordViewModel);
      if (result) {
           ViewBag.Message = "Password change success";
      else {
           ModelState.AddModelError("ChangePassword", "Some password error");
      }
      return RedirectToAction("Index");
    }

Et mon action index :

[ImportModelStateFromTempData]
public ActionResult Index() {
    ProfileViewModel pVM = new ProfileViewModel { //setup }
    return View(pVM);
}

Le code des filtres d'action :

// Following best practices as listed here for storing / restoring model data:
// http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg
public abstract class ModelStateTempDataTransfer : ActionFilterAttribute {
    protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName;
}

:

public class ExportModelStateToTempData : ModelStateTempDataTransfer {
    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        //Only export when ModelState is not valid
        if (!filterContext.Controller.ViewData.ModelState.IsValid) {
            //Export if we are redirecting
            if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) {
                filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
            }
        }
        // Added to pull message from ViewBag
        if (!string.IsNullOrEmpty(filterContext.Controller.ViewBag.Message)) {
            filterContext.Controller.TempData["Message"] = filterContext.Controller.ViewBag.Message;
        }

        base.OnActionExecuted(filterContext);
    }
}

:

public class ImportModelStateFromTempData : ModelStateTempDataTransfer {
    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;

        if (modelState != null) {
            //Only Import if we are viewing
            if (filterContext.Result is ViewResult) {
                filterContext.Controller.ViewData.ModelState.Merge(modelState);
            } else {
                //Otherwise remove it.
                filterContext.Controller.TempData.Remove(Key);
            }
        }
        // Restore Viewbag message
        if (!string.IsNullOrEmpty((string)filterContext.Controller.TempData["Message"])) {
            filterContext.Controller.ViewBag.Message = filterContext.Controller.TempData["Message"];
        }

        base.OnActionExecuted(filterContext);
    }
}

Je réalise que mes changements ici sont une extension assez évidente de ce qui était déjà fait avec le ModelState par le code @ le lien fourni par @bob - mais j'ai dû tomber sur ce fil avant même de penser à le manipuler de cette façon.

0 votes

Merci, il faut juste modifier la ligne de ImportModelStateFromTempData pour les vues partielles >> if (filterContext.Result is ViewResult || filterContext.Result is PartialViewResult)

-3voto

Ty. Points 1890

Essayez peut-être

return View("Index");

au lieu de

return Index();

0 votes

Cela ne fonctionne pas, car cela n'exécute pas la logique de l'action d'indexation. Tout ce qu'elle fait, c'est essayer de rendre le modèle actuel en utilisant la vue Index.

0 votes

Ne voulez-vous pas simplement montrer les erreurs du modèle sur la même vue que celle à partir de laquelle vous avez posté votre message ? Que faites-vous dans l'action Index qui doit être exécutée lorsqu'il y a des erreurs de modèle ? Je renvoie simplement View("ViewName", model) lorsqu'il y a des erreurs et cela fonctionne bien.

0 votes

Non, je veux rediriger vers l'action Index et lier la vue aux données générées par cette action ainsi qu'au ModelState défini par l'action Delete qui a échoué.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X