> Manuales > Manual del framework ASP.NET MVC

En toda aplicación web hay dos mecanismos básicos que se usan para pasar información de cliente al servidor: usando la URL (lo que se conoce como querystring) o usando un formulario (lo que se conoce como POST).

ASP.NET MVC tiene un soporte directo para usar la querystring: los parámetros que se pongan a la URL serán enviados como parámetros de la acción correspondiente.

Es decir, si yo tengo la siguiente url: http://host/home/Index?p1=10&p2=no (y suponiendo la tabla de rutas por defecto), se invocará la acción Index de HomeController con dos parámetros p1 (con valor 10) y p2 con valor “no”. Así en el controlador podríamos tener definida la acción de la siguiente forma:

public ActionResult Index(int p1, string p2)
{
// Codigo...
}

Los nombres de los parámetros deben coincidir con los nombres de los parámetros de la querystring. Bien, fijaos que dado que hemos declarado el parámetro p1 como int sólo podemos pasar valores enteros, mientras que en el parámetro p2, podemos pasar cualquier cadena. Si pasamos una cadena en el parámetro p1, p.ej. la url http://host/home/index?p1=texto&p2=otrotexto el error que recibimos es el siguiente:

Lo que ha ocurrido es que ASP.NET MVC ha intentado convertir el valor de p1 (“texto”) a un entero. Al no poder hacerlo, internamente asigna null al valor del parámetro p1, pero luego cuando debe invocar el método Index y pasarle un int se encuentra que int no acepta valores null. De aquí el error que recibimos.

Como podríamos evitar esto? Bueno… una manera fácil y sencilla es usar int? (es decir Nullable) en lugar de int para declarar el tipo de p1:

public ActionResult Index(int? p1, string p2)
{
// Codigo
}

Ahora si invocamos la url y el valor de p1 no es numérico, nos llegará null, mientras que si el valor de p1 es numérico recibiremos su valor.

La regla es realmente muy simple: Si quieres que un parámetro de querystring sea opcional debes usar un tipo por referencia (es decir una clase como string o Nullable). Si usas un tipo por valor (como int o double) el parámetro no puede ser opcional y además el valor que se entre en la URL debe ser convertible de cadena al tipo concreto que pongas en el controlador.

Generar URLs con querystrings desde las vistas

Uno de los errores más frecuentes en ASP.NET MVC es tener código en las vistas que sea parecido a este:

Pulsa <a href="/Home/View?pid=10">aquí</a> para ver los detalles

Este código tiene dos errores fundamentales.

  1. Está ignorando al tabla de rutas. Está generando las URLs usando la convención de Controlador/Acción pero esta convención es sólo válida si se usa la tabla de rutas estándar. En proyectos de tamaño medio es normal tener una tabla de rutas que sea totalmente personalizada (¡esa es una de las gracias de ASP.NET MVC!)
  2. Está pasando los parámetros siempre en querystring, ignorando los valores de ruta
Para solucionar el primer punto, lo que debemos hacer es usar el Helper Url. Los Helpers son clases que nos proporcionan mecanismos de ayuda (de serie vienen algunos que iremos viendo y se pueden crear de propios), para ayudarnos con tareas repetitivas. Para generar una URL que respete la tabla de rutas debemos usar el método Url.Action. Su firma básica es Url.Action (“acción”, “controlador”). Así el código anterior lo podemos reescribir de la forma:

Pulsa <a href="@Url.Action("View","Home")?pid=10">aquí</a> para ver los detalles

Si ejecutamos eso y miramos el código HTML veremos que es exactamente lo que habíamos tecleado antes (debido a que usamos la tabla de rutas estándar). Pero si añado una entrada a la tabla de rutas, dejándola así:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Ver",
"VerProducto",
new {controller = "Home", action = "View"});
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

Ahora la URL que se me genera usando Url.Action es la siguiente:

