Cambiar la ubicación de las vistas en ASP.NET MVC

¡Menuda vista! El framework ASP.NET MVC utiliza en varios puntos la filosofía que suele denominarse convención sobre configuración, ahorrando tiempo y esfuerzos al desarrollador que decida asumir una serie de normas preestablecidas, a la vez que facilita la homogeneidad y coherencia en las soluciones que las implementen.

Por ejemplo, todos sabemos que las vistas de una aplicación ASP.NET MVC deben encontrarse en la carpeta Views. Si se trata de una vista compartida como puede ser una página maestra, debemos colocarlas en la subcarpeta Shared; en caso contrario, deberá existir una subcarpeta con el nombre del controlador en la que se encontrarán las vistas para las distintas acciones, nombradas igual que éstas. Esto, a la postre, evita que sea el desarrollador el que tenga que decidir dónde ubicar dichos archivos y configurar el sistema para que sea capaz de localizarlos cuando  sean necesarios: simplemente deberá seguir la convención preestablecida.

Pero, ¿qué ocurre cuando estas convenciones no encajan con nuestras necesidades o preferencias?

En este post voy a describir cómo es posible modificar la ubicación de las vistas en una aplicación ASP.NET MVC 1.0, saltándonos las convenciones establecidas por el framework. Pero sobre todo, ojo:

Saltarse las convenciones = malo

Hazlo sólo cuando realmente esté justificado… o por diversión, como es el caso 😉

1. Cómo localiza ASP.NET MVC las vistas

Cada vez que el framework necesita localizar una vista, por ejemplo después de ejecutar una acción cuyo resultado indica que debe mostrarse al usuario una página,  recorre los directorios donde, por convención, se supone que debe estar. Esta información se encuentra definida a nivel de código en el constructor de la clase WebFormViewEngine, es decir, en el motor de vistas Webforms que se utiliza por defecto en ASP.NET MVC. Su definición es la siguiente (recordad que está disponible el código fuente del framework):

 

public class WebFormViewEngine : VirtualPathProviderViewEngine
{
[...]
public WebFormViewEngine()
{
base.MasterLocationFormats = new string[]
{
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};

base.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
[...]
}

Como se puede observar, existen tres propiedades en el motor del vistas, heredadas de su antecesor, que indican los directorios donde se encuentran los archivos con la definición del interfaz:

  • MasterLocationFormats es un array con las rutas en las que se buscarán sucesivamente las páginas maestras. En el código puede verse que por defecto el sistema intentará localizar las masters en ~/Views y ~/Views/Shared; los parámetros {0} y {1} corresponden al nombre de la master y del controlador que están siendo buscados, respectivamente.

    Es importante tener en cuenta que el valor de esta propiedad sólo se aplica cuando se establece desde el código la página maestra con la que debe ser mostrada una vista.

  • ViewLocationFormats contiene las rutas donde se intentarán localizar las vistas. En la implementación por defecto, puede verse cómo el framework intenta localizar primero en ~/Views/{1} (recordad que este parámetro se sustituirá por el nombre del controlador) y luego en ~/Views/Shared una página .aspx o .ascx con el nombre de la vista (parámetro {0}). 
  • Por último, PartialViewLocationFormats indica las carpetas donde se deberán buscar las vistas parciales. Por defecto, se buscarán en los mismos directorios que las vistas normales.

2. Lo que pretendemos

Nueva estructura de carpetas para las vistasUna vez visto en qué se basa el framework para localizar las vistas, vamos al ataque.  Nuestra intención es saltarnos las convenciones creando una nueva estructura de carpetas para las mismas, como la que puede observarse en la imagen lateral. Existirá un directorio raíz, llamado “Interfaz”, en el que podemos encontrar dos subdirectorios: “Vistas” y “Masters”. En ellos almacenaremos, respectivamente, las vistas y las páginas maestras de nuestra aplicación.

La estructura de carpetas del proyecto será, por tanto, diferente a la propuesta por las plantillas de Visual Studio. Además, no tiene demasiado sentido, desde el punto de vista práctico, pero nos valdrá como ejemplo.

En los siguientes apartados vamos a ver dos formas distintas de conseguirlo.

3. Cómo lograrlo

La forma más sencilla de conseguir reemplazar las rutas de búsqueda de las vistas es, sin duda, alterar el contenido de las propiedades del motor descritas en el primer punto (MasterLocationFormats, ViewLocationFormats y PartialViewLocationFormats).

Esto puede conseguirse introduciendo el siguiente código en la inicialización de la aplicación, en el archivo global.asax:

public class MvcApplication : System.Web.HttpApplication
{
[...]
protected void Application_Start()
{
ReplacePaths();
// Reemplaza las rutas en el View Engine actual
RegisterRoutes(RouteTable.Routes);
}

private void ReplacePaths()
{
WebFormViewEngine eng
= ViewEngines.Engines[0] as WebFormViewEngine;
eng.MasterLocationFormats
=
new string[] {
"~/Interfaz/Masters/{0}.master"
};
eng.ViewLocationFormats
=
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx"
};
eng.PartialViewLocationFormats
= eng.ViewLocationFormats;
}
[...]
}

