Cómo establecer una ruta predeterminada (a un área) en MVC

122

Ok, esto se ha preguntado antes, pero no hay una solución sólida por ahí. Por lo tanto, para mí y para otros que puedan encontrar esto útil.

En MVC2 (ASP.NET) lo quiero para que cuando alguien navegue al sitio web, se especifique un área predeterminada. Entonces, navegar a mi sitio debería enviarlo a ControllerX ActionY en AreaZ.

Usando la siguiente ruta en Global.asax

routes.MapRoute(
                "Area",
                "",
                new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
            );

Ahora esto funciona ya que intenta servir la página correcta. Sin embargo, MVC procede a buscar la Vista en la raíz del sitio y no en la carpeta Área.

¿Hay una manera de resolver esto?

EDITAR

Hay una 'Solución' y que está en ControllerX, ActionY devuelve la ruta completa de la vista. Un poco hack pero funciona. Sin embargo, espero que haya una mejor solución.

         public ActionResult ActionY()
        {
            return View("~/Areas/AreaZ/views/ActionY.aspx");
        }

Editar:

Esto también se convierte en un problema cuando se tiene un HTML ActionLink de la página. Si el área no está configurada, el enlace de acción se muestra en blanco.

¿Es todo esto por diseño o un defecto?

LiamB
fuente

Respuestas:

98

Este me interesó, y finalmente tuve la oportunidad de investigarlo. Otras personas aparentemente no han entendido que este es un problema con la búsqueda de la vista , no un problema con el enrutamiento sí mismo, y eso es probablemente porque el título de su pregunta indica que se trata de enrutamiento.

En cualquier caso, debido a que este es un problema relacionado con la Vista, la única forma de obtener lo que desea es anular el motor de vista predeterminado . Normalmente, cuando haces esto, es con el simple propósito de cambiar tu motor de visualización (es decir, a Spark, NHaml, etc.). En este caso, no es la lógica de creación de vistas lo que necesitamos anular, sino los métodos FindPartialViewy FindViewen la VirtualPathProviderViewEngineclase.

Puede gracias a su buena suerte que estos métodos son en realidad virtual, porque todo lo demás en el VirtualPathProviderViewEngineni siquiera es accesible - que es privado, y eso hace que sea muy molesto anular la lógica de búsqueda porque básicamente tiene que reescribir la mitad del código que ya está disponible. escrito si quieres que se vea bien con el caché de ubicación y los formatos de ubicación. Después de investigar un poco en Reflector, finalmente logré encontrar una solución de trabajo.

Lo que he hecho aquí es crear primero un resumen AreaAwareViewEngineque se derive directamente de en VirtualPathProviderViewEnginelugar de WebFormViewEngine. Hice esto para que si desea crear vistas de Spark en su lugar (o lo que sea), aún pueda usar esta clase como el tipo base.

El siguiente código es bastante largo, por lo que para darle un resumen rápido de lo que realmente hace: le permite poner un {2}formato de ubicación, que corresponde al nombre del área, de la misma manera{1} corresponde al nombre del controlador. ¡Eso es! Para eso tuvimos que escribir todo este código:

BaseAreaAwareViewEngine.cs

