Parámetros de un action y Query String en ASP.NET MVC 5

 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).


Acciones y tabla de rutas

Las acciones se distinguen por su nombre, sin tener en cuenta sus parámetros. Eso significa que un controlador no puede definir dos veces la misma acción con parámetros distintos, salvo que se responda a verbos http distintos (p.ej. una acción responda a GET y otra a POST).

La tabla de rutas mapea una URL a una única acción de un controlador.

La tabla de rutas tiene una o más rutas, que se evalúan en orden. Cuando una URL hace matching con una ruta, se termina la evaluación.

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? 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.

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!)
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 ?

La cuestión no es baladí: a nuestro controlador le estamos pasando:

Un parámetro de ruta con valor 10 y que (gracias a la tabla de rutas) se llama id
Un parámetro de querystring llamado id con valor 20.
Ambos parámetros (de ruta y querystring) se mapean a parámetros de la acción del controlador. ¿Así el  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):


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();
}

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.


Referencias

Comentarios

Entradas populares de este blog

¿Qué es la JCAHO Joint Commission on Accreditation of Healthcare Organizations?

PARSEO DEL CODIGO PDF417 DEL DNI ARGENTINO

¿Como instalar El Cliente de SOPHOS VPN ?