So legen Sie in MVC eine Standardroute (zu einem Bereich) fest

122

Ok, das wurde schon einmal gefragt, aber es gibt keine feste Lösung da draußen. Also für mich und andere, die dies vielleicht nützlich finden.

In MVC2 (ASP.NET) möchte ich, dass beim Navigieren zur Website ein Standardbereich angegeben wird. Wenn Sie also zu meiner Site navigieren, sollten Sie zu ControllerX ActionY in AreaZ gesendet werden.

Verwenden Sie die folgende Route in Global.asax

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

Dies funktioniert nun so, als würde versucht, die richtige Seite bereitzustellen. MVC sucht jedoch weiterhin im Stammverzeichnis der Site nach der Ansicht und nicht im Ordner "Bereich".

Gibt es eine Möglichkeit, dies zu beheben?

BEARBEITEN

Es gibt eine 'Lösung', und in ControllerX gibt ActionY den vollständigen Pfad der Ansicht zurück. Ein bisschen hacken, aber es funktioniert. Ich hoffe jedoch, dass es eine bessere Lösung gibt.

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

Bearbeiten:

Dies wird auch zu einem Problem, wenn ein HTML-ActionLink der Seite vorhanden ist. Wenn der Bereich nicht festgelegt ist, wird der Aktionslink leer ausgegeben.

Ist das alles beabsichtigt oder ein Fehler?

LiamB
quelle

Antworten:

98

Dieser interessierte mich und ich hatte endlich die Gelegenheit, mich damit zu beschäftigen. Andere Leute haben anscheinend nicht verstanden, dass dies ein Problem beim Finden der Ansicht ist , kein Problem beim Routing selbst - und das liegt wahrscheinlich daran, dass Ihr Fragentitel darauf hinweist, dass es sich um Routing handelt.

In jedem Fall besteht die einzige Möglichkeit, das gewünschte Ergebnis zu erzielen, darin, die Standard-Ansichts-Engine zu überschreiben , da dies ein Problem im Zusammenhang mit der Ansicht ist . Wenn Sie dies tun, dient dies normalerweise dem einfachen Zweck, Ihre Ansichts-Engine zu wechseln (z. B. auf Spark, NHaml usw.). In diesem Fall müssen wir nicht die Logik zum Erstellen von Ansichten überschreiben, sondern die Methoden FindPartialViewund FindViewin der VirtualPathProviderViewEngineKlasse.

Sie können Ihren Glückssternen danken, dass diese Methoden tatsächlich virtuell sind, da alles andere in der VirtualPathProviderViewEnginenicht einmal zugänglich ist - es ist privat, und das macht es sehr ärgerlich, die Suchlogik zu überschreiben, da Sie im Grunde die Hälfte des bereits vorhandenen Codes neu schreiben müssen wurde geschrieben, wenn Sie möchten, dass es mit dem Standort-Cache und den Standortformaten gut funktioniert. Nach einigem Graben in Reflector gelang es mir schließlich, eine funktionierende Lösung zu finden.

Was ich hier getan habe, ist, zuerst eine Zusammenfassung zu erstellen AreaAwareViewEngine, die direkt von VirtualPathProviderViewEnginestatt abgeleitet ist WebFormViewEngine. Ich habe dies getan, damit Sie diese Klasse weiterhin als Basistyp verwenden können, wenn Sie stattdessen (oder was auch immer) Spark-Ansichten erstellen möchten.

Der folgende Code ist ziemlich langwierig, um Ihnen eine kurze Zusammenfassung der tatsächlichen Funktionsweise zu geben: Sie können ein Code {2}in das Standortformat einfügen, das dem Bereichsnamen entspricht, genauso wie {1}der Name des Controllers. Das ist es! Dafür mussten wir den ganzen Code schreiben:

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

Nun, wie gesagt, dies ist kein konkreter Motor, also müssen Sie das auch erstellen. Glücklicherweise ist dieser Teil viel einfacher. Wir müssen lediglich die Standardformate festlegen und die Ansichten erstellen:

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

Beachten Sie, dass wir dem Standard nur wenige Einträge hinzugefügt haben ViewLocationFormats. Dies sind die neuen {2}Einträge, bei denen die Einträge {2}den von areauns eingegebenen zugeordnet werden RouteData. Ich habe das MasterLocationFormatsalleine gelassen, aber natürlich können Sie das ändern, wenn Sie wollen.

