software solutions / project leadership / agile coaching and training

ASP.NET MVC: Pass parameters when redirecting from one action to another

Posted on April 6, 2009

In ASP.NET MVC, it’s common to have a situation where you post to one controller action and then redirect to another controller action. Let’s say that you want to pass values from the first controller method to the other. The problem is that in out-of-the-box ASP.NET MVC, there is no way to redirect to another action and pass a parameter into the action that you are redirecting to. Here is one way you can get around this problem:

public class AccountController : Controller
{
[AcceptGet]
public ActionResult Index(IndexViewModel model)
{
model.Message = (string) TempData["Message"];

return View(model);
}

[AcceptPost]
public ActionResult Save(SaveUpdateModel model)
{
// save the information

TempData["Message"] = model.Message;

return RedirectToAction("Index");
}
}

The reason that you would do a redirect in this case is so that (a) you can reuse the code in the Index method and (b) you are redirecting to a GET operation, so if the user clicks Refresh in their browser, it won’t try to re-post the previous page.

I don’t really like how that code works, for various reasons:

  • I’m using hardcoded strings (“Index” and “Message”). These might not get caught by refactoring tools if I want to change names, and I don’t get compile time checking on them.
  • In the Index() method, I have to grab the value out of TempData. That works fine if you’re being redirected from the Save() action, but what if I’m being redirected from another action? What if I’m not being redirected from anywhere and the user is accessing the action directly? Ideally, when the Index() method is called, it should have everything that it needs in its parameters at the beginning of the method.

Personally, I don’t like to see TempData or ViewData anywhere in my controller methods. Don’t get me wrong, I need them and I’m going to use them, but I want it all done under the covers. I favor the “One Model In, One Model Out” pattern, where each controller action method takes in one model parameter (or none) and returns another model. Doing it this way allows me to avoid the use of magic strings and hashtables. I want my code to look like this:

[PassParametersDuringRedirect]
public class AccountController : Controller
{
[AcceptGet]
public ActionResult Index(IndexPresentationModel model)
{
return View(model);
}

[AcceptPost]
public ActionResult Save(SaveUpdateModel model)
{
// save the information

var presentationModel = new IndexPresentationModel();

presentationModel.Message = model.Message;

return this.RedirectToAction(c => c.Index(presentationModel));
}
}

This code does exactly what I want. Now…

  • I don’t have any magic strings.
  • Everything is strongly typed.
  • The parameter passed to the Index() method will always be complete. I don’t have to load anything out of TempData to get it to work.

One thing I love about ASP.NET MVC is that it is very extensible, so you can usually make it do what you want. This is the purpose of the MVCContrib project — people extending ASP.NET MVC to make it even better.

You may notice that in the code snippet above, I decorated my controller class with the [PassParametersDuringRedirect] attribute. Now, any parameters that I pass into RedirectToAction() will get passed into the action that I redirected to.

One slight problem… this new version of RedirectToAction() is an extension method on the Controller class, which means to call it, you would have to write “this.RedirectToAction()”. If you don’t want to have to write “this.” every time, you can just create a base controller class that has this method:

protected RedirectToRouteResult RedirectToAction(Expression> action)
where T : Controller
{
return ControllerExtensions.RedirectToAction(this, action);
}

