154

When you call RedirectToAction within a controller, it automatically redirects using an HTTP GET. How do I explicitly tell it to use an HTTP POST?

I have an action that accepts both GET and POST requests, and I want to be able to RedirectToAction using POST and send it some values.

Like this:

this.RedirectToAction(
    "actionname",
    new RouteValueDictionary(new { someValue = 2, anotherValue = "text" })
);

I want the someValue and anotherValue values to be sent using an HTTP POST instead of a GET. Does anyone know how to do this?

tereško
  • 58,060
  • 25
  • 98
  • 150
Chris Pietschmann
  • 29,502
  • 35
  • 121
  • 166
  • The answer posting by jason will work in most scenarios, the only problem which i see is that it is accident prone. i.e. Calling a action method directly **bypasses all filters** applied to the action. So , in case there is any authentication or counter sort of filter applied to the action method, that data might be lost. Calling a action method directly will work, but it should be carefully applied. – amarnath chatterjee Feb 26 '12 at 17:36

7 Answers7

170

For your particular example, I would just do this, since you obviously don't care about actually having the browser get the redirect anyway (by virtue of accepting the answer you have already accepted):

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index() {
   // obviously these values might come from somewhere non-trivial
   return Index(2, "text");
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(int someValue, string anotherValue) {
   // would probably do something non-trivial here with the param values
   return View();
}

That works easily and there is no funny business really going on - this allows you to maintain the fact that the second one really only accepts HTTP POST requests (except in this instance, which is under your control anyway) and you don't have to use TempData either, which is what the link you posted in your answer is suggesting.

I would love to know what is "wrong" with this, if there is anything. Obviously, if you want to really have sent to the browser a redirect, this isn't going to work, but then you should ask why you would be trying to convert that regardless, since it seems odd to me.

starball
  • 20,030
  • 7
  • 43
  • 238
Jason Bunting
  • 58,249
  • 14
  • 102
  • 93
  • 7
    Who knows why you were downvoted. This is a very useful method. – Peter J Jun 03 '10 at 18:25
  • 2
    This is how I always solved this problem as well. Downvoting this makes no sense. – Adrian Grigore Jan 10 '11 at 13:08
  • 41
    I voted up, although I disagree with calling people idiots when you don't know them. – Joseph Yaduvanshi Jan 18 '11 at 21:38
  • 3
    I'm not a downvoter, but the one caution with this is if you were to call a view with a different name, or if the parameters are important, they are lost. The reason being is the URL will reflect the action+parameters before the server side redirect. This can lead to confusion by the user, especially if they refreshed the page and then found themselves at a previous page(because the refresh used the old URL). This technique is essentially very similar to asp.net's Server.Transfer, and the same cautions should be exercised. – AaronLS Jun 26 '12 at 15:40
  • Is it possible to do something similar to this when the second action resides in a separate controller, i.e. somehow specify in one of the actions a different path to the view? – Alejo Aug 17 '12 at 14:00
  • 17
    I didn't downvote per se but I can see reason to. This method violates the coding convention set up by the MVC pattern. It *only* works when calling the same action. If the action is another, even on the same controller, the routing values are screwed and the wrong view will be returned. In short: Don't do this. – erlando Sep 24 '12 at 14:01
  • 1
    @erlando What do you mean it only works when the action is the same action? I do this all the time to call other actions. The only issue I've encountered is that when you return View(), you need to specify the appropriate view, or it will think you want the view from the calling action. – DCShannon Jun 04 '14 at 22:22
  • 1
    I might be stupid, but this does not even answers the question, if i'm in `Index2()` then still, I can't directly call the POST action of `Index1(string s, int i)` – Antoine Pelletier Jul 05 '17 at 17:17
  • You have to pass through the `[get]` to reach the `[post]` if i understand. So there is no calling `[post]` directly isn't it ? – Antoine Pelletier Jul 10 '17 at 13:35
  • @AntoinePelletier - ultimately, those are just methods. The HTTP Verb attributes are read by the layer of code that is processing the request, but at the same time, if you are just calling a method within a class from another method within that same instantiation of said class, I believe it doesn't care about or honor those attributes. All I know is, it worked and over 100 people feel the same way. ;) – Jason Bunting Aug 09 '17 at 20:09
  • Yeah, i guess, i thought it could solve my problem too. But in my case, i can't go through the `[get]` but i'm going to call another useless get in order to reach the post i want. I hope it works – Antoine Pelletier Aug 10 '17 at 14:47
  • @JamesBunting can you explain what your solution is actually doing and how it resolves the problem? – Ciaran Gallagher May 29 '18 at 11:09
  • In some circumstances this will work, but not ideal. I'm actually on SO because actions identical to this have caused problems in our source-code & I'm trying to fix it. As @erlando noted, `routing values are screwed`. You'll also get messed up `ModelState` (If you call `ModelState.IsValid` within the "Post" action, it will validate the "Get" action's model instead), action filters may not be applied, etc. @vicky's answer seems to be the only one that's generally appropriate here & directly answers the question, but it's a bit of a hack. – Lovethenakedgun Sep 07 '22 at 12:35