Ändern Sie nun Ihre global.asax, um diese Ansichts-Engine zu registrieren:

Global.asax.cs

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

... und registrieren Sie die Standardroute:

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

Erstellen Sie nun das, auf das AreaControllerwir gerade verwiesen haben:

DefaultController.cs (in ~ / Controllers /)

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

Natürlich brauchen wir dazu die Verzeichnisstruktur und die Ansicht - wir halten das super einfach:

TestView.aspx (in ~ / Areas / AreaZ / Views / Default / oder ~ / Areas / AreaZ / Views / Shared /)

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

Und das ist es. Endlich sind wir fertig .

Zum größten Teil sollten Sie in der Lage sein, das BaseAreaAwareViewEngineund einfach AreaAwareViewEnginein jedes MVC-Projekt zu kopieren. Obwohl dafür viel Code erforderlich war, müssen Sie es nur einmal schreiben. Danach müssen Sie nur noch ein paar Zeilen bearbeiten global.asax.csund Ihre Site-Struktur erstellen.

Aaronaught
quelle
Dies ist höchstwahrscheinlich die derzeit beste Lösung, aber alles andere als ideal. Wie oben besteht das gleiche Problem, sobald Sie einen Actionlink oder ähnliches hinzufügen.
LiamB
1
@Pino: Ich denke, Sie sollten in der Lage sein, das ActionLinkProblem zu lösen , indem Sie dasselbe area = "AreaZ"zur "Standard" -Routenzuordnung in hinzufügen global.asax.cs. Ich bin allerdings nicht positiv; versuchen Sie es und sehen Sie.
Aaronaught
In MVC4 wurde die "Standard" -Routenerklärung von Global.asax nach ~ / App_Start / RouteConfig.cs / RegisterRoutes () verschoben
Andriy F.
3
Ich hasse es, abzustimmen, aber ich kann wirklich nicht glauben, dass die folgende Antwort von @Chris Alderson nicht mehr Stimmen erhalten hat. Es ist eine viel einfachere Lösung als diese und scheint die Randfälle (ActionLinks usw.) zu lösen.
jdmcnair
Es scheint, dass hier ein Fehler vorliegt. Die Ansichten für einen Bereich mit dem Namen "Re" befinden sich beispielsweise in ~ / Areas / Re / Views / Ctrlr / blah.aspx, aber der Code hier verwendet ~ / {2} / {1} / {0}, was ~ wäre /Re/Ctrl/blah.aspx, wobei das Verzeichnis für kritische Bereiche im Pfad fehlt. Es sollte "~ / Areas / {2} / Views / {1} / {0} .aspx" sein
Chris Moschini
100

So habe ich es gemacht. Ich weiß nicht, warum Sie mit MapRoute () den Bereich nicht festlegen können, aber das Routenobjekt wird zurückgegeben, sodass Sie weitere Änderungen vornehmen können, die Sie möchten. Ich verwende dies, weil ich eine modulare MVC-Site habe, die an Unternehmenskunden verkauft wird und die in der Lage sein müssen, DLLs in den Ordner bin zu legen, um neue Module hinzuzufügen. Ich erlaube ihnen, die "HomeArea" in der AppSettings-Konfiguration zu ändern.

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

Bearbeiten: Sie können dies auch in Ihrer AreaRegistration.RegisterArea für den Bereich versuchen, in den der Benutzer standardmäßig gehen soll. Ich habe es nicht getestet, aber AreaRegistrationContext.MapRoute macht Sets route.DataTokens["area"] = this.AreaName;für Sie.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );
Chris Alderson
quelle
Es klappt. Beachten Sie die neue Datei web.config, die möglicherweise Ihre alten globalen Konfigurationen überschreibt.
Mert Akcakaya
56