Como se observa en el código anterior, en el método ReplacePaths() en primer lugar se obtiene una referencia al motor de vistas por defecto, que sabemos que es del tipo WebFormViewEngine. Después, modificamos las propiedades para introducir las rutas que nos interesan. Tan simple como eso.

Es obvio que de esta forma estamos realizando una actualización destructiva de las propiedades. Si quisiéramos mantener las localizaciones por defecto del WebFormViewEngine pero añadir nuevas ubicaciones de búsqueda deberíamos copiar el contenido original del array, añadirle las rutas adicionales, y establecerlo en las propiedades.

Veamos otra forma de conseguir lo mismo.

4. Cómo lograrlo, toma 2

ASP.NET MVC framework ha sido diseñado desde sus orígenes con la extensibilidad en mente. Prácticamente cualquiera de sus comportamientos puede ser personalizado sustituyendo componentes como si se trataran de piezas de un mecano.

Una de las posibilidades de extensión incluidas en la plataforma es la sustitución del motor de vistas que utiliza para componer los interfaces de usuario. Esto, llevado al extremo, permite la utilización de motores distintos al proporcionado por defecto (WebForms) y utilizar alternativas como NHaml, Spark, Brail, o NVelocity, entre otros.

Pero nosotros no iremos tan lejos. Para cumplir con nuestros objetivos simplemente necesitamos crear y registrar en el sistema un nuevo motor de vistas que herede de WebFormViewEngine y reemplace la inicialización de las propiedades que definen la localización de las vistas.

El nuevo motor, al que llamaremos AcmeViewEngine, será algo como lo siguiente:

 public class AcmeViewEngine : WebFormViewEngine
{
public AcmeViewEngine()
{
MasterLocationFormats
=
new string[] {
"~/Interfaz/Masters/{0}.master"
};

ViewLocationFormats
=
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx",
};

PartialViewLocationFormats
= base.ViewLocationFormats;
}
}

Y ahora sólo nos faltaría registrar el motor de vistas en ASP.NET MVC framework, tarea que podemos realizar en el global.asax, así:

protected void Application_Start()
{
ReplaceViewEngine();
// Registra el nuevo View Engine
RegisterRoutes(RouteTable.Routes);
}

private void ReplaceViewEngine()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(
new AcmeViewEngine());
}

Observad que antes de añadir el nuevo motor de vistas a la colección estamos vaciándola (llamando a su método Clear()), lo cual asegurará que nuestro AcmeViewEngine será el utilizado por la plataforma.

5. Conclusión

No podremos saltar directamente a la vista desde el controladorComo ya he comentado al principio, saltarnos las convenciones no es buena idea. Por ejemplo, un daño colateral de romper con la convención propuesta por el framework MVC para la localización de las vistas es que Visual Studio no será capaz de proporcionarnos la función que nos  permite abrir la vista asociada con una acción seleccionando la opción “Go to View” del menú contextual, o intentará insistentemente crear las nuevas vistas (con la opción “Add View”) en las carpetas en las que deberían encontrarse según la convención.

Pero en cualquier caso, es un ejercicio interesante para conocer las interioridades de este framework y comprobar de primera mano la flexibilidad con la que ha sido implementado.

Si lo deseas, puedes descargar el código fuente de los ejemplos (requiere Visual Studio 2008 o Web Developer Express con SP1 y ASP.NET MVC 1.0):

Publicado en: www.variablenotfound.com

2 comentarios en “Cambiar la ubicación de las vistas en ASP.NET MVC”

  1. Hola, Luis.

    Has pasado rozando :-DDDDD

    No, todavía no me he hecho con ningún libro de asp.net mvc, estoy esperando al verano… Eso sí, sigo muy de cerca lo que publican las figurillas del framework en sus blogs, y experimento todo lo que puedo 🙂

    Saludos y muchas gracias.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *