117

I have an MVC controller that has this Action Method:

[HttpPost]
public ActionResult SubmitAction()
{
     // Get Post Params Here
 ... return something ...
}

The form is a non-trivial form with a simple textbox.

Question

How I access the parameter values?

I am not posting from a View, the post is coming externally. I'm assuming there is a collection of key/value pairs I have access to.

I tried Request.Params.Get("simpleTextBox"); but it returns error "Sorry, an error occurred while processing your request.".

Cœur
  • 37,241
  • 25
  • 195
  • 267
Richard
  • 1,677
  • 3
  • 13
  • 15

4 Answers4

160

You could have your controller action take an object which would reflect the form input names and the default model binder will automatically create this object for you:

[HttpPost]
public ActionResult SubmitAction(SomeModel model)
{
    var value1 = model.SimpleProp1;
    var value2 = model.SimpleProp2;
    var value3 = model.ComplexProp1.SimpleProp1;
    ...

    ... return something ...
}

Another (obviously uglier) way is:

[HttpPost]
public ActionResult SubmitAction()
{
    var value1 = Request["SimpleProp1"];
    var value2 = Request["SimpleProp2"];
    var value3 = Request["ComplexProp1.SimpleProp1"];
    ...

    ... return something ...
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 6
    I would just like to point out that you lose the backup of the compiler in option 2. If the model changes, the compiler will not catch the change in the related controllers. There are good cases for option 2 but I wouldn't encourage wide use. – Serguei Fedorov Mar 02 '16 at 15:06
  • 3
    Sometimes you need Ugly stuff, is good to have a choice when you already know what the best practices are – Oscar Ortiz May 05 '16 at 20:22
  • As someone still learning dot net, why is the second way uglier? – Goose Jan 12 '17 at 18:16
  • 3
    @Goose, because it is magic strings. You don't get any compile time safety. Your code will fail at runtime if you make a typo in the name of the variable whereas if you are using strong typing, the compiler will be your friend. – Darin Dimitrov Jan 12 '17 at 22:11
  • @DarinDimitrov makes sense. Different than the world I come from. Very nice feature. – Goose Jan 13 '17 at 00:47
  • Request doesn't have a [] operator! – Joshua Mar 22 '19 at 20:45
  • on new ASP.NET core 3.1 `Request.Form["ABC"]`, yes seems Request isn't assoc object @Joshua. I tried to use it using cshtml, and if the request isn't POST it will cause error. – Benyamin Limanto Aug 08 '20 at 11:39
  • @BenyaminLimanto Of course it will cause an error when you're trying to read POST data from a non-POST request lol – Zimano Apr 22 '21 at 11:12
107

Simply, you can use FormCollection like:

[HttpPost] 
public ActionResult SubmitAction(FormCollection collection)
{
     // Get Post Params Here
 string var1 = collection["var1"];
}

You can also use a class, that is mapped with Form values, and asp.net mvc engine automagically fills it:

//Defined in another file
class MyForm
{
  public string var1 { get; set; }
}

[HttpPost]
public ActionResult SubmitAction(MyForm form)
{      
  string var1 = form1.Var1;
}
sam
  • 4,357
  • 5
  • 29
  • 34
Adeel
  • 19,075
  • 4
  • 46
  • 60
37

The answers are very good but there is another way in the latest release of MVC and .NET that I really like to use, instead of the "old school" FormCollection and Request keys.


Consider a HTML snippet contained within a form tag that either does an AJAX or FORM POST.

<input type="hidden"   name="TrackingID" 
<input type="text"     name="FirstName"  id="firstnametext" />
<input type="checkbox" name="IsLegal"  value="Do you accept terms and conditions?" />

Your controller will actually parse the form data and try to deliver it to you as parameters of the defined type. I included checkbox because it is a tricky one. It returns text "on" if checked and null if not checked. The requirement though is that these defined variables MUST exists (unless nullable(remember though that string is nullable)) otherwise the AJAX or POST back will fail.

[HttpPost]
public ActionResult PostBack(int TrackingID, string FirstName, string IsLegal){
    MyData.SaveRequest(TrackingID,FirstName, IsLegal == null ? false : true);
}

You can also post back a model without using any razor helpers. I have come across that this is needed some times.

public Class HomeModel
{
  public int HouseNumber { get; set; }
  public string StreetAddress { get; set; }
}

The HTML markup will simply be ...

<input type="text" name="variableName.HouseNumber" id="whateverid" >

and your controller(Razor Engine) will intercept the Form Variable "variableName" (name is as you like but keep it consistent) and try to build it up and cast it to MyModel.

[HttpPost]
public ActionResult PostBack(HomeModel variableName){
    postBack.HouseNumber; //The value user entered
    postBack.StreetAddress; //the default value of NULL.
}

When a controller is expecting a Model (in this case HomeModel) you do not have to define ALL the fields as the parser will just leave them at default, usually NULL. The nice thing is you can mix and match various models on the Mark-up and the post back parse will populate as much as possible. You do not need to define a model on the page or use any helpers.

TIP: The name of the parameter in the controller is the name defined in the HTML mark-up "name=" not the name of the Model but the name of the expected variable in the !


Using List<> is bit more complex in its mark-up.

<input type="text" name="variableNameHere[0].HouseNumber" id="id"           value="0">
<input type="text" name="variableNameHere[1].HouseNumber" id="whateverid-x" value="1">
<input type="text" name="variableNameHere[2].HouseNumber"                   value="2">
<input type="text" name="variableNameHere[3].HouseNumber" id="whateverid22" value="3">

Index on List<> MUST always be zero based and sequential. 0,1,2,3.

[HttpPost]
public ActionResult PostBack(List<HomeModel> variableNameHere){
     int counter = MyHomes.Count()
     foreach(var home in MyHomes)
     { ... }
}

Using IEnumerable<> for non zero based and non sequential indices post back. We need to add an extra hidden input to help the binder.

<input type="hidden" name="variableNameHere.Index" value="278">
<input type="text" name="variableNameHere[278].HouseNumber" id="id"      value="3">

<input type="hidden" name="variableNameHere.Index" value="99976">
<input type="text" name="variableNameHere[99976].HouseNumber" id="id3"   value="4">

<input type="hidden" name="variableNameHere.Index" value="777">
<input type="text" name="variableNameHere[777].HouseNumber" id="id23"    value="5">

And the code just needs to use IEnumerable and call ToList()

[HttpPost]
public ActionResult PostBack(IEnumerable<MyModel> variableNameHere){
     int counter = variableNameHere.ToList().Count()
     foreach(var home in variableNameHere)
     { ... }
}

It is recommended to use a single Model or a ViewModel (Model contianing other models to create a complex 'View' Model) per page. Mixing and matching as proposed could be considered bad practice, but as long as it works and is readable its not BAD. It does however, demonstrate the power and flexiblity of the Razor engine.

So if you need to drop in something arbitrary or override another value from a Razor helper, or just do not feel like making your own helpers, for a single form that uses some unusual combination of data, you can quickly use these methods to accept extra data.

Piotr Kula
  • 9,597
  • 8
  • 59
  • 85
  • Using the Index option is obscure. Who on God's green earth would have known to use that or that it even existed?! But, I'm glad I found this post. It's going to save a great deal of network traffic. – Michael Silver Nov 22 '16 at 00:14
  • 1
    This worked for me but only after i changed to @Html.Hidden("myId") – radkan Feb 02 '17 at 22:56
  • @Piotr - please fix your reference inconsistencies with MyModel and MyHomes. It causes confusion how it is currently. – Spencer Sullivan May 20 '19 at 16:23
17

If you want to get the form data directly from Http request, without any model bindings or FormCollection you can use this:

[HttpPost] 
public ActionResult SubmitAction() {

    // This will return an string array of all keys in the form.
    // NOTE: you specify the keys in form by the name attributes e.g:
    // <input name="this is the key" value="some value" type="test" />
    var keys = Request.Form.AllKeys;

    // This will return the value for the keys.
    var value1 = Request.Form.Get(keys[0]);
    var value2 = Request.Form.Get(keys[1]);
}
A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
  • 2
    Caution that this may be bad form (no pun intended) but sometimes you just want the form values and you can't cleanly change the function signature. This is the only solution here that suited my particular situation. – Ryan Jul 27 '16 at 02:33
  • How to unit test this method with those static references? FormCollection would be way more desirable when it comes to testing. – Kees Jun 30 '17 at 15:20
  • @KeesdeWit if you read the previous comment this is not the best way, but sometimes use as a workaround. To unit test, probably you can mock the `Request` and inject it to the method. – A-Sharabiani Jun 30 '17 at 16:30