Uso de POST en ASP.NET MVC

  • Por
  • .NET
Sin duda alguna de todos los mecanismos que hay para mandar información desde una vista hacia un controlador, el uso de POST es el más común.
Dicho rápido y mal: el uso de POST equivale al uso de formularios HTML. Digo lo de mal porque ni los formularios son la única manera de enviar datos vía POST, ni todos los formularios se envían por POST, pero no nos vamos a centrar en estos detalles, ya que la mayoría de formularios hoy en día se envían via POST. La principal diferencia entre enviar datos via POST o via GET (es decir usando la URL, ya sea a través de querystring que vimos en el artículo anterior, o en valores de ruta) es que con POST los datos circulan en el cuerpo de la petición y no son visibles en la URL.

Enviar datos a través de un formulario

Hablar de datos enviados a través de un formulario a un controlador implica presentar una característica de ASP.NET MVC que se va a convertir en un gran aliado: el model binding. Por ese nombre nos referimos a la capacidad de ASP.NET MVC de construir objetos a partir de los datos que le lleguen en la petición. Para ser honestos el model binding funciona con independencia de si los datos vienen de un formulario, en la querystring o por valores de ruta, pero va a ser cuando se mandan datos a través de formularios que adquiere su máxima expresión.

Pero vayamos por partes... Empecemos por una vista sencilla, que se mostrará en respuesta a la acción Nuevo del controlador Usuarios:

<h2>Nuevo usuario</h2>

<form method="POST">
<label for="login">login:</label>
<input type="text" name="login" />
<br />
<label for="password">clave:</label>
<input type="text" name="password" />
<br />
<input type="submit" value="enviar"/>
</form>

Esta vista crea un formulario que será enviado por POST. El formulario contiene:

  • Dos etiquetas con texto (
  • Dos campos de texto (<input type=”text”>), uno que se llama “login” y otro llamado “password” (el valor de sus atributos name).
  • Un botón para enviar el formulario
Si os fijáis no hemos indicado a que URL debe enviarse el formulario. Eso se hace a través del atributo action de la etiqueta <form>. Si no aparece, el formulario se mandará de vuelta a la misma URL a la que estamos.

En el controlador (UsuariosController) metemos simplemente una acción que muestre la vista:

public ActionResult Nuevo()
{
return View();
}

Bien, si ahora con el navegador nos dirigimos a /Usuarios/Nuevo nos aparecerá la vista con el formulario. Podemos rellenar datos y pulsar enviar. Al pulsar enviar simplemente se nos mostrará la vista (vacía) de nuevo. Esto ocurre porque al pulsar el botón de enviar datos se envía el formulario a la misma URL (/Usuarios/Nuevo) de la que venimos. Por lo tanto se invoca de nuevo la acción Nuevo del controlador Usuarios que lo único que hace es mostrar la vista otra vez.

Ahora bien, lo que nosotros queremos es que cuando se envíe el formulario vía POST podamos obtener los datos y hacer algo con ellos. En definitiva queremos hacer otra cosa que no sea mostrar la vista. La verdad es que suena un poco como si quisiéramos otra acción distinta. Pero, si recordáis en los inicios de este manual, dijimos que una acción sólo podía estar implementada por un solo método en el controlador. Bueno, la verdad es que... mentimos un poquillo. La realidad es que una acción puede estar implementada por un solo método por cada verbo HTTP. Si no sabes lo que son los verbos HTTP no te preocupes mucho: es la manera técnica de referirnos a GET y POST. Así GET es un verbo HTTP y POST es otro. Hay más, como HEAD, PUT y DELETE pero dado que no hay soporte en HTML para estos verbos no nos vamos a preocupar de ellos (eso no significa que ASP.NET MVC no los soporte, sólo que no vamos a verlo aquí). Para nosotros sólo van a existir GET y POST. Y volviendo a lo que decíamos, eso significa que para la misma acción (por lo tanto, la misma URL) puedo tener dos métodos en el controlador: uno que se invoque a través de GET y otro que se invoque a través de POST. Así pues podemos añadir el siguiente método a nuestro controlador:

[HttpPost]
public ActionResult Nuevo(string login, string password)
{
// Codigo...
}

Observad como el método está decorado con [HttpPost]. Al aplicar este atributo al método le estamos indicando a ASP.NET MVC que cuando se deba invocar la acción Nuevo del controlador Usuarios use este método si la invocación es vía POST. Si la invocación es vía GET (p.ej. tecleando la URL en la barra de direcciones del navegador) se invocará el método Nuevo que ya teníamos. Fijaos pues que tenemos una manera simple y elegante de separar nuestro código en función del verbo HTTP que se use.

Fijémonos ahora en los parámetros del método Nuevo: dos parámetros cuyo nombre es el mismo que los nombres de los campos del formulario. Sólo con esto le basta a ASP.NET MVC para enlazar los valores del formulario con los parámetros de la acción del controlador. Ahora bien, imagina que nuestro formulario en lugar de tener dos campos, tiene veinte... ¿Te imaginas tener que poner veinte parámetros en la acción del controlador? Pues para evitar esto existe precisamente el model binding.

Model Binding

Llamamos model binding a la capacidad de ASP.NET MVC de crear objetos (de clases nuestras) a partir de los parámetros que vengan en la petición. En nuestro caso a partir de los campos del formulario que enviamos.

Así podríamos tener una clase Usuario tal y como sigue:

public class Usuario
{
public string login { get; set; }
public string password { get; set; }
}

Y sustituir los dos parámetros que teníamos en la acción Nuevo por un solo parámetro de tipo Usuario:

[HttpPost]
public ActionResult Nuevo(Usuario usuario)
{
// Codigo...
}

Y gracias al poder del model binding recibiremos un objeto usuario rellenado a partir de los datos del formulario. La única condición es que las propiedades del objeto se llamen igual que los campos del formulario.

Llegados a este punto podríamos validar los datos y si hay algún error, los podemos mandar de vuelta a la vista (junto con un mensaje explicativo del error):

[HttpPost]
public ActionResult Nuevo(Usuario usuario)
{
if (string.IsNullOrEmpty(usuario.login) ||
string.IsNullOrEmpty(usuario.password))
{
ViewBag.Error = "Login o password no pueden estar vacíos";
return View(usuario);
}
// Damos de alta el usuario en la BBDD y redireccionamos
return RedirectToAction("Home", "Index");
}

Si el campo de login o password se deja vacio, entonces añadimos un campo llamado Error en el ViewBag y devolvemos la vista, pasándole como datos el objeto usuario que hemos recibido. Si por otro lado la validación es correcta redirigimos el usuario a la acción Index del controlador Home.

Bien, ahora vayamos a por la vista: la idea es que si la vista recibe un objeto de tipo Usuario rellene los campos de texto con el valor de los campos de dicho usuario. De este modo al mandarle de vuelta el objeto desde el controlador, el usuario verá exactamente lo mismo que él ha enviado y sólo deberá corregir los errores que se le indiquen. El nuevo código de la vista es:

@model MvcDatosPost.Models.Usuario
<h2>Nuevo usuario</h2>

@if (!string.IsNullOrEmpty(ViewBag.Error))
{
<div class="error">@ViewBag.Error</div>
}
<form method="POST">
<label for="login" >login:</label>
<input type="text" name="login" value="@(Model!=null ? Model.login : string.Empty)"/>
<br />
<label for="password">clave:</label>
<input type="text" name="password" />
<br />
<input type="submit" value="enviar"/>
</form>

Lo que hemos añadido respecto a la vista original es que muestre un <div> con el error en caso de que este exista y establecer el valor del atributo value del campo login al valor del elemento recibido si existe. El valor del campo password no lo enlazamos porque, por norma general, cuando hay un error se obliga siempre a volver entrar el password.

¡Y listos! Si el usuario envía un formulario con el campo login o password vacíos, se le mostrará de nuevo los datos que había entrada (salvo el password) junto con el mensaje de error. ¿Sencillo, verdad? Pues bien, esa manera en que hemos hecho la validación, y la forma en como hemos modificado la vista para mostrar los datos devueltos por el controlador, aunque funcionan no son las ideales. En los próximos artículos veremos dos maneras más elegantes de hacerlo: por un lado la validación mediante Data Annotations y por otro el uso de los helpers en las vistas...

¡Un saludo!

Autor

Eduard Tomàs

Apasionado de la informática, los videojuegos, rol y... la cerveza. Key Consultant en Pasiona y MVP en #aspnet

Compartir

Comentarios

JOAN

08/3/2012
ESTUPENDO!
POR FIN ALGUIEN QUE EXPLICA LAS COSAS CLARAS!!
GRACIAS!

futar75

22/5/2012
Consulta Model Binding
Ante todo, muy clara toda la serie de articulos de ASP.NET.
Una consulta. En la parte de model Binding, cuando se crea la clase Usuario, supongo que se hace dentro de la carpeta Models del proyecto, no? Por eso en la vista se agrega "@model ASPMvc3Application.Models.Usuario".
Si es correcto, por que me sale este error?

Error 1 'ASPMvc3Application.Controllers.Usuario' does not contain a definition for 'login' and no extension method 'login' accepting a first argument of type 'ASPMvc3Application.Controllers.Usuario' could be found (are you missing a using directive or an assembly reference?)

desde ya, gracias

Rushmata

23/6/2012
Duda sobre mostrar el formulario
Hola, estoy intentando que se muestre el formulario que indicas arriba, pero cuando le doy a ejecutar, aun habiendo puesto el atributo action con su URL me salta el error de "No se encuentra el recurso", he creado un controlador llamado UsuariosController y una vista llamada Usuarios, he creado la tabla de rutas, todo está correcto o eso pienso yo, ¿Por que no se ve el formulario?, esta sería la etiqueta form:
<form action="@Url.Action("Nuevo","usuarios")" method="POST">
Espero que me puedan ayudar, la verdad que los tutoriales están de lujo pero me atranqué aquí. Gracias de antemano.

Luis

06/11/2012
Mal explicado
Esta bien redactado y todo, pero no estas enseñando paso a paso como se tiene que utilizar post, hablas como si tu ya tuvieras el proyecto creado etc.. te falta explicar detalle a detalle y paso a paso como mandar y procesar los datos ya que esto no lo estas haciendo

Daniel

08/11/2012
Mal explicado
La forma que explicas las cosas podrias ser un poco mas explicito, donde colocar cada una de ellas, pues cuando hablas ya das por entendido muchos intens que talvez vos sabes, pero aqui estamos intentando aprender. Tenes que explicar paso a paso como se hace.

Juan Francisco Mosquera Hurtado

28/1/2013
Amigo te falto esto
amigo gracias por este articulo siguiendolo llegue a un error es que en mvc 4 talves toca que agregar un nuevo metodo con httpget decorado sino me generaba error.
coloco esto por si alguien pasa por lo mismo . mil gracias desde colombia
[HttpGet]
public ActionResult nuevo()
{
return View();
}

Ruy

17/7/2013
Excelente
Me pareció muy buena explicación, felicidades, me puedes decir en donde encuentro tu siguiente articulo con helper y Data Annotations

juan

06/6/2014
Codigo
Hola a todos, esta bien explicado, pero como seria con la sintaxis de visual basic esto en c#

David Morales Valencia

09/7/2014
Agradecimiento
Qué haríamos sin personas como tú... Explicas muy bien, con un orden secuencial, de lo simple a lo complejo y de forma magistral!

ferchas525

07/6/2015
error
[HttpGet]
public ActionResult Nuevo()
{
return View();
}
agregar esa linea antes del [HttpPost] en el controlador para que no genere error, en mvc4

djKaya

12/8/2015
Muchas gracias
Muchas gracias por el aporte, me ayudo a entender como enviar parámetros a un controlador si tener la vista fuertemente tipada con un modelo, y entendí como funciona el enlace con un modelo y hacerlo de manera manual sin usar scaffolding. De hecho era exactamente lo que necesitaba ya que estaba haciendo un formulario de contacto el cual no iba a guardar ninguna parte.
De nuevo muchas gracias.
PD: al amigo que criticaba y decía que estaba mal explicado por que no tenia idea de donde salía el POST, lo invito a que regrese al nivel mas básico de programación web, ya que a este nivel deberías comprenderlo.

Alejandra

30/9/2016
Preguta sobre los parametros enviados en el POST
Soy relativamente nueva en esto y hago un pequeño formulario en el cual trato de enviar parametros por medio de un post a otra pagina, el problema esque parece que me cambia el nombre de las parametros por ejemplo
si envio UrlOK:"URL" en el navegador veo que va de esta forma
ctl00$MainContent$UrlOK:"URL" , no se si sea parte de alguna configuración dentro del proyecto asp.net.

te agradeceria mucho si me pudieras ayudar.
Saludos

Javier

25/1/2017
Mi punto de vista
Muy buena información, muchas gracias por compartir esa información.