public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
    private static readonly string[] EmptyLocations = { };

    public override ViewEngineResult FindView(
        ControllerContext controllerContext, string viewName,
        string masterName, bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(viewName))
        {
            throw new ArgumentNullException(viewName,
                "Value cannot be null or empty.");
        }

        string area = getArea(controllerContext);
        return FindAreaView(controllerContext, area, viewName,
            masterName, useCache);
    }

    public override ViewEngineResult FindPartialView(
        ControllerContext controllerContext, string partialViewName,
        bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(partialViewName))
        {
            throw new ArgumentNullException(partialViewName,
                "Value cannot be null or empty.");
        }

        string area = getArea(controllerContext);
        return FindAreaPartialView(controllerContext, area,
            partialViewName, useCache);
    }

    protected virtual ViewEngineResult FindAreaView(
        ControllerContext controllerContext, string areaName, string viewName,
        string masterName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string viewPath = GetPath(controllerContext, ViewLocationFormats,
            "ViewLocationFormats", viewName, controllerName, areaName, "View",
            useCache, out searchedViewPaths);
        string[] searchedMasterPaths;
        string masterPath = GetPath(controllerContext, MasterLocationFormats,
            "MasterLocationFormats", masterName, controllerName, areaName,
            "Master", useCache, out searchedMasterPaths);
        if (!string.IsNullOrEmpty(viewPath) &&
            (!string.IsNullOrEmpty(masterPath) || 
              string.IsNullOrEmpty(masterName)))
        {
            return new ViewEngineResult(CreateView(controllerContext, viewPath,
                masterPath), this);
        }
        return new ViewEngineResult(
            searchedViewPaths.Union<string>(searchedMasterPaths));
    }

    protected virtual ViewEngineResult FindAreaPartialView(
        ControllerContext controllerContext, string areaName,
        string viewName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string partialViewPath = GetPath(controllerContext,
            ViewLocationFormats, "PartialViewLocationFormats", viewName,
            controllerName, areaName, "Partial", useCache,
            out searchedViewPaths);
        if (!string.IsNullOrEmpty(partialViewPath))
        {
            return new ViewEngineResult(CreatePartialView(controllerContext,
                partialViewPath), this);
        }
        return new ViewEngineResult(searchedViewPaths);
    }

    protected string CreateCacheKey(string prefix, string name,
        string controller, string area)
    {
        return string.Format(CultureInfo.InvariantCulture,
            ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
            base.GetType().AssemblyQualifiedName,
            prefix, name, controller, area);
    }

    protected string GetPath(ControllerContext controllerContext,
        string[] locations, string locationsPropertyName, string name,
        string controllerName, string areaName, string cacheKeyPrefix,
        bool useCache, out string[] searchedLocations)
    {
        searchedLocations = EmptyLocations;
        if (string.IsNullOrEmpty(name))
        {
            return string.Empty;
        }
        if ((locations == null) || (locations.Length == 0))
        {
            throw new InvalidOperationException(string.Format("The property " +
                "'{0}' cannot be null or empty.", locationsPropertyName));
        }
        bool isSpecificPath = IsSpecificPath(name);
        string key = CreateCacheKey(cacheKeyPrefix, name,
            isSpecificPath ? string.Empty : controllerName,
            isSpecificPath ? string.Empty : areaName);
        if (useCache)
        {
            string viewLocation = ViewLocationCache.GetViewLocation(
                controllerContext.HttpContext, key);
            if (viewLocation != null)
            {
                return viewLocation;
            }
        }
        if (!isSpecificPath)
        {
            return GetPathFromGeneralName(controllerContext, locations, name,
                controllerName, areaName, key, ref searchedLocations);
        }
        return GetPathFromSpecificName(controllerContext, name, key,
            ref searchedLocations);
    }

    protected string GetPathFromGeneralName(ControllerContext controllerContext,
        string[] locations, string name, string controllerName,
        string areaName, string cacheKey, ref string[] searchedLocations)
    {
        string virtualPath = string.Empty;
        searchedLocations = new string[locations.Length];
        for (int i = 0; i < locations.Length; i++)
        {
            if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
            {
                continue;
            }
            string testPath = string.Format(CultureInfo.InvariantCulture,
                locations[i], name, controllerName, areaName);
            if (FileExists(controllerContext, testPath))
            {
                searchedLocations = EmptyLocations;
                virtualPath = testPath;
                ViewLocationCache.InsertViewLocation(
                    controllerContext.HttpContext, cacheKey, virtualPath);
                return virtualPath;
            }
            searchedLocations[i] = testPath;
        }
        return virtualPath;
    }

    protected string GetPathFromSpecificName(
        ControllerContext controllerContext, string name, string cacheKey,
        ref string[] searchedLocations)
    {
        string virtualPath = name;
        if (!FileExists(controllerContext, name))
        {
            virtualPath = string.Empty;
            searchedLocations = new string[] { name };
        }
        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
            cacheKey, virtualPath);
        return virtualPath;
    }


    protected string getArea(ControllerContext controllerContext)
    {
        // First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
        object areaO;
        controllerContext.RouteData.Values.TryGetValue("area", out areaO);

        // If not specified, try to get it from the Controller's namespace
        if (areaO != null)
            return (string)areaO;

        string namespa = controllerContext.Controller.GetType().Namespace;
        int areaStart = namespa.IndexOf("Areas.");
        if (areaStart == -1)
            return null;

        areaStart += 6;
        int areaEnd = namespa.IndexOf('.', areaStart + 1);
        string area = namespa.Substring(areaStart, areaEnd - areaStart);
        return area;
    }

    protected static bool IsSpecificPath(string name)
    {
        char ch = name[0];
        if (ch != '~')
        {
            return (ch == '/');
        }
        return true;
    }
}

Ahora, como se dijo, este no es un motor concreto, por lo que también debe crearlo. Afortunadamente, esta parte es mucho más fácil, todo lo que tenemos que hacer es establecer los formatos predeterminados y crear las vistas:

AreaAwareViewEngine.cs

public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
    public AreaAwareViewEngine()
    {
        MasterLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.master",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.master",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.master",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.master"
            "~/Views/Shared/{0}.cshtml"
        };
        ViewLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.aspx",
            "~/Areas/{2}/Views/{1}/{0}.ascx",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.aspx",
            "~/Areas/{2}/Views/Shared/{0}.ascx",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.aspx",
            "~/Views/{1}/{0}.ascx",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.aspx"
            "~/Views/Shared/{0}.ascx"
            "~/Views/Shared/{0}.cshtml"
        };
        PartialViewLocationFormats = ViewLocationFormats;
    }

    protected override IView CreatePartialView(
        ControllerContext controllerContext, string partialPath)
    {
        if (partialPath.EndsWith(".cshtml"))
            return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
        else
            return new WebFormView(controllerContext, partialPath);
    }

    protected override IView CreateView(ControllerContext controllerContext,
        string viewPath, string masterPath)
    {
        if (viewPath.EndsWith(".cshtml"))
            return new RazorView(controllerContext, viewPath, masterPath, false, null);
        else
            return new WebFormView(controllerContext, viewPath, masterPath);
    }
}

Tenga en cuenta que hemos agregado algunas entradas al estándar ViewLocationFormats. Estas son las nuevas {2}entradas, donde {2}serán asignadas a las que areacolocamos en RouteData. Lo dejé MasterLocationFormatssolo, pero obviamente puedes cambiar eso si quieres.

Ahora modifique su global.asaxpara registrar este motor de vista:

Global.asax.cs

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new AreaAwareViewEngine());
}

... y registra la ruta predeterminada:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        "Area",
        "",
        new { area = "AreaZ", controller = "Default", action = "ActionY" }
    );
    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" }
    );
}

Ahora cree el AreaControllerque acabamos de referenciar:

DefaultController.cs (en ~ / Controllers /)

public class DefaultController : Controller
{
    public ActionResult ActionY()
    {
        return View("TestView");
    }
}

Obviamente, necesitamos la estructura del directorio y la vista para acompañarlo; mantendremos esto súper simple:

TestView.aspx (en ~ / Areas / AreaZ / Views / Default / o ~ / Areas / AreaZ / Views / Shared /)

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.

Y eso es. Finalmente hemos terminado .

En su mayor parte, usted debería ser capaz de tomar sólo la BaseAreaAwareViewEnginee AreaAwareViewEnginey colocarlo en cualquier proyecto de MVC, por lo que a pesar de que tomó una gran cantidad de código para hacer esto, sólo tiene que escribir una vez. Después de eso, solo es cuestión de editar algunas líneas global.asax.csy crear la estructura de su sitio.

