85 votes

ASP.NET MVC - Comment Préserver ModelState Erreurs à Travers RedirectToAction?

J'ai le texte suivant deux méthodes d'action (simplifié de la question):

[HttpGet]
public ActionResult Create(string uniqueUri)
{
   // get some stuff based on uniqueuri, set in ViewData.  
   return View();
}

[HttpPost]
public ActionResult Create(Review review)
{
   // validate review
   if (validatedOk)
   {
      return RedirectToAction("Details", new { postId = review.PostId});
   }  
   else
   {
      ModelState.AddModelError("ReviewErrors", "some error occured");
      return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
   }   
}

Donc, si la validation passe, je redirige vers une autre page (confirmation).

Si une erreur se produit, j'ai besoin d'afficher la même page avec l'erreur.

Si je n' return View(), l'erreur est affichée, mais si je n' return RedirectToAction (comme ci-dessus), il perd les erreurs de Modèle.

Je ne suis pas surpris par la question, je me demandais comment vous avez gérer cela?

Je pourrais bien sûr, il suffit de retourner le même point de Vue à la place de la redirection, mais j'ai de la logique dans le "Créer" la méthode qui remplit le point de vue des données, que je ne voudrais avoir à dupliquer.

Toutes les suggestions?

76voto

asgeo1 Points 3336

J'ai eu à résoudre ce problème, aujourd'hui, moi-même, et suis tombé sur cette question.

Certaines réponses sont utiles (à l'aide de TempData), mais ne pas vraiment répondre à la question à portée de main.

Le meilleur conseil que j'ai trouvé sur ce blog:

http://www.jefclaes.be/2012/06/persisting-model-state-when-using-prg.html

Fondamentalement, l'utilisation TempData pour enregistrer et restaurer les ModelState objet. Cependant, c'est beaucoup plus propre si vous le résumé de cette distance dans les attributs.

E. g.

public class SetTempDataModelStateAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);         
        filterContext.Controller.TempData["ModelState"] = 
           filterContext.Controller.ViewData.ModelState;
    }
}

public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        if (filterContext.Controller.TempData.ContainsKey("ModelState"))
        {
            filterContext.Controller.ViewData.ModelState.Merge(
                (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);
        }
    }
}

Alors que par votre exemple, vous pouvez sauvegarder / restaurer la ModelState comme suit:

[HttpGet]
[RestoreModelStateFromTempData]
public ActionResult Create(string uniqueUri)
{
    // get some stuff based on uniqueuri, set in ViewData.  
    return View();
}

[HttpPost]
[SetTempDataModelState]
public ActionResult Create(Review review)
{
    // validate review
    if (validatedOk)
    {
        return RedirectToAction("Details", new { postId = review.PostId});
    }  
    else
    {
        ModelState.AddModelError("ReviewErrors", "some error occured");
        return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
    }   
}

Si vous aussi vous voulez passer le modèle dans TempData (comme bigb a suggéré), alors vous pouvez toujours le faire aussi.

49voto

Riapp Points 2889

Vous avez besoin d'avoir la même instance d' Review sur votre HttpGetaction. Pour le faire vous devez enregistrer un objet Review review dans la variable temp sur votre HttpPost action, puis de la restaurer sur HttpGet action.

[HttpGet]
public ActionResult Create(string uniqueUri)
{
   //Restore
   Review review = TempData["Review"] as Review;            

   // get some stuff based on uniqueuri, set in ViewData.  
   return View(review);
}
[HttpPost]
public ActionResult Create(Review review)
{
   //Save you object
   TempData["Review"] = review;

   // validate review
   if (validatedOk)
   {
      return RedirectToAction("Details", new { postId = review.PostId});
   }  
   else
   {
      ModelState.AddModelError("ReviewErrors", "some error occured");
      return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
   }   
}

Aussi je voudrais des conseils, si vous voulez le faire fonctionner aussi lorsque le bouton actualiser de votre navigateur enfoncé après HttpGet action exécutée première fois, vous pouvez aller comme ça

  Review review = TempData["Review"] as Review;  
  TempData["Review"] = review;

Sinon sur le bouton actualiser objet review sera vide car il n'y aurait pas toutes les données en TempData["Review"].

7voto

Wim Points 1445

Pourquoi ne pas créer une fonction privée avec la logique de la "Création" de la méthode et de l'appel de cette méthode à partir de l'Obtenir et de la méthode Post et il suffit de faire retour (Vue).

4voto

CRice Points 4717

Je vous suggère de revenir à la vue, et d'éviter la duplication via un attribut sur l'action. Voici un exemple de remplissage pour afficher les données. Vous pourriez faire quelque chose de similaire avec votre méthode de création de la logique.

public class GetStuffBasedOnUniqueUriAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var filter = new GetStuffBasedOnUniqueUriFilter();

        filter.OnActionExecuting(filterContext);
    }
}


public class GetStuffBasedOnUniqueUriFilter : IActionFilter
{
    #region IActionFilter Members

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {

    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData["somekey"] = filterContext.RouteData.Values["uniqueUri"];
    }

    #endregion
}

Et pour ceux qui ont juste eu du mal à imaginer comment cela pourrait fonctionner:

[HttpGet, GetStuffBasedOnUniqueUri]
public ActionResult Create()
{
    return View();
}

[HttpPost, GetStuffBasedOnUniqueUri]
public ActionResult Create(Review review)
{
    // validate review
    if (validatedOk)
    {
        return RedirectToAction("Details", new { postId = review.PostId });
    }

    ModelState.AddModelError("ReviewErrors", "some error occured");
    return View(review);
}

4voto

rob waminal Points 4567

Je pourrais utiliser TempData["Errors"]

TempData sont passés à travers des actions de préservation de données de 1 heure.

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