122

HTTP doesn't support redirection to a page using POST. When you redirect somewhere, the HTTP "Location" header tells the browser where to go, and the browser makes a GET request for that page. You'll probably have to just write the code for your page to accept GET requests as well as POST requests.

Eli Courtwright
  • 186,300
  • 67
  • 213
  • 256
24

If you want to pass data between two actions during a redirect without include any data in the query string, put the model in the TempData object.

ACTION

TempData["datacontainer"] = modelData;

VIEW

var modelData= TempData["datacontainer"] as ModelDataType; 

TempData is meant to be a very short-lived instance, and you should only use it during the current and the subsequent requests only! Since TempData works this way, you need to know for sure what the next request will be, and redirecting to another view is the only time you can guarantee this.

Therefore, the only scenario where using TempData will reliably work is when you are redirecting.

Otto Kanellis
  • 3,629
  • 1
  • 23
  • 24
13

try this one

return Content("<form action='actionname' id='frmTest' method='post'><input type='hidden' name='someValue' value='" + someValue + "' /><input type='hidden' name='anotherValue' value='" + anotherValue + "' /></form><script>document.getElementById('frmTest').submit();</script>");
vicky
  • 1,546
  • 1
  • 18
  • 35
5

I would like to expand the answer of Jason Bunting

like this

ActionResult action = new SampelController().Index(2, "text");
return action;

And Eli will be here for something idea on how to make it generic variable

Can get all types of controller

Yitzhak Weinberg
  • 2,324
  • 1
  • 17
  • 21
  • 7
    You shouldn't create an instance to a controller with `new ...()` because you will lose the `RequestContext` - if you are already in the same controller, you might not need to create a new instance. Otherwise, take the following way: `SampelController sampleController = DependencyResolver.Current.GetService()` then: `sampleController.ControllerContext = new ControllerContext(Request.RequestContext, sampleController);` then you can `return sampleController.Index(2, "text");` Just a hint :) – Matthias Burger Jul 31 '20 at 06:35
0

I have just experienced the same problem.

The solution was to call the controller action like a function:

return await ResendConfirmationEmail(new ResendConfirmationEmailViewModel() { Email = input.Email });

The controller action:

[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> ResendConfirmationEmail(ResendConfirmationEmailViewModel input)
{
    ...
 
    return View("ResendConfirmationEmailConfirmed");
}
SNag
  • 17,681
  • 10
  • 54
  • 69
PowerMan2015
  • 1,307
  • 5
  • 19
  • 40
  • This is good but will only be possible within the same controller. From another controller, you'd need an instance to call the method, like possibly `new EmailController().ResendConfirmationEmail(...);` ? – SNag Mar 09 '22 at 20:06
0

Try this: In your view from Page1

<form method="post">
  <input name="text" />
  <button type="submit">Send</button>
</form>

In your control from page1

public IActionResult OnPost(string text){
  return RedirectToPagePreserveMethod("Page2");
}

In your control from Page2

public IActionResult OnPost(string text){
      ViewData["text"] = text;
      return Page();
    }

In your View from Page2

<h1>@ViewData["Text"]</h1>

I Tried in .NetCore 6