Aaronaught
fuente
Esta es probablemente la mejor solución actual pero lejos de ser ideal. Como se indicó anteriormente, una vez que agrega un Actionlink o existe el mismo problema.
LiamB
1
@Pino: Creo que deberías poder resolver el ActionLinkproblema agregando lo mismo area = "AreaZ"a la asignación de ruta "predeterminada" global.asax.cs. Sin embargo, no soy positivo; Pruébalo y verás.
Aaronaught
En MVC4, la ruta "predeterminada" se trasladó de Global.asax a ~ / App_Start / RouteConfig.cs / RegisterRoutes ()
Andriy F.
3
Odio votar en contra, pero realmente no puedo creer que la respuesta a continuación de @Chris Alderson no haya recibido más votos. Es una solución mucho más simple que esta y parece resolver los casos extremos (ActionLinks, etc.).
jdmcnair
Parece que hay un error aquí. Las vistas para un área llamada "Re", por ejemplo, estarían en ~ / Areas / Re / Views / Ctrlr / blah.aspx, pero el código aquí usa ~ / {2} / {1} / {0} que sería ~ /Re/Ctrl/blah.aspx, falta el directorio de Áreas críticas en la ruta. Debería ser "~ / Areas / {2} / Views / {1} / {0} .aspx"
Chris Moschini
100

Así es como lo hice. No sé por qué MapRoute () no le permite establecer el área, pero sí devuelve el objeto de ruta para que pueda continuar haciendo los cambios adicionales que desee. Utilizo esto porque tengo un sitio MVC modular que se vende a clientes empresariales y necesitan poder colocar archivos dlls en la carpeta bin para agregar nuevos módulos. Les permito cambiar el "HomeArea" en la configuración de AppSettings.

var route = routes.MapRoute(
                "Home_Default", 
                "", 
                new {controller = "Home", action = "index" },
                new[] { "IPC.Web.Core.Controllers" }
               );
route.DataTokens["area"] = area;

Editar: también puede probar esto en su AreaRegistration.RegisterArea para el área que desea que el usuario vaya de forma predeterminada. No lo he probado, pero AreaRegistrationContext.MapRoute lo configura route.DataTokens["area"] = this.AreaName;para usted.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );
Chris Alderson
fuente
Funciona. Tenga cuidado con el nuevo archivo web.config, puede anular sus configuraciones globales anteriores.
Mert Akcakaya
56

incluso ya fue respondida: esta es la sintaxis corta (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });
Serio
fuente
66
Esto funciona muy bien para mi. No tengo ningún controlador en la raíz y solo uso Áreas. Para MVC 4, tengo que reemplazar el valor predeterminado en RouteConfig.cs. ¡Gracias!
Marc
2
Estoy usando MVC4 y esta fue la solución más simple para mí. Permite que la aplicación use la vista de índice dentro de un área particular como la 'página de inicio' del sitio
JTech
2
Esta solución no funcionará en el futuro (de Asp.Net MVC6 y superior).
Patrick Desjardins
@PatrickDesjardins: ¿Alguna razón para no admitir la solución anterior?
Akash KC
@SeriousM Tu respuesta es de hoja perenne. Sigue siendo útil. Me salvaste una noche.
skpaul
16

Gracias a Aaron por señalar que se trata de localizar las vistas, no entendí eso.

[ACTUALIZACIÓN] Acabo de crear un proyecto que envía al usuario a un Área por defecto sin meterse con ninguno de los códigos o rutas de búsqueda:

En global.asax, regístrese como de costumbre:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = ""}  // Parameter defaults,
        );
    }

en Application_Start(), asegúrese de usar el siguiente orden;

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }

en su registro de área, use

    public override void RegisterArea(AreaRegistrationContext context)
    {
        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller = "MyRoute" },
            new { controller = "MyRoute" }
        );
    }

Se puede encontrar un ejemplo en http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

Realmente espero que esto sea lo que estabas pidiendo ...

////

No creo que escribir un pseudo ViewEnginesea ​​la mejor solución en este caso. (Sin reputación, no puedo comentar). El WebFormsViewEngineárea es consciente y contiene el AreaViewLocationFormatsque se define por defecto como

AreaViewLocationFormats = new[] {
        "~/Areas/{2}/Views/{1}/{0}.aspx",
        "~/Areas/{2}/Views/{1}/{0}.ascx",
        "~/Areas/{2}/Views/Shared/{0}.aspx",
        "~/Areas/{2}/Views/Shared/{0}.ascx",
    };

