ASP.NET MVC: Pass parameters when redirecting from one action to another
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<T>(Expression<Action<T>> action)
where T : Controller
{
return ControllerExtensions.RedirectToAction(this, action);
}
on April 6th, 2009 at 7:03 pm
ASP.NET MVC: Pass parameters when redirecting from to another action…
You’ve been kicked (a good thing) - Trackback from DotNetKicks.com…
on April 12th, 2009 at 11:54 pm
[...] 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 [...]
on May 15th, 2009 at 6:34 pm
Thanks a lot !! I was wondering how to do this..
on July 6th, 2009 at 4:19 pm
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
on July 6th, 2009 at 4:54 pm
@Brandon,
I haven’t run into that problem. Can you post some code snippets of what you’re trying to do?
on July 7th, 2009 at 7:50 am
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
on July 7th, 2009 at 7:53 am
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; }
}
on July 7th, 2009 at 7:56 am
@Brandon,
Make sure that your MVCContrib is up to date. This looks like a bug that I fixed on March 25.
Jon
on July 7th, 2009 at 12:24 pm
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
on October 8th, 2009 at 9:39 am
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 ?
on October 8th, 2009 at 8:21 pm
@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.
on November 5th, 2009 at 10:42 am
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
on November 5th, 2009 at 11:14 am
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
on November 5th, 2009 at 11:40 am
@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.
on November 5th, 2009 at 1:08 pm
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!
on February 28th, 2010 at 6:01 am
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
on April 12th, 2010 at 3:36 am
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
on May 25th, 2010 at 7:41 pm
@Nick Formosa — did you get that sorted out, I have the same issue.