Explorando ASP.NET MVC 2.0… áreas

No hace mucho Jorge Dieguez comentaba la salida de ASP.NET MVC 2.0 Preview 1. He estado investigando un poco las novedades del framework, y hoy quiero hablaros de lo que se conoce como áreas.

Cuando hablamos de áreas no nos referimos a zonas de la pantalla (tipo webparts) sinó que las áreas en ASP.NET MVC permiten construir una aplicación en base a módulos. Actualmente en la preview 1, el sistema consiste en:

  • Definir la área principal: Un proyecto ASP.NET MVC completo, con sus vistas, sus controladores, sus hojas de estilo, …
  • Definir el resto de áreas (módulos). Cada área es un proyecto ASP.NET MVC que añade sus propias vistas y controladores. Cada área confía en el área principal para el resto de temas (p.ej. scripts y css están en el área principal). De igual modo toda la inicialización (global.asax) se realiza únicamente en el área principal: el resto de áreas no tienen global.asax.

De esta manera se pretende poder gestionar mejor grandes aplicaciones: al estar formadas por áreas independientes, que a su vez son proyectos independientes de ASP.NET MVC, equipos distintos podrían realizar cada una de las áreas.

En el archivo global.asax, del área principal, cuando registramos las rutas, mapeamos determinadas URLs a cada uno de las áreas. Imaginad que tenemos una área que contiene toda la parte de administración del sitio, podríamos añadir a global.asax, una línea tal como:

routes.MapAreaRoute("Admin", "Admin_Default", 
    "admin/{controller}/{action}/{id}",
    new string[] { " Admin.Controllers" });

El primer parámetro es el nombre del área (luego veremos que el nombre tiene implicaciones), el segundo el nombre de la ruta (cada área puede tener tantas rutas como se desee), el tercer parámetro es la URL a enrutar y el cuarto parámetro es un array con todos los namespaces donde buscar los controladores.

De esta manera todos las URLs que empiecen por /Admin/ serán enrutadas a la área Admin. P.ej. la URL /Admin/Users/View/20 sería enrutada al controlador “UsersController”, llamando a su acción View con el Id de 20. El último parámetro (un array de strings) es importante puesto que contiene todos los namespaces de los posibles controladores de la área de administración. Dado que son proyectos distintos pueden tener controladores con el mismo nombre, así que el motor de ASP.NET MVC debe saber el namespace a utilizar… En fin, que simplemente le estamos diciendo al motor de ASP.NET MVC que use el controlador que sea que esté en el namespace Admin.Controllers.

P.ej. en mi caso estoy trabajando con 3 áreas (la principal y dos más) y tengo definida la siguiente tabla de enrutamiento:

routes.MapAreaRoute("Game", "Game_Default", 
"game/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Game.Controllers" }); routes.MapAreaRoute(
"Admin", "Admin_Default", "admin/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Admin.Controllers" }); routes.MapAreaRoute("Main",
"Main_Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Web.Controllers" });

