Comment définir une route par défaut (vers une zone) dans MVC

122

Ok, cela a déjà été demandé mais il n'y a pas de solution solide là-bas. Donc pour moi et pour les autres qui peuvent trouver cela utile.

Dans MVC2 (ASP.NET), je le veux donc, lorsque quelqu'un accède au site Web, une zone par défaut est spécifiée. Donc, la navigation sur mon site devrait vous envoyer vers ControllerX ActionY dans AreaZ.

Utilisation de l'itinéraire suivant dans Global.asax

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

Maintenant, cela fonctionne comme il essaie de servir la bonne page. Cependant MVC cherche la vue à la racine du site et non dans le dossier Area.

y a-il un moyen de résoudre cela?

ÉDITER

Il y a une 'Solution' et c'est dans ControllerX, ActionY renvoie le chemin complet de la vue. Un peu de hack mais ça marche. Cependant, j'espère qu'il existe une meilleure solution.

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

Éditer:

Cela devient également un problème lors de la création d'un HTML ActionLink de la page. Si la zone n'est pas définie, le lien d'action est vierge.

Tout cela est-il intentionnel ou un défaut?

LiamB
la source

Réponses:

98

Celui-ci m'intéressait et j'ai finalement eu la chance de l'examiner. D'autres personnes n'ont apparemment pas compris qu'il s'agissait d'un problème de recherche de la vue , pas d'un problème de routage lui-même - et c'est probablement parce que le titre de votre question indique qu'il s'agit de routage.

Dans tous les cas, comme il s'agit d'un problème lié à la vue, le seul moyen d'obtenir ce que vous voulez est de remplacer le moteur de vue par défaut . Normalement, lorsque vous faites cela, c'est dans le simple but de changer votre moteur de vue (c'est-à-dire sur Spark, NHaml, etc.). Dans ce cas, ce n'est pas la logique de création de vue que nous devons remplacer, mais les méthodes FindPartialViewand FindViewde la VirtualPathProviderViewEngineclasse.

Vous pouvez remercier vos chanceuses étoiles que ces méthodes sont en fait virtuelles, car tout le VirtualPathProviderViewEnginereste n'est même pas accessible - c'est privé, et cela le rend très ennuyeux de remplacer la logique de recherche parce que vous devez fondamentalement réécrire la moitié du code qui est déjà été écrit si vous voulez qu'il joue bien avec le cache de localisation et les formats de localisation. Après quelques recherches dans Reflector, j'ai finalement réussi à trouver une solution fonctionnelle.

Ce que j'ai fait ici, c'est d'abord créer un résumé AreaAwareViewEnginequi dérive directement de VirtualPathProviderViewEngineau lieu de WebFormViewEngine. J'ai fait cela pour que si vous souhaitez créer des vues Spark à la place (ou autre), vous pouvez toujours utiliser cette classe comme type de base.

Le code ci-dessous est assez long, donc pour vous donner un résumé rapide de ce qu'il fait réellement: il vous permet de mettre un {2}dans le format de l'emplacement, qui correspond au nom de la zone, de la même manière{1} correspond au nom du contrôleur. C'est tout! C'est pour cela que nous avons dû écrire tout ce code:

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;
    }
}

Maintenant, comme indiqué, ce n'est pas un moteur concret, vous devez donc le créer également. Cette partie, heureusement, est beaucoup plus facile, il suffit de définir les formats par défaut et de créer les vues:

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);
    }
}

Notez que nous avons ajouté quelques entrées à la norme ViewLocationFormats. Ce sont les nouvelles {2}entrées, où le {2}sera mappé à celui que areanous avons mis dans leRouteData . J'ai laissé le MasterLocationFormatsseul, mais évidemment vous pouvez changer cela si vous le souhaitez.

Modifiez maintenant votre global.asax pour enregistrer ce moteur de vue:

Global.asax.cs

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

... et enregistrez la route par défaut:

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 = "" }
    );
}

Maintenant, créez le AreaController nous venons de référencer:

DefaultController.cs (dans ~ / Controllers /)

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

De toute évidence, nous avons besoin de la structure de répertoires et de la vue qui vont avec - nous allons garder cela très simple:

TestView.aspx (dans ~ / Areas / AreaZ / Views / Default / ou ~ / Areas / AreaZ / Views / Shared /)

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

Et c'est tout. Enfin, nous avons terminé .

Pour la plupart, vous devriez pouvoir simplement prendre le BaseAreaAwareViewEngineet AreaAwareViewEngineet le déposer dans n'importe quel projet MVC, donc même s'il a fallu beaucoup de code pour y parvenir, vous n'avez qu'à l'écrire une fois. Après cela, il ne vous reste plus qu'à éditer quelques lignes global.asax.cset à créer la structure de votre site.