Creo que no te adhieres a esta convención. Publicaste

public ActionResult ActionY() 
{ 
    return View("~/Areas/AreaZ/views/ActionY.aspx"); 
} 

como un truco funcional, pero eso debería ser

   return View("~/Areas/AreaZ/views/ControllerX/ActionY.aspx"); 

SI no desea seguir la convención, sin embargo, es posible que desee tomar un camino corto derivando del WebFormViewEngine(que se hace en MvcContrib, por ejemplo) donde puede establecer las rutas de búsqueda en el constructor, o -a little hacky- especificando tu convención como esta en Application_Start:

((VirtualPathProviderViewEngine)ViewEngines.Engines[0]).AreaViewLocationFormats = ...;

Esto debería realizarse con un poco más de cuidado, por supuesto, pero creo que muestra la idea. Estos campos están publicen VirtualPathProviderViewEngineen MVC 2 RC.

mnemosyn
fuente
Vale la pena señalar que esto solo se aplica en el MVC 2 RC: el MVC 1 VirtualPathProviderViewEngineno tiene esta propiedad y no tiene en cuenta el área. Y si bien se dijo que esta pregunta era sobre MVC 2, mucha gente todavía no la usa (y no lo hará por algún tiempo). Entonces, su respuesta es más fácil para la pregunta específica, pero la mía es la única que funcionará para los usuarios de MVC1 que se topan con esta pregunta. Me gusta proporcionar respuestas que no dependen de la funcionalidad de prelanzamiento que está potencialmente sujeta a cambios.
Aaronaught
Tampoco es un "pseudo motor de vista": las clases de motor de vista se hicieron deliberadamente para que sean extensibles, de modo que se puedan usar diferentes tipos de vistas.
Aaronaught
Eso no fue para insultarlo, lo siento. Es 'pseudo' en el sentido de que no cambia significativamente la forma en que se manejan las Vistas, sino que simplemente reemplaza algunos valores.
mnemosyn
No me ofendió, solo quería aclarar el hecho de que no es una razón particularmente inusual derivar un motor de vista personalizado, como lo demuestra el hecho de que los métodos relevantes son invalidables.
Aaronaught
2
Un gran consejo sobre RegisterAreasir antes RegisterRoutes. Me preguntaba por qué mi código dejó de funcionar repentinamente y noté ese refactor;)
webnoob
6

Supongo que desea que el usuario sea redirigido a la ~/AreaZURL una vez que haya visitado la ~/URL. Lo lograría mediante el siguiente código dentro de su raíz HomeController.

public class HomeController
{
    public ActionResult Index()
    {
        return RedirectToAction("ActionY", "ControllerX", new { Area = "AreaZ" });
    }
}

Y la siguiente ruta en Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);
Anthony Serdyukov
fuente
Esto funciona, pero cambia a URL en el navegador de los usuarios. No es ideal en realidad.
LiamB
2

Primero, ¿qué versión de MVC2 estás usando? Ha habido cambios significativos de preview2 a RC.

Suponiendo que use el RC, creo que su mapeo de rutas debería tener un aspecto diferente. En AreaRegistration.cssu área, puede registrar algún tipo de ruta predeterminada, p. Ej.

        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller="MyRoute" }
        );

El código anterior enviará al usuario a MyRouteControllernuestroShopArea por defecto.

El uso de una cadena vacía como segundo parámetro debería generar una excepción, ya que se debe especificar un controlador.

Por supuesto, tendrá que cambiar la ruta predeterminada en Global.asax para que no interfiera con esta ruta predeterminada, por ejemplo, utilizando un prefijo para el sitio principal.

Consulte también este hilo y la respuesta de Haack: Orden de rutas de registro de área MVC 2

Espero que esto ayude.

mnemosyn
fuente
Gracias, pero no estoy seguro de que esto resuelva el problema explicado en la pregunta. Y su MVC RC
LiamB
2