24 Comments »

  1. ASP.NET MVC: Pass parameters when redirecting from to another action…

    You’ve been kicked (a good thing) – Trackback from DotNetKicks.com…

    DotNetKicks.com — April 6, 2009 @ 7:03 pm

  2. [...] to VoteASP.NET MVC: Pass parameters when redirecting from one action to another (4/6/2009)Monday, April 06, 2009 from jonkruger.comIn ASP.NET MVC , it’s common to have a situation where [...]

    ASP.NET MVC Archived Blog Posts, Page 1 — April 12, 2009 @ 11:54 pm

  3. Thanks a lot !! I was wondering how to do this..

    Dilish — May 15, 2009 @ 6:34 pm

  4. Hi Jon,

    Thanks for posting this article! I am not very fond of using TempData explicitly in my controller classes and this is a really helpful solution. Everything appears to be working with this approach. TempData is being populated with the object parameter and I’m able to use it in the second controller method. However, a ModelError exists as part of the ModelStateDictionary on the object that is being passed as a parameter to the controller method. The error text is “The parameter conversion from type ‘System.String’ to type ” failed because no TypeConverter can convert between these types…’ The only way I was able to get rid of the error was to override ToString() on my object and return an empty string, but this does not seem like a very elegant approach. Have you had this issue or have any ideas how this can be resolved?

    Thanks,

    Brandon

    Brandon — July 6, 2009 @ 4:19 pm

  5. @Brandon,

    I haven’t run into that problem. Can you post some code snippets of what you’re trying to do?

    Jon Kruger — July 6, 2009 @ 4:54 pm

  6. Here is a short example.

    namespace MvcApplication1.Controllers
    {
    [PassParametersDuringRedirect]
    public class HomeController : Controller
    {
    public ActionResult Index()
    {
    //build basic DTO to pass as parameter
    TestModel testModel = new TestModel();
    testModel.Name = “Brandon”;
    testModel.TestModelID = 5;

    //redirect to another controller action, passing TestModel as parameter
    return this.RedirectToAction(c => c.About(testModel));
    }

    public ActionResult About(TestModel testModel)
    {
    //TestData is properly populated with the TestModel object, but there is an error in the ModelStateDictionary
    //triggering the ValidationSummary on the view to display itself
    ModelStateDictionary msd = ViewData.ModelState;

    foreach (var modelState in msd.Values)
    {
    foreach (var modelError in modelState.Errors)
    Debug.WriteLine(modelError.Exception.ToString());
    }

    return View();
    }
    }
    }

    The Debug output displays this exception:

    System.InvalidOperationException: The parameter conversion from type ‘System.String’ to type ‘MvcApplication1.Models.TestModel’ failed because no TypeConverter can convert between these types.
    at System.Web.Mvc.ValueProviderResult.ConvertSimpleType(CultureInfo culture, Object value, Type destinationType)
    at System.Web.Mvc.ValueProviderResult.UnwrapPossibleArrayType(CultureInfo culture, Object value, Type destinationType)
    at System.Web.Mvc.ValueProviderResult.ConvertTo(Type type, CultureInfo culture)
    at System.Web.Mvc.DefaultModelBinder.ConvertProviderResult(ModelStateDictionary modelState, String modelStateKey, ValueProviderResult valueProviderResult, Type destinationType)

    Thanks again,

    Brandon

    Brandon — July 7, 2009 @ 7:50 am

  7. Jon,

    Here is some more information. The URL ends up looking like this:

    http://localhost:26964/Home/About?testModel=MvcApplication1.Models.TestModel

    Here is the TestModel object definition:

    public class TestModel
    {
    public string Name { get; set; }
    public int TestModelID { get; set; }
    }

    Brandon — July 7, 2009 @ 7:53 am

  8. @Brandon,

    Make sure that your MVCContrib is up to date. This looks like a bug that I fixed on March 25.

    Jon

    Jon Kruger — July 7, 2009 @ 7:56 am

  9. Hi Jon,

    I was using the most recent release of the MvcContrib bits, released on Mar 25. The following update addressed the issue I’m having:

    r951 by Jeremy.Skinner on Jun 14, 2009 Diff
    Issue #4405 – PassParametersDuringRedirect
    should not add reference types to the
    RouteData.

    The specific method of interest is RemoveReferenceTypesFromRouteValues.

    Thanks again for your help. No more TempData references in my controllers!

    Brandon

    Brandon — July 7, 2009 @ 12:24 pm

  10. when i try to redirectaction method, i get error like below
    __________________________________
    return this.RedirectToAction(c => c.CreateSqlTask(custList));
    __________________________________
    Could not load file or assembly ‘Microsoft.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.

    have any idea ?

    sefer kılıç — October 8, 2009 @ 9:39 am

  11. @sefer,

    Microsoft.Web.Mvc, also known as the “MVC Futures” assembly, comes as part of MVCContrib. It should be packaged with the MVCContrib DLLs that you downloaded.

    Jon Kruger — October 8, 2009 @ 8:21 pm

  12. I am having a similar problem with persisting model data over various views under the same controller. Is this a good solution for view to view communication of a common ViewModel – or is there a better way to do this in the context of a single view?

    More info on the problem: http://stackoverflow.com/questions/1681325/asp-net-mvc-how-to-persist-model-over-various-views

    Thanks,
    Adam Tolley

    Adam Tolley — November 5, 2009 @ 10:42 am

  13. To reply to my own post – It looks like this also supports intra-controller redirects. My question then changes slightly – Why would I use a redirect rather than just returning View(“otherView”, model); ?

    Link to answer http://mvccontrib.codeplex.com/wikipage?title=RedirectToAction&referringTitle=Documentation

    Adam Tolley — November 5, 2009 @ 11:14 am

  14. @Adam,

    A lot of times the action that you’re redirecting to has a bunch of logic in it. So you could just return View() if you want, because in your case you don’t have any logic in the action method that you would be redirecting to if you did the redirect.

    Jon Kruger — November 5, 2009 @ 11:40 am

  15. OH! Light bulb moment! Calling View(“otherView”, model) causes the current action to dump data into that view but not invoke the method – this seems obvious but I had missed it for some reason. I think that clears up a lot, Thanks!

    Adam Tolley — November 5, 2009 @ 1:08 pm

  16. Hi Jon,

    This was exactly what i was looking for, but alas, my model parameter which im trying to pass over to the Action is not being passed during my redirect. Am i doing something wrong or missing something?

    i’ve added the decoration [PassParametersDuringRedirect], and the code looks like:

    if(!ModelState.IsValid)
    {
    return this.RedirectToAction(c => c.Index(model));
    }

    The Action index looks like :

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Index(RegisterModel model)

    Any help would be great.

    ps. Curretly Using MVC2 and MVCContrib 2.0.61

    Thanks Nick

    Nick Formosa — February 28, 2010 @ 6:01 am

  17. Hi,
    TempData is stored in session right? Won’t this give problems in a load balanced environment (if session state is in-proc)?

    Thanks,
    Steven

    Steven Hillaert — April 12, 2010 @ 3:36 am

  18. @Nick Formosa — did you get that sorted out, I have the same issue.

    Ryk — May 25, 2010 @ 7:41 pm

  19. hi
    my name is pavan
    i got stucked with small problem.

    1.i wanted to change the case of the url generated when return this.RedirectToAction(c => c.Login(String.Empty)); is executed .

    2.is there any way to know to which view the code will return

    pavankumar — October 4, 2010 @ 1:05 am

  20. Very nice article, really like the “One Model In, One Model Out” idea.

    What about buttons? Do you prefer a parameter like “string cancelButton” on your Save method or would you add CancelButton as a property on your SaveUpdateModel (which I assume MVC would also bind to)?

    -glenn- — July 28, 2011 @ 10:42 am

  21. This is just what I was looking for. So nice to be able to pass models between controller instead of having to use TempData. Thanks!

    David — January 27, 2012 @ 12:57 am

  22. I’ve noticed that your example uses this. What about passing parameters to another controller class? Is that possible?

    Farhad — March 5, 2012 @ 8:43 pm

  23. It does not work for me. The model passed to the next Action was always null. What’s missing?

    using Microsoft.Web.Mvc;
    using MvcContrib.Filters;

    namespace MySeat.WebClient.Controllers
    {
    [PassParametersDuringRedirect]
    public class SkedsController : Controller
    {

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult MakeIt(MyChosenViewModel chosen, FormCollection fc)
    {
    if (ModelState.IsValid)
    {
    // do something

    MyOtherViewModel otherModel = new MyOtherViewModel();
    otherModel.Chosen = chosen;

    return this.RedirectToAction(x => x.SelectIt(otherModel));

    }
    return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult SelectIt(MyOtherViewModel otherModel)
    {
    // here i got null otherModel

    // do other things and display View
    return View(otherModel);
    }
    }
    }

    euo — January 23, 2013 @ 2:15 am

  24. [...] Different Rediret [...]

    Different Rediret | ASP.DOTNET SAMPLE — January 29, 2014 @ 4:24 am

Leave a comment





SERVICES
SOFTWARE SOLUTIONS
I have over 10 years of software development experience on several different platforms (mostly Ruby and .NET). I recognize that software is expensive, so I'm always trying to find ways to speed up the software development process, but at the same time remembering that high quality is essential to building software that stands the test of time.
PROJECT LEADERSHIP
I have experience leading and architecting large Agile software projects and coordinating all aspects of a project's lifecycle. Whether you're looking for technical expertise or someone to lead all aspects of an Agile project, I have proven experience from multiple projects in different environments that can help make your project a success.
AGILE COACHING
I believe that Agile processes and tools should be applied with common sense. I've spent the last 6 years working on Agile projects as a consultant in many different environments, both in leadership roles and as a practitioner doing the work. I can help you find out how Agile can work best in your organization, not just apply a prescriptive process.
TEST DRIVEN DEVELOPMENT TRAINING
TDD Boot Camp is a hands-on, three day, comprehensive training course that will teach you all of the skills, tools, frameworks that you will need to use test-driven development to develop real world .NET applications. If you're not looking for something that intensive, check out the the half-day version.
Have any questions? Contact me for more information.
PRESENTATIONS
The Business of You: 10 Steps For Running Your Career Like a Business
From CONDG 2012, Stir Trek 2014
From Stir Trek 2013, DogFoodCon 2013
From Stir Trek 2012, QA or the Highway 2014
(presented with Brandon Childers, Chris Hoover, Laurel Odronic, and Lan Bloch from IGS Energy) from Path to Agility 2012
(presented with Paul Bahler and Kevin Chivington from IGS Energy)
From CodeMash 2011
An idea of how to make JavaScript testable, presented at Stir Trek 2011. The world of JavaScript frameworks has changed greatly since then, but I still agree with the concepts.
A description of how test-driven development works along with some hands-on examples.
From CodeMash 2010
From CodeMash 2010