Todas las URLs que empiecen por /Game/ se enrutarán al área que implementa la aplicación web en sí (efectivamente es un juego :p), mientras que todas las URLs que empiecen por /Admin/ se enrutarán al área que implementa la zona de administración. Finalmente el resto de URLs (que no empiecen ni por /Game/ ni por /Admin/ serán enrutadas al área principal. Fijaos que las 3 áreas definen un controlador “Home” pero no es problema, puesto que el namespace es distinto 🙂

Obviamente se puede generar un enlace desde una área a otra (sino maldita la gracia). Así de esta manera se genera un enlace a la acción About del controlador Home de la área actual:

<%= Html.ActionLink("Pulse aquí", "About", "Home") %>

En fin, como siempre… Para cambiar de área simplemente debemos pasar el nombre del área (debe usarse el mismo nombre con el cual se ha registrado el área en MapAreaRoute):

<%= Html.ActionLink("Pulse aquí", "About", "Home", 
new { area = "Game" } , null)%>

Esto genera un enlace a la acción About del controlador Home del área Game (en mi caso una URL tipo /Game/Home/About).

Todo muy bonito pero…

… estamos en una preview 1, así que no todo es tan directo.

Imaginad el caso del enlace que he puesto que genera una URL a la acción About del controlador Home del área Game. Imaginad que el código de la acción About de dicho controlador es:

public ActionResult About() { return View(); }

Esto debería retornar la vista que està en Views/Home/About.aspx del proyecto que es el área Game… Pues no 🙁 Esto utiliza la vista que está en Views/Home/About.aspx del proyecto que es el área principal. Si el área principal no tiene esta vista todo rebienta. Obviamente este comportamiento ni es correcto (cada área debe proporcionar sus propias vistas) ni útil ya que si de todos modos el proyecto del área principal debe contener todas las áreas… donde está la capacidad de poder modularizar el desarrollo?

Bueno, todo esto viene debido a que estamos en preview 1, y simplemente debemos cambiar la configuración de los proyectos. Si editamos los ficheros .csproj de cada aplicación web, veremos primero unas líneas como estas (que como nos indican debemos descomentar):

<!-- To enable MVC area subproject support, uncomment the following two lines:
<UsingTask TaskName="Microsoft.Web.Mvc.Build.CreateAreaManifest" 
AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" /> <UsingTask TaskName="Microsoft.Web.Mvc.Build.CopyAreaManifests"
AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
-->

y luego otras como las siguientes (que debemos descomentar según el proyecto sea el área principal o no):

<!-- If this is an area child project, uncomment the following line:
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" 
AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
ContentFiles="@(Content)" />
--> <!-- If this is an area parent project, uncomment the following lines: <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent"
AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
ContentFiles="@(Content)" />
<CopyAreaManifests ManifestPath="$(AreasManifestDir)"
CrossCopy="false" RenameViews="true" />
-->

Así pues ya vemos lo que se debe hacer, no? 😉 Descomentamos en cada proyecto las líneas que pertoquen y listos!

Esto básicamente lo que hace es establecer una custom action build que copia las vistas de las áreas hijas dentro de la carpeta Views del área principal. De esta manera dentro de la carpeta Views del área principal tendremos:

Views/Home/About.aspx –> Vista para la acción Home.About del área principal

Views/Areas/Game/Home/About.aspx –> Vista para la acción Home.About del área “Game” (copia de la vista Views/Home/About.aspx) del proyecto que es el área Game.

Nota Importante: Para que todo esto funcione el nombre del área debe corresponderse con el nombre del ensamblado del proyecto que genera dicha área. En mi caso, el proyecto del área “Game” debe generar el ensamblado Game.dll. Si el nombre del área y el nombre del ensamblado del proyecto es distinto, no funcionará porque…

… el motor de ASP.NET MVC usa el nombre del área (definido en la llamada a MapAreaRoute)

…y en cambio la vista estará copiada en Views/Areas/<NombreAssembly>

Si no quereis que el assembly se llame igual que nombre del área podeis modificar la línea:

<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" 
AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
ContentFiles="@(Content)" />

que hemos descomentado antes y poner en AreaName el nombre lógico del área (el que habéis usado en RegisterMapRoute). De todos modos aunque funciona no os lo recomiendo ya que entonces las vistas de este ensamblado se duplican dentro de Views/Areas… es decir se siguen copiando como Views/Areas/<NombreAssembly> y además como View/Areas/<NuestoNombreDeArea>… o sea que lo más práctico es que los assemblies se llamen como las áreas y listos 🙂

Toda esta paranoia de editar el fichero de proyecto desaparecerá en futuras versiones donde tendremos un template de proyecto para aplicación ASP.NET MVC “área principal” y otro para “área hija”… recordad: preview 1 😉

En fin… espero que esto os haya servido para ver un poco que son las áreas en ASP.NET MVC 2.0 Preview 1. Resumiendo:

  • Una área es un proyecto de ASP.NET MVC 2.0 que implementa parte de una aplicación entera (p.ej. el módulo de administración)
  • Una aplicación ASP.NET MVC 2.0 se compone de una (y sólo una) área padre y varias áreas hijas (una por módulo)
  • El área padre proporciona los estilos (css) y ficheros de javascript, así como la inicialización/finalización de la aplicación (global.asax). Puede también proporcionar vistas y controladores
  • Las áreas hijas proporcionan vistas y controladores adicionales
  • El método MapAreaRoute sirve para enrutar determinadas URLs a los controladores de una área determinada
  • Todo está bastante verde todavía pero bueno… 🙂

Saludos!!!

8 comentarios sobre “Explorando ASP.NET MVC 2.0… áreas”

  1. Genial, Eduard! Es la explicación más completa de las áreas que he visto hasta el momento.

    Como indicas, todavía es bastante artesano todo, ya conforme vayan avanzando los desarrollos podremos ver y explotar esta nueva capacidad del framework MVC, que desde luego, era justa y necesaria ;-D

    Un saludo.

  2. @José
    Me alegro que te haya gustado el post!!!! La verdad es que las novedades que promete ASP.NET MVC 2 son muy interesantes…. esperaremos la siguiente release!!! 😉

    Saludos y gracias por el comentario!

  3. Hola Eduard

    He encontrado este post buscando cómo resolver un problema que me ha surgido al publicar en IIS 6 una aplicacion asp.net mvc 2.0. Resulta que tengo todo lo que comentas en el post bien configurado, tengo una aplicacion principal y otras tantas child. Al publicarlo solo me deja acceder a las vistas del proyecto principal y he verificado que se ha creado la carpeta Views/Areas que contiene todas las vistas de todas las app hijas y los nombres de las carpetas coinciden con los de los assamblies de los proyectos child.
    Te agradecería cualquier info que me ayude a resolver este problema…
    Gracias de antemano

  4. @nadia
    Yo nunca he desplegado ninguna aplicación ASP.NET MVC 2.0 en un IIS, puesto que solo he «jugado» con el framework, así que me he limitado a ejecutarlo usando el propio servidor web de VS.NET.
    Si te funciona con el servidor web de VS.NET, entonces tienes todo bien configurado a nivel de desarrollo…

    No comentas que error te da al acceder a las vistas de otras áreas (500, 404, …) pero se me ocurre pensar que el problema puede estar debido a:

    1) Que no despliegues todo en IIS, aunque esto es dificil, ya que en principio con desplegar la /bin/ con todas las DLLs compiladas (la de la área principal y la de cada subárea) y la carpeta Views que contenga todas las vistas debería ser suficiente.
    2) Que se deba configurar alguna cosa a nivel de IIS…

    Siento no poder ayudarte mucho… sólo por curiosidad: qué error te da?

    Un saludo!

  5. Gracias Edu

    Las configuraciones en el IIS ya las he hecho según otros post publicados donde encontré los cambios que debía hacer en las propiedades del directorio virtual. Seguiré en la búsqueda porque ya tengo muy avanzada la aplicacion y no quisera migrar a asp.net mvc 1.0 con un único proyecto…
    Por cierto el error que me da al tratar de acceder a vistas de una child es HTTP 404 – File not found.

    Un saludo

  6. @Williams
    Gracias por tu comentario… pero ten presente que este post trata sobre la Preview 1 de ASP.NET MVC 2. Actualmente ya está disponible la versión final y el tema ya no es «tan pedrestre» 🙂

Responder a anonymous Cancelar respuesta

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