Agregar lo siguiente a mi Application_Start funciona para mí, aunque no estoy seguro si tiene esta configuración en RC:

var engine = (WebFormViewEngine)ViewEngines.Engines.First();

// These additions allow me to route default requests for "/" to the home area
engine.ViewLocationFormats = new string[] { 
    "~/Views/{1}/{0}.aspx",
    "~/Views/{1}/{0}.ascx",
    "~/Areas/{1}/Views/{1}/{0}.aspx", // new
    "~/Areas/{1}/Views/{1}/{0}.ascx", // new
    "~/Areas/{1}/Views/{0}.aspx", // new
    "~/Areas/{1}/Views/{0}.ascx", // new
    "~/Views/{1}/{0}.ascx",
    "~/Views/Shared/{0}.aspx",
    "~/Views/Shared/{0}.ascx"
};
Derek Hunziker
fuente
1

Lo que hice para que esto funcione es lo siguiente:

  1. Creé un controlador predeterminado en la carpeta raíz / Controladores. Llamé a mi controlador DefaultController.
  2. En el controlador agregué el siguiente código:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. En mi RouterConfig.cs agregué lo siguiente:

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new {controller = "Default", action = "Index", id = UrlParameter.Optional});

El truco detrás de todo esto es que hice un constructor predeterminado que siempre será el controlador de inicio cada vez que se inicie mi aplicación. Cuando llegue a ese controlador predeterminado, redirigirá a cualquier controlador que especifique en la Acción de índice predeterminada. Que en mi caso es

www.myurl.com/FolderName/ControllerName

.

JEuvin
fuente
0
routes.MapRoute(
                "Area",
                "{area}/",
                new { area = "AreaZ", controller = "ControlerX ", action = "ActionY " }
            );

¿Has intentado eso?

Alp Barbaros
fuente
Sí, el problema se debe al hecho de que ahora el sitio busca las vistas en la raíz. No se encontró la vista 'ActionY' o su maestro. Se buscaron las siguientes ubicaciones: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB
2
Entiendo. Voy a tratar de encontrar una solución. +1 para la pregunta
Barbaros Alp
0

La localización de los diferentes bloques de construcción se realiza en el ciclo de vida de la solicitud. Uno de los primeros pasos en el ciclo de vida de la solicitud ASP.NET MVC es asignar la URL solicitada al método de acción del controlador correcto. Este proceso se conoce como enrutamiento. Una ruta predeterminada se inicializa en el archivo Global.asax y describe al marco MVC de ASP.NET cómo manejar una solicitud. Al hacer doble clic en el archivo Global.asax en el proyecto MvcApplication1, se mostrará el siguiente código:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing;

namespace MvcApplication1 {

   public class GlobalApplication : System.Web.HttpApplication
   {
       public static void RegisterRoutes(RouteCollection routes)
       {
           routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

           routes.MapRoute(
               "Default",                                          // Route name
               "{controller}/{action}/{id}",                       // URL with parameters
               new { controller = "Home", action = "Index",
                     id = "" }  // Parameter defaults
           );

       }

       protected void Application_Start()
       {
           RegisterRoutes(RouteTable.Routes);
       }
   }

}

En el controlador de eventos Application_Start (), que se activa cada vez que se compila la aplicación o se reinicia el servidor web, se registra una tabla de ruta. La ruta predeterminada se llama Predeterminada y responde a una URL en forma de http://www.example.com/ {controller} / {action} / {id}. Las variables entre {y} se rellenan con valores reales de la URL de solicitud o con los valores predeterminados si no hay anulación en la URL. Esta ruta predeterminada se asignará al controlador Home y al método de acción Index, de acuerdo con los parámetros de ruta predeterminados. No tendremos ninguna otra acción con este mapa de enrutamiento.

De forma predeterminada, todas las URL posibles se pueden asignar a través de esta ruta predeterminada. También es posible crear nuestras propias rutas. Por ejemplo, vamos a asignar la URL http://www.example.com/Employee/Maarten al controlador de Empleado, la acción Mostrar y el parámetro de nombre. El siguiente fragmento de código se puede insertar en el archivo Global.asax que acabamos de abrir. Dado que el marco ASP.NET MVC utiliza la primera ruta coincidente, este fragmento de código debe insertarse encima de la ruta predeterminada; de lo contrario, la ruta nunca se utilizará.

