¿Cómo recuperar valores de formulario de HTTPPOST, diccionario o?

112

Tengo un controlador MVC que tiene este método de acción:

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

El formulario es un formulario no trivial con un cuadro de texto simple.

Pregunta

¿Cómo accedo a los valores de los parámetros?

No estoy publicando desde una Vista, la publicación llega externamente. Supongo que hay una colección de pares clave / valor a los que tengo acceso.

Lo intenté Request.Params.Get("simpleTextBox");pero me devuelve el error "Lo sentimos, se produjo un error al procesar su solicitud".

Ricardo
fuente

Respuestas:

155

Puede hacer que la acción de su controlador tome un objeto que refleje los nombres de entrada del formulario y la carpeta de modelos predeterminada creará automáticamente este objeto para usted:

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

    ... return something ...
}

Otra forma (obviamente más fea) es:

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

    ... return something ...
}
Darin Dimitrov
fuente
5
Solo me gustaría señalar que pierde la copia de seguridad del compilador en la opción 2. Si el modelo cambia, el compilador no detectará el cambio en los controladores relacionados. Hay buenos casos para la opción 2, pero no recomendaría un uso generalizado.
Serguei Fedorov
1
A veces necesitas cosas feas, es bueno tener una opción cuando ya sabes cuáles son las mejores prácticas
Oscar Ortiz
Como alguien que todavía está aprendiendo dot net, ¿por qué la segunda forma es más fea?
Goose
3
@Goose, porque son cuerdas mágicas. No obtienes ninguna seguridad en el tiempo de compilación. Su código fallará en el tiempo de ejecución si comete un error tipográfico en el nombre de la variable, mientras que si está usando una escritura fuerte, el compilador será su amigo.
Darin Dimitrov
@DarinDimitrov tiene sentido. Diferente al mundo del que vengo. Característica muy bonita.
Goose
104

Simplemente, puede usar FormCollectioncomo:

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

También puede usar una clase, que está asignada con valores de formulario, y el motor asp.net mvc la llena automáticamente:

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

[HttpPost]
public ActionResult SubmitAction(MyForm form)
{      
  string var1 = form1.Var1;
}
Adeel
fuente
Me gustó la solución de la clase, es fácil y simple
Basheer AL-MOMANI
36

Las respuestas son muy buenas, pero hay otra forma en la última versión de MVC y .NET que realmente me gusta usar, en lugar de las claves FormCollection y Request de la "vieja escuela".


Considere un fragmento de HTML contenido en una etiqueta de formulario que hace un AJAX o un 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?" />

Su controlador analizará los datos del formulario e intentará entregárselos como parámetros del tipo definido. Incluí una casilla de verificación porque es complicada. Devuelve el texto "on" si está marcado y nulo si no está marcado. Sin embargo, el requisito es que estas variables definidas DEBEN existir (a menos que sean anulables (recuerde que stringes anulable)) de lo contrario, el AJAX o POST back fallará.

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

También puede publicar un modelo sin usar ayudantes de afeitado. Me he encontrado con que esto es necesario algunas veces.

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

El marcado HTML simplemente será ...

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

y su controlador (Razor Engine) interceptará la variable de formulario "variableName" (el nombre es el que desee pero lo mantendrá consistente) e intentará construirlo y convertirlo en MyModel.

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

Cuando un controlador espera un modelo (en este caso, HomeModel), no tiene que definir TODOS los campos, ya que el analizador simplemente los dejará por defecto, generalmente NULL. Lo bueno es que puede mezclar y combinar varios modelos en el Mark-up y el análisis posterior se completará tanto como sea posible. No es necesario definir un modelo en la página ni utilizar ayudantes.

SUGERENCIA: El nombre del parámetro en el controlador es el nombre definido en el marcado HTML "name =" no el nombre del modelo, sino el nombre de la variable esperada en el!


El uso List<>es un poco más complejo en su marcado.

<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">

El índice en la Lista <> DEBE ser siempre secuencial y basado en cero. 0,1,2,3.

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

Se utiliza IEnumerable<>para la devolución de índices no secuenciales y basados ​​en cero. Necesitamos agregar una entrada oculta adicional para ayudar a la carpeta.

<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">

Y el código solo necesita usar IEnumerable y llamar ToList()

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

Se recomienda utilizar un solo modelo o un modelo de vista (modelo que contiene otros modelos para crear un modelo de 'vista' complejo) por página. Mezclar y combinar como se propone podría considerarse una mala práctica, pero siempre que funcione y sea legible, no es MALA. Sin embargo, demuestra el poder y la flexibilidad del motor Razor.

Entonces, si necesita agregar algo arbitrario o anular otro valor de un asistente de Razor, o simplemente no tiene ganas de crear sus propios ayudantes, para un solo formulario que usa una combinación inusual de datos, puede usar rápidamente estos métodos para aceptar más datos.

Piotr Kula
fuente
El uso de la opción Índice es oscuro. ¡¿Quién en la tierra verde de Dios habría sabido usar eso o que incluso existía ?! Pero me alegro de haber encontrado esta publicación. Ahorrará una gran cantidad de tráfico de red.
Michael Silver
1
Esto funcionó para mí, pero solo después de cambiar <input type = "hidden" id = "myId"> a @ Html.Hidden ("myId")
radkan
@Piotr: corrija las incoherencias de referencia con MyModel y MyHomes. Causa confusión como está actualmente.
Spencer Sullivan
15

Si desea obtener los datos del formulario directamente de la solicitud Http, sin ningún enlace de modelo o FormCollectionpuede usar esto:

[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
fuente
2
Tenga en cuenta que esto puede ser una forma incorrecta (sin juego de palabras), pero a veces solo desea los valores del formulario y no puede cambiar claramente la firma de la función. Esta es la única solución aquí que se adapta a mi situación particular.
Ryan
¿Cómo realizar una prueba unitaria de este método con esas referencias estáticas? FormCollection sería mucho más deseable cuando se trata de pruebas.
Kees de Wit
@KeesdeWit si leyó el comentario anterior, esta no es la mejor manera, pero a veces se usa como solución alternativa. Para la prueba unitaria, probablemente puedas simular el Requeste inyectarlo al método.
A-Sharabiani