Pulsa <a href="/VerProducto?pid=10">aquí</a> para ver los detalles

Así pues, siempre que necesitéis obtener una URL desde una vista, usad Url.Action… recordad que el formato real de las URLs depende de la tabla de rutas. Asumir que siempre estarán en la forma /controlador/acción es una muy mala práctica (y como decía antes, un error común al principio).

Parámetros querystring y route values

Vamos a ver ahora que significa el segundo de los errores que cometíamos (pasar los valores siempre en querystring ignorando los valores de ruta). Para evitar entrar demasiado en tecnicismos diré que ASP.NET MVC mezcla todos los parámetros que le llegan antes de pasarlos a los controladores. A un controlador le pueden llegar parámetros por tres formas básicas (vale, eso no es del todo cierto, hay más formas pero vamos a ignorarlas de momento): como parámetros de ruta, como querystring y como POST (de esos hablaremos en un artículo posterior).

Los parámetros de ruta son los que colocamos en la URL separados por la barra, es decir: http://host/controlador/accion/10. En este caso 10 es el valor de un parámetro de ruta. ¿De cuál? Pues el nombre debe estar explicitado en la tabla de rutas. Vamos a suponer que usamos la tabla de rutas estándar. En este caso el nombre de dicho parámetro es id. Ahora yo os pregunto… que ocurriría si usamos la URL http://host/controlador/accion/10?id=20 ?

Fijaos que la cuestión no es baladí: a nuestro controlador le estamos pasando:

Ambos parámetros (de ruta y querystring) se mapean a parámetros de la acción del controlador. ¿Así pues… que valor recibiremos en el parámetro id de nuestra acción? ¿O bien ASP.NET MVC generará un error?

Pues lo que recibiremos en nuestra acción será el valor del parámetro de ruta (es decir 10, en lugar de 20):

Fijaos en la URL entrada (/Home/Index/10?id=20) y como el valor del parámetro id es “10” y no “20”.

Eso es debido a que comentábamos antes: ASP.NET MVC mezcla todos los parámetros que le llegan antes de enlazarlos con los controladores y lo hace según una cierta prioridad. Y los parámetros de ruta (cuyo nombre se define en la tabla de rutas) tienen más prioridad. Eso debería tenerlo en cuenta cuando genero URLs, es decir, en este caso debería generar URLs usando la convención /controlador/acción/valor_id antes que /controlador/acción?id=valor_id

Al final eso nos implica que no deberíamos nunca generar las URLs con parámetros querystring añadidos a mano. Por suerte para nosotros el helper Url.Action que hemos visto antes viene de nuevo a nuestra ayuda. En una de sus sobrecargas Url.Action acepta un objeto anónimo cuyas propiedades son los valores a mandar al controlador. ¡Url.Action es lo suficientemente inteligente como para usar valores de ruta si están definidos y querystring en caso de que no!

Así pues si tenemos la tabla de rutas estándar y tenemos las siguientes llamadas a Url.Action:

Url 1: @Url.Action("Index", "Home", new { id = 20 })

Url 2: @Url.Action("Index", "Home", new { id = 20, otro_id=30})

Url 3: @Url.Action("Index", "Home", new {otro_id=30})

La respuesta generada por esa vista es la siguiente:

Url 1: /Home/Index/20
Url 2: /Home/Index/20?otro_id=30
Url 3: /?otro_id=30

¿No es una maravilla? Url.Action sabe que id es un parámetro de ruta y nos lo coloca como tal. Y sabe que otro_id no lo es y nos lo coloca usando query_string. En este caso la acción en el controlador la tenemos definida:

public ActionResult Index(string id, string otro_id)
{
return View();
}

Fijaos como desde el controlador recibimos de igual manera parámetros de ruta que parámetros que vengan por querystring… :)

En el siguiente artículo del tutorial vamos a ver como mandar datos de formularios a las vistas, es decir cómo usar POST.

Eduard Tomàs

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

Manual