routes.MapRoute(

   "EmployeeShow",                    // Route name
   "Employee/{firstname}",            // URL with parameters
    new {                             // Parameter defaults
       controller = "Employee",
       action = "Show", 
       firstname = "" 
   }  

);

Ahora, agreguemos los componentes necesarios para esta ruta. En primer lugar, cree una clase llamada EmployeeController en la carpeta Controllers. Puede hacer esto agregando un nuevo elemento al proyecto y seleccionando la plantilla MVC Controller Class ubicada en la Web | Categoría MVC. Elimine el método de acción Índice y reemplácelo con un método o acción llamado Mostrar. Este método acepta un parámetro de nombre y pasa los datos al diccionario ViewData. La vista utilizará este diccionario para mostrar datos.

La clase EmployeeController pasará un objeto Employee a la vista. Esta clase de Empleado debe agregarse en la carpeta Modelos (haga clic con el botón derecho en esta carpeta y luego seleccione Agregar | Clase en el menú contextual). Aquí está el código para la clase Employee:

namespace MvcApplication1.Models {

   public class Employee
   {
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public string Email { get; set; }
   }

} 
Sanju
fuente
1
Gracias, sin embargo, no estoy muy seguro de cómo se relaciona esto con la configuración de un ÁREA predeterminada. : - /
LiamB
0

Bueno, si bien crear un motor de vista personalizado puede funcionar para esto, aún puede tener una alternativa:

  • Decida lo que necesita mostrar por defecto.
  • Ese algo tiene controlador y acción (y Área), ¿verdad?
  • Abra el registro de esa Área y agregue algo como esto:
public override void RegisterArea(AreaRegistrationContext context)
{
    //this makes it work for the empty url (just domain) to act as current Area.
    context.MapRoute(
        "Area_empty",
        "",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        namespaces: new string[] { "Area controller namespace" }
    );
        //other routes of the area
}

¡Salud!

Tengiz
fuente
Convenido. Aunque creo que un lugar más apropiado para esta definición de ruta es el archivo Global.asax.
nuhusky2003
En tal caso, sus definiciones global.asax sabrían acerca de la existencia de un espacio de nombres de controlador de área, lo que creo que no es correcto. Las áreas son una funcionalidad agregada, lo que significa que debe poder agregar / eliminar una sin tocar las definiciones global.asax. En mi enfoque de la pregunta, prefiero un área para "asumir" la solicitud, en lugar de un sitio web [global] para "entregar" la solicitud.
Tengiz
0

La solución aceptada para esta pregunta es, si bien es correcta al resumir cómo crear un motor de vista personalizado, no responde la pregunta correctamente. El problema aquí es que Pino está especificando incorrectamente su ruta predeterminada . Particularmente su definición de "área" es incorrecta. El "Área" se verifica a través de la colección DataTokens y debe agregarse como tal:

var defaultRoute = new Route("",new RouteValueDictionary(){{"controller","Default"},{"action","Index"}},null/*constraints*/,new RouteValueDictionary(){{"area","Admin"}},new MvcRouteHandler());
defaultRoute.DataTokens.Add("Namespaces","MyProject.Web.Admin.Controller"); 
routes.Add(defaultRoute);

El "área" especificada en el objeto predeterminado será ignorado . El código anterior crea una ruta predeterminada, que detecta las solicitudes a la raíz de su sitio y luego llama al controlador predeterminado, acción de índice en el área de administración. Tenga en cuenta también la clave "Espacios de nombres" que se agrega a DataTokens, esto solo es necesario si tiene varios controladores con el mismo nombre. Esta solución se verifica con Mvc2 y Mvc3 .NET 3.5 / 4.0

nuhusky2003
fuente
-1

mmm, no sé por qué toda esta programación, creo que el problema original se resuelve fácilmente especificando esta ruta predeterminada ...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
Tarun
fuente