Utilizar UrlMappings para hacer parecer que ciertos Controllers se encuentran agrupados en un directorio (Grails)
Un requisito de casi todo sitio web actual es que tenga Friendly Urls. Esto es que en lugar de tener una url como http://mi-pagina.com/verproducto.php?id=4 los parámetros, e incluso la página, se codifiquen en la misma url, quedando algo como http://mi-pagina.com/producto/video-grabadora
Este tipo de url tiene varias ventajas, entre ellas que es más linda de ver y recordar por el usuario, y que gozan de mejor posicionamiento orgánico en los motores de búsqueda, por lo que cualquier página interesada en un poco de SEO lo necesita.
Cada tecnología tiene su propia forma de crear este tipor de Url, en Grails, particularmente, se usa un archivo de configuración llamado UrlMappings el cual asocia la forma de las urls, con controllers, actions y parámetros específicos.
Este post se basa en un proyecto en el cual estoy trabajando, que por culpa de una desafortunada decisión de diseño al inicio del desarrollo, terminamos con un controller llamado entrada y otro llamado entradaAdmin. Uno pensado para el frontend de la aplcación y otro utilizado para la administración. Y así con casi todas las clases de dominio. (para mi es una limitación que grails no soporte correctamente el concepto de Package en los Controllers)
Mi objetivo era que desde el punto de vista del usuario todos los *Admin estuvieran dentro de una misma carpeta y a su vez que esta tenga una página de inicio. O sea yo quería que las siguientes urls funcionen y sean redirigidas a..
http://mi-pagina.com/admin/entrada => entradaAdminController
http://mi-pagina.com/admin/usuario => usuarioAdminController
http://mi-pagina.com/admin => View de vista de administracion (sin controller asociado, solo una pagina casi estática)
http://mi-pagina.com/usuario => usuarioController
Pero que http://mi-pagina.com/entradaAdmin no funcionara.
Entonces, para hacer esto investigué un poco el funcionamiento de UrlMappings y expongo aquí el como llegué al resultado deseado
Lo primero importante a notar es como está configurado por default el mapping en Grails. Como aquellos que han usado un tiempo Grails saben, la forma de invocar un action es http://mi-app.com/controller/action/id-param y esto es así gracias a que el archivo UrlMappings default trae la siguiente configuración:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Lo primero que hay que hacer es que instruir al mapping por “default” que ya no debe procesar aquellos controllers que terminen en Admin. Esto se logra agregandole constraints al mapeo por default, quedando de la siguiente manera
"/$controller/$action?/$id?"{
constraints {
// Las paginas del administrador, no las hacemos accesibles desde aqui..
controller(validator: { !it.endsWith("Admin") })
}
}
Ahora faltaba crear un nuevo mapeo para aquellos controllers que terminaran en Admin. Esto resultó ser un poquito más rebuscado de lo que espraba. Mi primer intento fue:
"/admin/$controller/$action?/$id?"{
constraints {
}
}
Lamentablemente esto no funcionó. Al menos no funcionó como yo esperaba.
Resulta que cuando se utiliza createLink, resource y otro tipo de tags propios de grails para generar los hipervínculos en la página, grails se fija en UrlMapping para determinar cual es la forma del link que le corresonde a un determinado action/controller.
Esto lo hace únicamente en base a los parámetros variables del mapping, y en este caso los dos mappings tenían como dato variable $controller, $action e $id por lo que cualquiera podría utilizarse, entonces elije básicamente el primero que encuentra. Resultado, ningun link comenzaba con /admin/ porque jamas elegía ese mapeo, pero como el mapping default evitaba que se pueda utilizar ese mapping con los controllers terminados en Admin, los links generados no andaban.
Busque varias formas, pero no encontré como solucionarlo, por lo que me resigné a hacerlo a la fuerza bruta. Y debí crear entonces un mapping para cada uno de los controllers Admin. Quedando el archivo UrlMappings de la siguiente manera:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// Las paginas del administrador, no las hacemos accesibles desde aqui..
controller(validator: { !it.endsWith("Admin") })
}
}
"/"(view:"/index")
"500"(view:'/error')
"/admin/entrada/$action?/$id?" (controller: "entradaAdmin")
"/admin/usuario/$action?/$id?" (controller: "usuarioAdmin")
}
}
Ahora cada vez que agrego un nuevo *Admin debo recordar agregarlo también al UrlMappings, pero al menos estan funcionando correctamente mis links.
Discussion Area - Leave a Comment