Come impostare un percorso predefinito (verso un'area) in MVC


122

Ok, questo è stato chiesto prima ma non esiste una soluzione solida là fuori. Quindi per uno scopo mio e di altri che potrebbero trovarlo utile.

In MVC2 (ASP.NET) lo voglio in modo che quando qualcuno naviga nel sito Web, viene specificata un'area predefinita. Quindi la navigazione nel mio sito dovrebbe inviarti a ControllerX ActionY in AreaZ.

Utilizzando il seguente percorso nel Global.asax

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

Ora funziona perché cerca di servire la pagina corretta. Tuttavia MVC procede a cercare la Vista nella radice del sito e non nella cartella Area.

C'è un modo per risolvere questo problema?

MODIFICARE

C'è una "Soluzione" e cioè in ControllerX, ActionY restituisce il percorso completo della vista. Un po 'un trucco ma funziona. Tuttavia spero che ci sia una soluzione migliore.

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

Modificare:

Questo diventa anche un problema quando si dispone di un ActionLink HTML della pagina. Se l'area non è impostata, Action Link viene emesso vuoto.

Tutto questo è dovuto alla progettazione o è un difetto?

Risposte:


98

Questo mi interessava e finalmente ho avuto la possibilità di esaminarlo. Altre persone apparentemente non hanno capito che questo è un problema con la ricerca della vista , non un problema con il routing stesso, e probabilmente è perché il titolo della tua domanda indica che si tratta di routing.

In ogni caso, poiché si tratta di un problema relativo alla visualizzazione, l'unico modo per ottenere ciò che desideri è sovrascrivere il motore di visualizzazione predefinito . Normalmente, quando lo fai, è per il semplice scopo di cambiare il tuo motore di visualizzazione (cioè a Spark, NHaml, ecc.). In questo caso, non è la logica di creazione della vista che dobbiamo sovrascrivere, ma i metodi FindPartialViewe FindViewnella VirtualPathProviderViewEngineclasse.

Puoi ringraziare le tue stelle fortunate che questi metodi sono in realtà virtuali, perché tutto il resto in VirtualPathProviderViewEnginenon è nemmeno accessibile : è privato e questo rende molto fastidioso sovrascrivere la logica di ricerca perché devi sostanzialmente riscrivere metà del codice che è già stato scritto se vuoi che funzioni bene con la cache di posizione e i formati di posizione. Dopo un po 'di ricerca in Reflector sono finalmente riuscito a trovare una soluzione funzionante.

Quello che ho fatto qui è creare prima un abstract AreaAwareViewEngineche derivi direttamente da VirtualPathProviderViewEngineinvece di WebFormViewEngine. L'ho fatto in modo che se desideri creare visualizzazioni Spark invece (o qualsiasi altra cosa), puoi comunque usare questa classe come tipo di base.

Il codice seguente è piuttosto prolisso, quindi per darti un breve riepilogo di ciò che fa effettivamente: ti consente di inserire un {2}nel formato della posizione, che corrisponde al nome dell'area, allo stesso modo{1} corrisponde al nome del controller. Questo è tutto! È per questo che abbiamo dovuto scrivere tutto questo codice:

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

Ora, come affermato, questo non è un motore concreto, quindi devi creare anche quello. Questa parte, fortunatamente, è molto più semplice, tutto ciò che dobbiamo fare è impostare i formati predefiniti e creare effettivamente le viste:

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

Nota che abbiamo aggiunto poche voci allo standard ViewLocationFormats. Queste sono le nuove {2}voci, in cui {2}verrà mappato il file che areaabbiamo inserito nel fileRouteData . Ho lasciato MasterLocationFormatsda solo, ma ovviamente puoi cambiarlo se lo desideri.

Ora modifica il tuo file global.asax per registrare questo motore di visualizzazione:

Global.asax.cs

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

... e registra la rotta predefinita:

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

Ora crea il file AreaController abbiamo appena fatto riferimento:

DefaultController.cs (in ~ / Controllers /)

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

Ovviamente abbiamo bisogno della struttura della directory e della vista per farlo - manterremo questo semplicissimo:

TestView.aspx (in ~ / 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.

E questo è tutto. Finalmente abbiamo finito .

Per la maggior parte, si dovrebbe essere in grado di prendere solo il BaseAreaAwareViewEngineed AreaAwareViewEnginee rilasciarlo in qualsiasi progetto MVC, quindi, anche se ci sono voluti un sacco di codice per ottenere questo fatto, devi solo scrivere una volta. Dopodiché, è solo questione di modificare alcune righe global.asax.cse creare la struttura del tuo sito.


Questa è molto probabilmente la migliore soluzione attuale, ma tutt'altro che ideale. Come sopra, una volta aggiunto un Actionlink o qualcosa del genere, esiste lo stesso problema.
LiamB

1
@Pino: Penso che dovresti essere in grado di risolvere il ActionLinkproblema aggiungendo lo stesso area = "AreaZ"alla mappatura del percorso "Default" in global.asax.cs. Non sono positivo però; provalo e guarda.
Aaronaught

In MVC4 la dichiarazione di route "Default" è stata spostata da Global.asax a ~ / App_Start / RouteConfig.cs / RegisterRoutes ()
Andriy F.

3
Non sopporto il voto negativo, ma non riesco davvero a credere che la risposta di seguito di @Chris Alderson non abbia ricevuto più voti. È una soluzione molto più semplice di questa e sembra risolvere i casi limite (ActionLinks, ecc.).
jdmcnair

Sembra che qui ci sia un bug. Le viste per un'area denominata "Re", ad esempio, sarebbero in ~ / Areas / Re / Views / Ctrlr / blah.aspx, ma il codice qui utilizza ~ / {2} / {1} / {0} che sarebbe ~ /Re/Ctrl/blah.aspx, manca la directory Aree critiche nel percorso. Dovrebbe essere "~ / Areas / {2} / Views / {1} / {0} .aspx"
Chris Moschini

100

Ecco come l'ho fatto. Non so perché MapRoute () non ti consente di impostare l'area, ma restituisce l'oggetto route in modo da poter continuare ad apportare eventuali modifiche aggiuntive che desideri. Lo uso perché ho un sito MVC modulare che viene venduto a clienti aziendali e devono essere in grado di rilasciare dll nella cartella bin per aggiungere nuovi moduli. Consento loro di modificare "HomeArea" nella configurazione di AppSettings.

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

Modifica: puoi provare anche questo nella tua AreaRegistration.RegisterArea per l'area in cui desideri che l'utente acceda per impostazione predefinita. Non l'ho testato ma AreaRegistrationContext.MapRoute fa i set route.DataTokens["area"] = this.AreaName;per te.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );

Funziona. Fai attenzione al nuovo file web.config, potrebbe sovrascrivere le tue vecchie configurazioni globali.
Mert Akcakaya

56

anche se è già stata data una risposta - questa è la sintassi breve (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });

6
Questo funziona alla grande per me. Non ho controller alla radice e utilizzo solo le aree. Per MVC 4 ho questo sostituire il valore predefinito in RouteConfig.cs. Grazie!
Marc

2
Sto usando MVC4 e questa è stata la soluzione più semplice per me. Consente all'applicazione di utilizzare la visualizzazione Indice all'interno di una particolare Area come "home page" del sito.
JTech

2
Questa soluzione non funzionerà in futuro (da Asp.Net MVC6 e versioni successive).
Patrick Desjardins

@PatrickDesjardins: Qualche motivo per non supportare la soluzione di cui sopra?
Akash KC

@SeriousM La tua risposta è sempreverde. È ancora utile. Mi hai salvato una notte.
skpaul

16

Grazie ad Aaron per aver sottolineato che si tratta di individuare i punti di vista, ho capito male.

[AGGIORNAMENTO] Ho appena creato un progetto che invia l'utente a un'Area per impostazione predefinita senza interferire con il codice o i percorsi di ricerca:

In global.asax, registrati come al solito:

    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(), assicurati di utilizzare il seguente ordine;

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

nella tua area registrazione, usa

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

Un esempio può essere trovato su http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

Spero davvero che questo sia quello che stavi chiedendo ...

////

Non credo che scrivere uno pseudo ViewEnginesia la soluzione migliore in questo caso. (Mancando di reputazione, non posso commentare). IlWebFormsViewEngine è Area a conoscenza e contiene AreaViewLocationFormatsche è definito per impostazione predefinita come

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

Credo che tu non aderisca a questa convenzione. Hai postato

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

come un trucco funzionante, ma dovrebbe essere

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

SE non vuoi seguire la convenzione, tuttavia, potresti voler prendere un percorso breve o derivando da WebFormViewEngine(che viene fatto in MvcContrib, ad esempio) dove puoi impostare i percorsi di ricerca nel costruttore, o -a little hacky- specificando la tua convenzione in questo modo suApplication_Start :

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

Questo dovrebbe essere eseguito con un po 'più di cura, ovviamente, ma penso che mostri l'idea. Questi campi sono publicin VirtualPathProviderViewEnginein MVC 2 RC.


Vale la pena notare che questo si applica solo nell'MVC 2 RC: l'MVC 1 VirtualPathProviderViewEnginenon ha questa proprietà e non è a conoscenza dell'area. E mentre questa domanda è stata effettivamente dichiarata riguardante MVC 2, molte persone non lo stanno ancora usando (e non lo sarà per un po 'di tempo). Quindi, la tua risposta è più facile per la domanda specifica, ma la mia è l'unica che funzionerà per gli utenti MVC1 che si imbattono in questa domanda. Mi piace fornire risposte che non dipendono dalla funzionalità pre-rilascio che è potenzialmente soggetta a modifiche.
Aaronaught

Inoltre non è uno "pseudo motore di visualizzazione": le classi del motore di visualizzazione sono state create deliberatamente per essere estensibili in modo da poter utilizzare diversi tipi di visualizzazioni.
Aaronaught

Non voleva insultarti, mi dispiace. È "pseudo" in quanto non cambia in modo significativo il modo in cui vengono gestite le viste, ma si limita a sostituire alcuni valori.
mnemosyn

Non mi sono offeso, volevo solo chiarire il fatto che non è un motivo particolarmente insolito per derivare un motore di visualizzazione personalizzato, come evidenziato dal fatto che i metodi pertinenti sono sovrascrivibili.
Aaronaught

2
Ottimo consiglio su come RegisterAreasandare prima RegisterRoutes. Mi chiedevo perché il mio codice
avesse

6

Immagino che tu voglia che l'utente venga reindirizzato ~/AreaZall'URL una volta che ha visitato l' ~/URL. Otterrei tramite il seguente codice all'interno della tua root HomeController.

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

E il seguente percorso in Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);

Funziona, ma cambia in URL nel browser degli utenti. Non è proprio l'ideale.
LiamB

2

Innanzitutto, quale versione di MVC2 stai utilizzando? Sono stati apportati cambiamenti significativi da preview2 a RC.

Supponendo che utilizzi l'RC, penso che la mappatura del percorso dovrebbe avere un aspetto diverso. Nella AreaRegistration.cstua zona, puoi registrare una sorta di percorso predefinito, ad es

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

