> Manuales > Manual del framework ASP.NET MVC

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:

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!

Eduard Tomàs

Apasionado de la informática, los videojuegos, rol y... la cerveza. Key Consulta...

Manual