sogar es wurde bereits beantwortet - dies ist die kurze Syntax (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });
SeriousM
quelle
6
Das funktioniert gut für mich. Ich habe keine Controller im Stammverzeichnis und verwende nur Bereiche. Für MVC 4 muss dies die Standardeinstellung in RouteConfig.cs ersetzen. Vielen Dank!
Marc
2
Ich benutze MVC4 und dies war die einfachste Lösung für mich. Ermöglicht der Anwendung, die Indexansicht in einem bestimmten Bereich als "Homepage" der Site zu verwenden.
JTech
2
Diese Lösung wird in Zukunft nicht mehr funktionieren (ab Asp.Net MVC6).
Patrick Desjardins
@PatrickDesjardins: Gibt es einen Grund, die obige Lösung nicht zu unterstützen?
Akash KC
@ SeriousM Ihre Antwort ist immergrün. Es ist immer noch hilfreich. Du hast mir eine Nacht gerettet.
Skpaul
16

Vielen Dank an Aaron für den Hinweis, dass es darum geht, die Ansichten zu lokalisieren. Ich habe das falsch verstanden.

[UPDATE] Ich habe gerade ein Projekt erstellt, das den Benutzer standardmäßig in einen Bereich sendet, ohne den Code oder die Suchpfade zu beeinträchtigen:

Registrieren Sie sich in global.asax wie gewohnt:

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

in Application_Start(), stellen Sie sicher , dass die folgende Reihenfolge zu verwenden;

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

Verwenden Sie in Ihrem Bereich Registrierung

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

Ein Beispiel finden Sie unter http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

Ich hoffe wirklich, dass Sie darum gebeten haben ...

////

Ich denke nicht, dass das Schreiben eines Pseudos ViewEnginein diesem Fall die beste Lösung ist. (Mangelnder Ruf kann ich nicht kommentieren). Das WebFormsViewEngineist bereichsbewusst und enthält, AreaViewLocationFormatswelches standardmäßig als definiert ist

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

Ich glaube, Sie halten sich nicht an diese Konvention. Du hast geposted

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

als funktionierender Hack, aber das sollte sein

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

Wenn Sie der Konvention jedoch nicht folgen möchten, möchten Sie möglicherweise einen kurzen Pfad WebFormViewEngineeinschlagen, indem Sie entweder von (was beispielsweise in MvcContrib erfolgt) ableiten, wo Sie die Suchpfade im Konstruktor festlegen können, oder -a kleiner Hacky - indem Sie Ihre Konvention wie folgt angeben Application_Start:

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

Dies sollte natürlich mit etwas mehr Sorgfalt durchgeführt werden, aber ich denke, es zeigt die Idee. Diese Felder sind publicin VirtualPathProviderViewEnginein MVC 2 RC.

Mnemosyn
quelle
Es ist erwähnenswert, dass dies nur in der MVC 2 RC gilt - die MVC 1 VirtualPathProviderViewEnginehat diese Eigenschaft nicht und ist nicht bereichsbewusst. Und obwohl diese Frage tatsächlich MVC 2 betraf, verwenden viele Leute sie immer noch nicht (und werden es für einige Zeit nicht sein). Ihre Antwort ist für die spezifische Frage einfacher, aber meine ist die einzige, die für MVC1-Benutzer funktioniert, die auf diese Frage stoßen. Ich möchte Antworten geben, die nicht von der Funktionalität vor der Veröffentlichung abhängen, die möglicherweise geändert werden kann.
Aaronaught
Es ist auch keine "Pseudo-View-Engine" - die View-Engine-Klassen wurden absichtlich erweiterbar gemacht, damit verschiedene Arten von Ansichten verwendet werden können.
Aaronaught
Das sollte dich nicht beleidigen, tut mir leid. Es ist insofern 'pseudo', als es die Art und Weise, wie Ansichten behandelt werden, nicht wesentlich ändert, sondern lediglich einige Werte ersetzt.
Mnemosyn
Ich war nicht beleidigt, ich wollte nur die Tatsache klären, dass es kein besonders ungewöhnlicher Grund ist, eine benutzerdefinierte Ansichts-Engine abzuleiten, was durch die Tatsache belegt wird, dass die relevanten Methoden überschreibbar sind.
Aaronaught
2
Toller Tipp, um RegisterAreasvorher zu gehen RegisterRoutes. Ich habe mich gefragt, warum mein Code plötzlich nicht mehr funktioniert und diesen Refactor bemerkt hat;)
webnoob
6

Ich denke, Sie möchten, dass der Benutzer zu der ~/AreaZURL umgeleitet wird, sobald er die ~/URL besucht hat . Ich würde mit dem folgenden Code in Ihrem Stamm erreichen HomeController.

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

Und die folgende Route in Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);
Anthony Serdyukov
quelle
Dies funktioniert, ändert sich jedoch im Browser des Benutzers in URL. Nicht wirklich ideal.
LiamB
2

Erstens, welche Version von MVC2 verwenden Sie? Es gab signifikante Änderungen von Vorschau2 zu RC.

Angenommen, Sie verwenden den RC, sollte die Routenzuordnung meiner Meinung nach anders aussehen. In AreaRegistration.csIhrer Nähe können Sie eine Standardroute registrieren, z

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

Der obige Code sendet den Benutzer standardmäßig an die MyRouteControllerin unserer ShopArea.

Die Verwendung einer leeren Zeichenfolge als zweiten Parameter sollte eine Ausnahme auslösen, da ein Controller angegeben werden muss.

Natürlich müssen Sie die Standardroute ändern, Global.asaxdamit diese Standardroute nicht beeinträchtigt wird, z. B. indem Sie ein Präfix für die Hauptwebsite verwenden.

Siehe auch diesen Thread und Haacks Antwort: MVC 2 AreaRegistration Routes Order

Hoffe das hilft.

Mnemosyn
quelle
Vielen Dank, aber ich bin nicht sicher, ob dies das in der Frage erläuterte Problem behebt. Und seine MVC RC
LiamB
2

Das Hinzufügen von Folgendem zu meinem Application_Start funktioniert für mich, obwohl ich nicht sicher bin, ob Sie diese Einstellung in RC haben:

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
quelle
1

Was ich getan habe, um dies zum Laufen zu bringen, ist Folgendes:

  1. Ich habe einen Standard-Controller im Ordner root / Controller erstellt. Ich habe meinen Controller DefaultController genannt.
  2. In der Steuerung habe ich folgenden Code hinzugefügt:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. In meiner RouterConfig.cs habe ich Folgendes hinzugefügt:

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

Der Trick dahinter ist, dass ich einen Standardkonstruktor erstellt habe, der bei jedem Start meiner App immer der Startcontroller ist. Wenn es diesen Standardcontroller trifft, wird es zu jedem Controller umgeleitet, den ich in der Standardindexaktion angegeben habe. Was in meinem Fall ist

www.myurl.com/FolderName/ControllerName

.

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

Hast du das versucht?

Barbaros Alp
quelle
Ja, das Problem ist darauf zurückzuführen, dass die Site jetzt nach den Ansichten im Stammverzeichnis sucht. Die Ansicht 'ActionY' oder deren Master wurde nicht gefunden. Die folgenden Speicherorte wurden durchsucht: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB
2
Ich verstehe. Ich werde versuchen, eine Lösung zu finden. +1 für die Frage
Barbaros Alp
0

Das Auffinden der verschiedenen Bausteine ​​erfolgt im Anforderungslebenszyklus. Einer der ersten Schritte im ASP.NET MVC-Anforderungslebenszyklus besteht darin, die angeforderte URL der richtigen Controller-Aktionsmethode zuzuordnen. Dieser Vorgang wird als Routing bezeichnet. Eine Standardroute wird in der Datei Global.asax initialisiert und beschreibt dem ASP.NET MVC-Framework, wie eine Anforderung verarbeitet wird. Durch Doppelklicken auf die Datei Global.asax im MvcApplication1-Projekt wird der folgende Code angezeigt:

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

}

Im Ereignishandler Application_Start (), der bei jedem Kompilieren der Anwendung oder beim Neustart des Webservers ausgelöst wird, wird eine Routentabelle registriert. Die Standardroute heißt Standard und antwortet auf eine URL in Form von http://www.example.com/ {controller} / {action} / {id}. Die Variablen zwischen {und} werden mit tatsächlichen Werten aus der Anforderungs-URL oder mit den Standardwerten gefüllt, wenn die URL keine Überschreibung enthält. Diese Standardroute wird gemäß den Standardroutingparametern dem Home-Controller und der Indexaktionsmethode zugeordnet. Mit dieser Routing-Karte werden wir keine weiteren Aktionen ausführen.

Standardmäßig können alle möglichen URLs über diese Standardroute zugeordnet werden. Es ist auch möglich, eigene Routen zu erstellen. Lassen Sie uns beispielsweise die URL http://www.example.com/Employee/Maarten dem Mitarbeiter-Controller, der Aktion Anzeigen und dem Parameter Vorname zuordnen. Das folgende Codefragment kann in die gerade geöffnete Datei Global.asax eingefügt werden. Da das ASP.NET MVC-Framework die erste übereinstimmende Route verwendet, sollte dieses Codefragment über der Standardroute eingefügt werden. Andernfalls wird die Route niemals verwendet.

routes.MapRoute(

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

);

Fügen wir nun die erforderlichen Komponenten für diese Route hinzu. Erstellen Sie zunächst eine Klasse mit dem Namen EmployeeController im Ordner Controllers. Sie können dies tun, indem Sie dem Projekt ein neues Element hinzufügen und die MVC Controller Class-Vorlage auswählen, die sich unter Web | befindet MVC-Kategorie. Entfernen Sie die Indexaktionsmethode und ersetzen Sie sie durch eine Methode oder Aktion mit dem Namen Anzeigen. Diese Methode akzeptiert einen Vornamenparameter und übergibt die Daten an das ViewData-Wörterbuch. Dieses Wörterbuch wird von der Ansicht zum Anzeigen von Daten verwendet.

Die EmployeeController-Klasse übergibt ein Employee-Objekt an die Ansicht. Diese Employee-Klasse sollte im Ordner Models hinzugefügt werden (klicken Sie mit der rechten Maustaste auf diesen Ordner und wählen Sie dann Add | Class aus dem Kontextmenü). Hier ist der Code für die Employee-Klasse:

namespace MvcApplication1.Models {

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

} 
Sanju
quelle
1
Vielen Dank, ich bin mir nicht ganz sicher, wie dies mit dem Festlegen eines Standardbereichs zusammenhängt. : - /
LiamB
0

Nun, während das Erstellen einer benutzerdefinierten Ansichts-Engine dafür funktionieren kann, können Sie dennoch eine Alternative haben:

  • Entscheiden Sie, was Sie standardmäßig anzeigen möchten.
  • Das etwas hat Controller und Action (und Area), oder?
  • Öffnen Sie diese Bereichsregistrierung und fügen Sie Folgendes hinzu:
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
}

Prost!

Tengiz
quelle
Einverstanden. Obwohl ich denke, dass ein geeigneterer Ort für diese Routendefinition in der Datei Global.asax liegt.
nuhusky2003
In einem solchen Fall würden Ihre global.asax-Definitionen über die Existenz eines Area Controller-Namespace Bescheid wissen, was meiner Meinung nach nicht richtig ist. Bereiche sind eine zusätzliche Funktionalität, dh Sie müssen in der Lage sein, eine hinzuzufügen / zu entfernen, ohne die Definitionen von global.asax zu berühren. Bei meiner Herangehensweise an die Frage bevorzuge ich einen Bereich, in dem die Anfrage "übernommen" wird, anstatt eine [globale] Website, um die Anfrage zu "übergeben".
Tengiz
0

Eine akzeptierte Lösung für diese Frage ist, dass die Frage, wie eine benutzerdefinierte Ansichts-Engine erstellt wird, zwar korrekt ist, die Frage jedoch nicht richtig beantwortet. Das Problem hierbei ist, dass Pino seine Standardroute falsch angibt . Insbesondere seine "Gebiets" -Definition ist falsch. "Bereich" wird über die DataTokens-Sammlung überprüft und sollte als solcher hinzugefügt werden:

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

Der angegebene "Bereich" im Standardobjekt wird ignoriert . Der obige Code erstellt eine Standardroute, die Anforderungen an das Stammverzeichnis Ihrer Site abfängt und dann die Standardaktion "Index" im Administratorbereich aufruft. Beachten Sie auch, dass der Schlüssel "Namespaces" zu DataTokens hinzugefügt wird. Dies ist nur erforderlich, wenn Sie mehrere Controller mit demselben Namen haben. Diese Lösung wird mit Mvc2 und Mvc3 .NET 3.5 / 4.0 überprüft

nuhusky2003
quelle
-1

ähm, ich weiß nicht warum all diese Programmierung, ich denke, das ursprüngliche Problem kann leicht gelöst werden, indem diese Standardroute angegeben wird ...

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