Aaronaught
la source
C'est vraisemblablement la meilleure solution actuelle mais loin d'être idéale. Comme ci-dessus, une fois que vous ajoutez un Actionlink ou un problème similaire, il existe.
LiamB
1
@Pino: Je pense que vous devriez être en mesure de résoudre le ActionLinkproblème en ajoutant la même chose area = "AreaZ"au mappage d'itinéraire "par défaut" dans global.asax.cs. Je ne suis pas sûr cependant; essayez-le et voyez.
Aaronaught le
Dans MVC4, la déclaration de route "par défaut" a été déplacée de Global.asax vers ~ / App_Start / RouteConfig.cs / RegisterRoutes ()
Andriy F.
3
Je déteste voter contre, mais je ne peux vraiment pas croire que la réponse ci-dessous de @Chris Alderson n'a pas reçu plus de votes. C'est une solution beaucoup plus simple que celle-ci et semble résoudre les cas extrêmes (ActionLinks, etc.).
jdmcnair
Il semble qu'il y ait un bug ici. Les vues d'une zone nommée "Re" par exemple se trouveraient dans ~ / Areas / Re / Views / Ctrlr / blah.aspx, mais le code ici utilise ~ / {2} / {1} / {0} qui serait ~ /Re/Ctrl/blah.aspx, il manque le répertoire des zones critiques dans le chemin. Ce devrait être "~ / Areas / {2} / Views / {1} / {0} .aspx"
Chris Moschini
100

Voilà comment je l'ai fait. Je ne sais pas pourquoi MapRoute () ne vous permet pas de définir la zone, mais il renvoie l'objet route afin que vous puissiez continuer à apporter les modifications supplémentaires que vous souhaitez. J'utilise cela parce que j'ai un site MVC modulaire qui est vendu aux entreprises clientes et qu'ils doivent pouvoir déposer des dll dans le dossier bin pour ajouter de nouveaux modules. Je leur permets de changer le "HomeArea" dans la configuration AppSettings.

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

Modifier: vous pouvez également essayer cela dans votre AreaRegistration.RegisterArea pour la zone dans laquelle vous voulez que l'utilisateur se rende par défaut. Je ne l'ai pas testé mais AreaRegistrationContext.MapRoute fait des ensembles route.DataTokens["area"] = this.AreaName;pour vous.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );
Chris Alderson
la source
Ça marche. Attention au nouveau fichier web.config, il peut remplacer vos anciennes configurations globales.
Mert Akcakaya
56

même il a déjà été répondu - c'est la syntaxe courte (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });
SeriousM
la source
6
Cela fonctionne très bien pour moi. Je n'ai pas de contrôleurs à la racine et n'utilise que des zones. Pour MVC 4, je remplace la valeur par défaut dans RouteConfig.cs. Merci!
Marc
2
J'utilise MVC4 et c'était la solution la plus simple pour moi. Permet à l'application d'utiliser la vue Index dans une zone particulière comme «page d'accueil» du site.
JTech
2
Cette solution ne fonctionnera pas à l'avenir (à partir d'Asp.Net MVC6 et supérieur).
Patrick Desjardins
@PatrickDesjardins: Une raison pour ne pas soutenir la solution ci-dessus?
Akash KC
@SeriousM Votre réponse est toujours verte. C'est toujours utile. Vous m'avez sauvé une nuit.
skpaul
16

Merci à Aaron d'avoir souligné qu'il s'agissait de localiser les vues, j'ai mal compris cela.

[MISE À JOUR] Je viens de créer un projet qui envoie l'utilisateur dans une zone par défaut sans déranger aucun des codes ou des chemins de recherche:

Dans global.asax, enregistrez-vous comme d'habitude:

    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,
        );
    }

dans Application_Start(), assurez-vous d'utiliser l'ordre suivant;

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

dans votre zone d'enregistrement, utilisez

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

Un exemple peut être trouvé à http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

J'espère vraiment que c'est ce que vous demandiez ...

////

Je ne pense pas qu'écrire un pseudo ViewEnginesoit la meilleure solution dans ce cas. (Manque de réputation, je ne peux pas commenter). Le WebFormsViewEngineest sensible à la zone et contient AreaViewLocationFormatsce qui est défini par défaut comme

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",
    };

Je crois que vous n'adhérez pas à cette convention. Vous avez posté

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

comme un hack de travail, mais cela devrait être

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

Si vous ne voulez pas suivre la convention, cependant, vous voudrez peut-être emprunter un chemin court en dérivant du WebFormViewEngine(qui est fait dans MvcContrib, par exemple) où vous pouvez définir les chemins de recherche dans le constructeur, ou -a petit hacky- en spécifiant votre convention comme ceci sur Application_Start:

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

Cela devrait être effectué avec un peu plus de soin, bien sûr, mais je pense que cela montre l'idée. Ces champs sont publicdans VirtualPathProviderViewEnginedans MVC 2 RC.

mnemosyn
la source
Il convient de noter que cela ne s'applique que dans le MVC 2 RC - le MVC 1 VirtualPathProviderViewEnginen'a pas cette propriété et n'est pas sensible à la zone. Et bien que cette question ait effectivement été posée comme concernant MVC 2, beaucoup de gens ne l'utilisent toujours pas (et ne le seront pas avant un certain temps). Donc, votre réponse est plus facile pour la question spécifique, mais la mienne est la seule qui fonctionnera pour les utilisateurs de MVC1 qui tombent sur cette question. J'aime fournir des réponses qui ne dépendent pas des fonctionnalités de pré-version qui sont potentiellement sujettes à changement.
Aaronaught
Ce n'est pas non plus un "pseudo moteur de vue" - les classes du moteur de vue ont été délibérément conçues pour être extensibles afin que différents types de vues puissent être utilisés.
Aaronaught
Cela ne visait pas à vous insulter, je suis désolé. C'est «pseudo» en ce sens qu'il ne change pas de manière significative la façon dont les vues sont gérées, mais remplace simplement certaines valeurs.
mnemosyn
Je n'ai pas été offensé, je voulais juste clarifier le fait que ce n'est pas une raison particulièrement inhabituelle pour dériver un moteur de vue personnalisé, comme en témoigne le fait que les méthodes pertinentes sont remplaçables.
Aaronaught
2
Un bon conseil pour y RegisterAreasaller avant RegisterRoutes. Je me demandais pourquoi mon code a soudainement cessé de fonctionner et a remarqué ce refactor;)
webnoob
6

Je suppose que vous voulez que l'utilisateur soit redirigé vers l' ~/AreaZURL une fois qu'il a visité l' ~/URL. Je réaliserais au moyen du code suivant dans votre racine HomeController.

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

Et l'itinéraire suivant Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);
Anthony Serdyukov
la source
Cela fonctionne, mais cela change en URL sur le navigateur des utilisateurs. Pas vraiment idéal.
LiamB
2

Tout d'abord, quelle version de MVC2 utilisez-vous? Il y a eu des changements significatifs de preview2 à RC.

En supposant que vous utilisez le RC, je pense que la cartographie de l'itinéraire devrait avoir un aspect différent. Dans AreaRegistration.csvotre région, vous pouvez enregistrer une sorte d'itinéraire par défaut, par exemple

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

Le code ci-dessus enverra l'utilisateur à MyRouteControllernotre ShopAreapar défaut.

L'utilisation d'une chaîne vide comme deuxième paramètre doit lever une exception, car un contrôleur doit être spécifié.

Bien sûr, vous devrez changer la route par défaut Global.asaxpour qu'elle n'interfère pas avec cette route par défaut, par exemple en utilisant un préfixe pour le site principal.

Voir également ce fil et la réponse de Haack: Ordre des routes d'enregistrement de zone MVC 2

J'espère que cela t'aides.

mnemosyn
la source
Merci, mais je ne suis pas sûr que cela résout le problème expliqué dans la question. Et son MVC RC
LiamB
2

L'ajout de ce qui suit à mon Application_Start fonctionne pour moi, même si je ne suis pas sûr que vous ayez ce paramètre dans 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
la source
1

Voici ce que j'ai fait pour que cela fonctionne:

  1. J'ai créé un contrôleur par défaut dans le dossier racine / Controllers. J'ai nommé mon contrôleur DefaultController.
  2. Dans le contrôleur, j'ai ajouté le code suivant:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. Dans mon RouterConfig.cs, j'ai ajouté ce qui suit:

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

L'astuce derrière tout cela est que j'ai créé un constructeur par défaut qui sera toujours le contrôleur de démarrage à chaque fois que mon application démarre. Lorsqu'il atteint ce contrôleur par défaut, il sera redirigé vers n'importe quel contrôleur que je spécifie dans l'action d'index par défaut. Ce qui dans mon cas est

www.myurl.com/FolderName/ControllerName

.

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

Avez-vous essayé cela?

Alpage Barbaros
la source
Oui, le problème vient du fait que maintenant le site recherche les vues à la racine. La vue «ActionY» ou son maître est introuvable. Les emplacements suivants ont été recherchés: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB
2
Je comprends. Je vais essayer de trouver une solution. +1 pour la question
Barbaros Alp
0