Il codice di cui sopra invierà l'utente al MyRouteControllerin nostroShopArea per impostazione predefinita.

L'utilizzo di una stringa vuota come secondo parametro dovrebbe generare un'eccezione, poiché è necessario specificare un controller.

Ovviamente dovrai cambiare il percorso predefinito in Global.asax modo che non interferisca con questo percorso predefinito, ad esempio utilizzando un prefisso per il sito principale.

Vedi anche questo thread e la risposta di Haack: MVC 2 AreaRegistration Routes Order

Spero che questo ti aiuti.


Grazie, ma non sono sicuro che questo risolva il problema spiegato nella domanda. E il suo MVC RC
LiamB

2

L'aggiunta di quanto segue al mio Application_Start funziona per me, anche se non sono sicuro che tu abbia questa impostazione in 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"
};

1

Quello che ho fatto per farlo funzionare è il seguente:

  1. Ho creato un controller predefinito nella cartella root / Controllers. Ho chiamato il mio controller DefaultController.
  2. Nel controller ho aggiunto il seguente codice:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. Nel mio RouterConfig.cs ho aggiunto quanto segue:

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

Il trucco dietro tutto questo è che ho creato un costruttore predefinito che sarà sempre il controller di avvio ogni volta che la mia app viene avviata. Quando colpisce quel controller predefinito, reindirizzerà a qualsiasi controller specificato nell'azione indice predefinita. Che nel mio caso lo è

www.myurl.com/FolderName/ControllerName

.


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

L'hai provato?


Sì, il problema è dovuto al fatto che ora il sito cerca le visualizzazioni nella radice. La vista "ActionY" o il relativo master non è stato trovato. Sono state cercate le seguenti posizioni: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB

2
Capisco. Cercherò di trovare una soluzione. +1 per la domanda
Barbaros Alp

0

L'individuazione dei diversi elementi costitutivi viene eseguita nel ciclo di vita della richiesta. Uno dei primi passaggi nel ciclo di vita della richiesta ASP.NET MVC consiste nel mappare l'URL richiesto al metodo di azione del controller corretto. Questo processo è denominato routing. Una route predefinita viene inizializzata nel file Global.asax e descrive al framework ASP.NET MVC come gestire una richiesta. Facendo doppio clic sul file Global.asax nel progetto MvcApplication1 verrà visualizzato il codice seguente:

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

}

