2010-05-22 21 views
6

Me gustaría lograr algo muy similar a this question, con algunas mejoras.Cómo enrutar URL estructuradas por árbol con enrutamiento ASP.NET?

Hay una aplicación web ASP.NET MVC.

Tengo un árbol de entidades.
Por ejemplo, una clase Page que tiene una propiedad llamada Children, que es del tipo IList<Page>. (Una instancia de la clase Page corresponde a una fila en una base de datos.)

Tenga en cuenta que los propietarios del sitio pueden agregar una página nueva en cualquier momento o eliminar las existentes, y las URL también deben reflejar esos cambios.

Me gustaría asignar una URL única a cada Page en la base de datos.
Manejo de objetos Page con un controlador llamado PageController.

URL de ejemplo:

http://mysite.com/Page1/ 
http://mysite.com/Page1/SubPage/ 
http://mysite.com/Page/ChildPage/GrandChildPage/ 

Usted consigue el cuadro.
Entonces, me gustaría que cada objeto Page tenga su propia URL que sea igual a la URL de su padre más su propio nombre.
Además de eso, también me gustaría la capacidad de asignar un solo Page a la dirección URL / (raíz).

quisiera aplicar estas reglas:

  1. Si una URL puede ser manejado con cualquier otra vía, o existe un archivo en el sistema de archivos en la URL especificada, dejar que el mapeo URL predeterminada ocurra
  2. Si una URL puede ser manejado por el proveedor de ruta de acceso virtual, deje que lo manejan
  3. Si no hay otro, mapear las otras direcciones URL a la clase PageController

También encontré this question, y also this one y this one, pero no fueron de mucha ayuda, ya que no brindan una explicación sobre mis dos primeros puntos.

veo las siguientes soutions posibles:

  • trazar una ruta para cada página invidually.
    Esto requiere que revise todo el árbol cuando se inicia la aplicación y agrega una ruta de coincidencia exacta al final de la tabla de rutas.
  • Podría agregar una ruta con {*path} y escribir una IRouteHandler personalizada que lo maneje, pero no veo cómo podría lidiar con las dos primeras reglas, dado que este manejador manejaría todo.

Hasta ahora, la primera solución parece ser la correcta, porque también es la más simple. Pero aún así, incluso en ese caso, no estoy seguro de cómo puedo hacer el PageController para manejar las solicitudes.

Realmente apreciaría sus pensamientos sobre esto.

¡Gracias de antemano!

EDIT: Ahora tuve tiempo de examinar cada aspecto de cada respuesta que recibí. Acepté la respuesta de Neal, ya que él es quien brinda la mejor explicación sobre cómo funcionan las cosas. También volví a votar todas las otras respuestas, ya que proporcionan buenas ideas.

Respuesta

3

Las rutas se procesan en el orden en que se agregan a la colección. Puede agregar su ruta personalizada después de las rutas existentes para asegurarse de que sea la última en tener la oportunidad de manejar la solicitud. Esto le permitirá agregar rutas para archivos existentes (virtuales o de otro tipo) y satisfacer los criterios 1 y 2.

De manera predeterminada, el enrutamiento MVC enrutará a los archivos existentes antes de aplicar cualquier ruta almacenada en la colección de rutas; ver http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx. (Hattip a Paul - ver comentarios).

Para enrutar las solicitudes a su controlador de página, simplemente cree una ruta personalizada que examine la ruta virtual y, si coincide con el patrón de una página en la base de datos, devuelve RouteData. Configure su RouteData con los valores apropiados extraídos de la ruta virtual (por ejemplo, establezca la clave de ruta en/Parent/Child/Grandchild), establezca la clave del controlador en el nombre del controlador de página (por ejemplo, Página) y la acción en el nombre de la acción que desea ejecutar (por ejemplo, Mostrar). El RouteData debe crearse con MvcRouteHandler (no estoy seguro de si ese es el nombre de clase correcto).

Para asegurarse de que las direcciones URL a las páginas con bases de datos se devuelven correctamente, reemplazar el método de RouteBaseGetVirtualPath(RequestContext, RouteValueDictionary) y utilizar los valores de ruta aprobada en determinar si se trata de una página conducido base de datos y si se trata de crear los datos de trayectoria virtual deseado (o devolver nulo de lo contrario).

Para obtener ayuda con GetRouteData primordial y GetVirtualPath, mirar el código fuente reflejada de System.Web.Routing.RouteBase y System.Web.Routing.Route; después de eso Google es tu amigo.