La localisation des différents blocs de construction est effectuée dans le cycle de vie de la demande. L'une des premières étapes du cycle de vie de la demande ASP.NET MVC consiste à mapper l'URL demandée à la méthode d'action du contrôleur appropriée. Ce processus est appelé routage. Une route par défaut est initialisée dans le fichier Global.asax et décrit au framework ASP.NET MVC comment gérer une demande. Double-cliquer sur le fichier Global.asax dans le projet MvcApplication1 affichera le code suivant:

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);
       }
   }

}

Dans le gestionnaire d'événements Application_Start (), qui est déclenché chaque fois que l'application est compilée ou que le serveur Web est redémarré, une table de routage est enregistrée. La route par défaut est nommée Default et répond à une URL sous la forme http://www.example.com/ {controller} / {action} / {id}. Les variables entre {et} sont remplies avec les valeurs réelles de l'URL de la requête ou avec les valeurs par défaut si aucun remplacement n'est présent dans l'URL. Cette route par défaut sera mappée au contrôleur Home et à la méthode d'action Index, selon les paramètres de routage par défaut. Nous n'aurons aucune autre action avec cette carte de routage.

Par défaut, toutes les URL possibles peuvent être mappées via cette route par défaut. Il est également possible de créer nos propres itinéraires. Par exemple, mappons l'URL http://www.example.com/Employee/Maarten au contrôleur Employee, à l'action Afficher et au paramètre firstname. L'extrait de code suivant peut être inséré dans le fichier Global.asax que nous venons d'ouvrir. Étant donné que l'infrastructure ASP.NET MVC utilise la première route correspondante, cet extrait de code doit être inséré au-dessus de la route par défaut; sinon, l'itinéraire ne sera jamais utilisé.

routes.MapRoute(

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

);

Maintenant, ajoutons les composants nécessaires pour cette route. Tout d'abord, créez une classe nommée EmployeeController dans le dossier Controllers. Vous pouvez le faire en ajoutant un nouvel élément au projet et en sélectionnant le modèle MVC Controller Class situé sous le site Web | Catégorie MVC. Supprimez la méthode d'action Index et remplacez-la par une méthode ou une action nommée Show. Cette méthode accepte un paramètre firstname et transmet les données dans le dictionnaire ViewData. Ce dictionnaire sera utilisé par la vue pour afficher les données.

La classe EmployeeController transmettra un objet Employee à la vue. Cette classe d'employé doit être ajoutée dans le dossier Modèles (cliquez avec le bouton droit sur ce dossier, puis sélectionnez Ajouter | Classe dans le menu contextuel). Voici le code de la classe Employee:

namespace MvcApplication1.Models {

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

} 
Sanju
la source
1
Merci, je ne sais pas trop comment cela se rapporte à la définition d'une ZONE par défaut. : - /
LiamB
0

Eh bien, même si la création d'un moteur de vue personnalisé peut fonctionner pour cela, vous pouvez toujours avoir une alternative:

  • Décidez de ce que vous devez afficher par défaut.
  • Ce quelque chose a un contrôleur et une action (et une zone), non?
  • Ouvrez l'enregistrement de cette zone et ajoutez quelque chose comme ceci:
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
}

À votre santé!

Tengiz
la source
D'accord. Bien que je pense qu'un endroit plus approprié pour cette définition d'itinéraire se trouve dans le fichier Global.asax.
nuhusky2003
Dans un tel cas, vos définitions global.asax connaîtraient l'existence d'un espace de noms de contrôleur de zone, ce qui, à mon avis, n'est pas correct. Les zones sont une fonctionnalité supplémentaire, ce qui signifie que vous devez pouvoir en ajouter / en supprimer une sans toucher aux définitions global.asax. Dans mon approche de la question, je préfère une zone pour "prendre en charge" la demande, au lieu d'un site Web [global] pour "transférer" la demande.
Tengiz
0

La solution acceptée à cette question est, bien que correcte pour résumer comment créer un moteur de vue personnalisé, ne répond pas correctement à la question. Le problème ici est que Pino spécifie incorrectement son itinéraire par défaut . En particulier, sa définition de «zone» est incorrecte. "Area" est vérifié via la collection DataTokens et doit être ajouté comme tel:

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);

La "zone" spécifiée dans l'objet par défaut sera ignorée . Le code ci-dessus crée une route par défaut, qui intercepte les demandes adressées à la racine de votre site, puis appelle le contrôleur par défaut, l'action d'indexation dans la zone d'administration. Veuillez également noter que la clé «Namespaces» est ajoutée aux DataTokens, ceci n'est requis que si vous avez plusieurs contrôleurs avec le même nom. Cette solution est vérifiée avec Mvc2 et Mvc3 .NET 3.5 / 4.0

nuhusky2003
la source
-1

ummm, je ne sais pas pourquoi toute cette programmation, je pense que le problème d'origine se résout facilement en spécifiant cette route par défaut ...

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