Nel gestore di eventi Application_Start (), che viene attivato ogni volta che l'applicazione viene compilata o il server Web viene riavviato, viene registrata una tabella di route. La route predefinita è denominata Default e risponde a un URL nel formato http://www.example.com/ {controller} / {action} / {id}. Le variabili tra {e} vengono popolate con i valori effettivi dell'URL della richiesta o con i valori predefiniti se non è presente alcuna sostituzione nell'URL. Questa route predefinita verrà mappata al controller Home e al metodo di azione Index, in base ai parametri di routing predefiniti. Non avremo altre azioni con questa mappa di instradamento.

Per impostazione predefinita, tutti i possibili URL possono essere mappati attraverso questo percorso predefinito. È anche possibile creare i nostri percorsi. Ad esempio, mappiamo l'URL http://www.example.com/Dipendente/Maarten al controller Employee, all'azione Show e al parametro firstname. Il seguente frammento di codice può essere inserito nel file Global.asax che abbiamo appena aperto. Poiché il framework ASP.NET MVC utilizza la prima route corrispondente, questo frammento di codice deve essere inserito sopra la route predefinita; altrimenti il ​​percorso non verrà mai utilizzato.

routes.MapRoute(

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

);

Ora aggiungiamo i componenti necessari per questo percorso. Prima di tutto, crea una classe denominata EmployeeController nella cartella Controllers. Puoi farlo aggiungendo un nuovo elemento al progetto e selezionando il modello di classe controller MVC che si trova in Web | Categoria MVC. Rimuovere il metodo di azione Index e sostituirlo con un metodo o un'azione denominata Show. Questo metodo accetta un parametro firstname e passa i dati nel dizionario ViewData. Questo dizionario verrà utilizzato dalla vista per visualizzare i dati.

La classe EmployeeController passerà un oggetto Employee alla visualizzazione. Questa classe Employee dovrebbe essere aggiunta nella cartella Models (fare clic con il tasto destro su questa cartella e quindi selezionare Aggiungi | Classe dal menu di scelta rapida). Ecco il codice per la classe Employee:

namespace MvcApplication1.Models {

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

} 

1
Grazie, non sono del tutto sicuro di come questo sia correlato all'impostazione di un'AREA predefinita. : - /
LiamB

0

Bene, mentre la creazione di un motore di visualizzazione personalizzato può funzionare per questo, puoi comunque avere un'alternativa:

  • Decidi cosa devi mostrare per impostazione predefinita.
  • Quel qualcosa ha controller e azione (e Area), giusto?
  • Apri la registrazione di quell'area e aggiungi qualcosa del genere:
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
}

Saluti!


Concordato. Anche se penso che un posto più appropriato per questa definizione di percorso sia nel file Global.asax.
nuhusky2003

In tal caso, le tue definizioni global.asax saprebbero dell'esistenza di uno spazio dei nomi di un controller di area, il che penso non sia corretto. Le aree sono una funzionalità aggiuntiva, il che significa che devi essere in grado di aggiungerne / rimuoverne una senza toccare le definizioni global.asax. Nel mio approccio alla domanda, preferisco un'area in cui "rilevare" la richiesta, invece di un sito Web [globale] per "consegnare" la richiesta.
Tengiz

0

La soluzione accettata a questa domanda è che, sebbene corretta nel riassumere come creare un motore di visualizzazione personalizzato, non risponde correttamente alla domanda. Il problema qui è che Pino sta specificando in modo errato il suo percorso predefinito . In particolare la sua definizione di "area" non è corretta. "Area" viene controllata tramite la raccolta DataTokens e deve essere aggiunta come tale:

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

L '"area" specificata nell'oggetto predefinito verrà ignorata . Il codice sopra crea un percorso predefinito, che cattura le richieste alla radice del tuo sito e quindi chiama controller predefinito, azione Indice nell'area di amministrazione. Si noti inoltre che la chiave "Namespaces" viene aggiunta a DataTokens, è necessaria solo se si dispone di più controller con lo stesso nome. Questa soluzione è verificata con Mvc2 e Mvc3 .NET 3.5 / 4.0


-1

ummm, non so perché tutta questa programmazione, penso che il problema originale sia risolto facilmente specificando questo percorso predefinito ...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.