Las rutas se utilizan a la inversa para determinar la URL dada al controlador, a la acción y a cualquier otro valor de ruta. Debería poder utilizar esto para construir la url de la página dentro del contexto que se solicita.

+0

¿Por qué y cómo debería agregar rutas para los archivos existentes? Además, ¿cómo construiría la ruta para el árbol y los dirigiría al Controlador apropiado? – Venemo

+1

No es necesario; de forma predeterminada, el sistema de enrutamiento buscará un archivo existente y favorecerá que se entregue eso en vez de tratar de encontrar un controlador y una acción que coincidan. – Paul

+0

@Paul - Gracias. ¿Esto se aplica a los archivos proporcionados por un proveedor de ruta virtual también? – Venemo

1

Una idea diferente es usar T4 (Text Template Transformation Toolkit) para leer a sus hijos una vez y generar el contenido de su archivo Global.asax.

EDITAR: Básicamente con T4 puede automatizar la generación de archivos de texto. Por ejemplo, en lugar de copiar elementos de una gran colección y pegarlos con un contexto específico en un archivo de texto (como INSERT INTO [MyTable] (Text) VALUES (@ItemText)), puede hacer que un motor T4 lea la colección y genere estos enunciados para usted. Es estático y no está diseñado para un tiempo de ejecución.

Encuentro que una muy buena introducción está disponible en el libro Pro Entity Framework 4.0.

Pero si dices que tienes que hacerlo de forma dinámica, esta puede no ser la herramienta para ti.

+0

Estaría encantado de poder proporcionar más detalles, nunca he oído hablar de T4. BTW, las páginas pueden cambiar durante el tiempo de ejecución. Los administradores del sitio pueden agregarlos/eliminarlos o cambiarlos tanto como quieran. – Venemo

+0

Gracias por señalar que puede no ser la herramienta para mí. De hecho, la colección cambia en tiempo de ejecución. De todos modos, gracias por tomarse el tiempo para responderme. :) – Venemo

1

Usted conoce la estructura de sus páginas cuando guarda la página. Por lo tanto, puede generar una URL para cada página y guardarla en el registro de la base de datos. Luego puede usar la regla {*path} y encontrar la coincidencia exacta en la base de datos. Esta regla debe ser la última en la definición de sus reglas, por lo que puede hacer coincidir otras rutas.

Por ejemplo, su Page1 no tiene página principal, su URL es Page1. Su SubPage sabe que es el padre, por lo que puede ganar la url Page1/SubPage etc.

+0

rarouš - ¡Gracias por tu respuesta! Esta es una buena ida pero ¿'{* path}' no capturará nombres de archivos reales (y virtuales) también? – Venemo

1

Puede usar un patrón "Page/{*path}". Luego puede descomponer la ruta dividiendo la cadena en '/' y recorrerla, o puede usar la sugerencia de Rarouš de almacenar la ruta [generada] en el DB y hacer una búsqueda directa.

Si usa el método de Rarouš, tendrá que actualizar las entradas de ruta en su tabla para todos los elementos secundarios cuando la ruta principal cambie. Esto se puede hacer simplemente con una sola consulta de actualización.

Supongo que está mapeando la página que desea utilizar para la página de inicio en algún lugar de un archivo de configuración o entrada de tabla. Puede hacer que el controlador de la página de inicio realice la búsqueda y devuelva el contenido de la vista de la página de inicio para procesar (puede usar una vista compartida, una vista parcial o una llamada al controlador de página para no duplicar el comportamiento) o puede hacer que lo redirija a esa página.

Al usar esta técnica, puede tener un controlador de página único y una vista que maneje todas estas páginas de la misma manera. Sus otros requisitos parecen ser manejados automáticamente por el marco MVC.

Su trayectoria se vería así:

http://mysite.com/Page/Page1/ 
http://mysite.com/Page/Page1/SubPage/ 
http://mysite.com/Page/Page/ChildPage/GrandChildPage/ 

Por supuesto, puedes usar un prefijo distinto de "página".

+0

@Andre - ¡Gracias por tu respuesta! Esta es la mejor idea hasta ahora. :) – Venemo

+0

Puede ordenar las entradas de su ruta de la más específica a la menos específica y soltar el prefijo, use RouteTester (http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx) para verificar su rutas –

+0

http://stephenwalther.com/blog/archive/2008/08/03/asp-net-mvc-tip-29-build-a-controller-to-debug-your-custom-routes.aspx –

Cuestiones relacionadas