How to set a Default Route (To an Area) in MVC
Asked Answered
S

13

122

Ok this has been asked before but there is no solid solution out there. So for purpose of myself and others who may find this useful.

In MVC2 (ASP.NET) I want it so when someone navigates to the website, there is a default area specified. So navigating to my site should send you to ControllerX ActionY in AreaZ.

Using the following route in the Global.asax

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

Now this works as in it does try to serve the correct page. However MVC proceeds to look for the View in the root of the site and not in the Area folder.

Is there a way to resolve this?

EDIT

There is a 'Solution' and that is in ControllerX, ActionY return the full path of the view. Bit of a hack but it does work. However I'm hoping there is a better solution.

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

Edit:

This also becomes an issue when having a HTML ActionLink of the page. If the area is not set the Action Link is output blank.

Is all of this by design or a flaw?

Saida answered 26/1, 2010 at 15:2 Comment(0)
B
102

This one interested me, and I finally had a chance to look into it. Other folks apparently haven't understood that this is an issue with finding the view, not an issue with the routing itself - and that's probably because your question title indicates that it's about routing.

In any case, because this is a View-related issue, the only way to get what you want is to override the default view engine. Normally, when you do this, it's for the simple purpose of switching your view engine (i.e. to Spark, NHaml, etc.). In this case, it's not the View-creation logic we need to override, but the FindPartialView and FindView methods in the VirtualPathProviderViewEngine class.

You can thank your lucky stars that these methods are in fact virtual, because everything else in the VirtualPathProviderViewEngine is not even accessible - it's private, and that makes it very annoying to override the find logic because you have to basically rewrite half of the code that's already been written if you want it to play nice with the location cache and the location formats. After some digging in Reflector I finally managed to come up with a working solution.

What I've done here is to first create an abstract AreaAwareViewEngine that derives directly from VirtualPathProviderViewEngine instead of WebFormViewEngine. I did this so that if you want to create Spark views instead (or whatever), you can still use this class as the base type.

The code below is pretty long-winded, so to give you a quick summary of what it actually does: It lets you put a {2} into the location format, which corresponds to the area name, the same way {1} corresponds to the controller name. That's it! That's what we had to write all this code for:

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

Now as stated, this isn't a concrete engine, so you have to create that as well. This part, fortunately, is much easier, all we need to do is set the default formats and actually create the views:

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

Note that we've added few entries to the standard ViewLocationFormats. These are the new {2} entries, where the {2} will be mapped to the area we put in the RouteData. I've left the MasterLocationFormats alone, but obviously you can change that if you want.

Now modify your global.asax to register this view engine:

Global.asax.cs

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

...and register the default route:

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

Now Create the AreaController we just referenced:

DefaultController.cs (in ~/Controllers/)

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

Obviously we need the directory structure and view to go with it - we'll keep this super simple:

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

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

And that's it. Finally, we're done.

For the most part, you should be able to just take the BaseAreaAwareViewEngine and AreaAwareViewEngine and drop it into any MVC project, so even though it took a lot of code to get this done, you only have to write it once. After that, it's just a matter of editing a few lines in global.asax.cs and creating your site structure.

Burress answered 31/1, 2010 at 16:12 Comment(8)
This is most likly the best current solution but far from ideal. As above once you add an Actionlink or such the same problem exists.Saida
@Pino: I think you should be able to solve the ActionLink issue by adding the same area = "AreaZ" to the "Default" route mapping in global.asax.cs. I'm not positive though; try it and see.Burress
In MVC4 "Default" route declaraton moved from Global.asax to ~/App_Start/RouteConfig.cs/RegisterRoutes()Workhorse
I hate to downvote, but I really can't believe the below answer by @Chris Alderson hasn't received more votes. It's a much simpler solution than this one and seems to resolve the edge cases (ActionLinks, etc).Wightman
It appears there's a bug here. The Views for an Area named "Re" for example would be in ~/Areas/Re/Views/Ctrlr/blah.aspx, but the code here uses ~/{2}/{1}/{0} which would be ~/Re/Ctrl/blah.aspx, missing the critical Areas directory in the path. It should be "~/Areas/{2}/Views/{1}/{0}.aspx"Resurgent
@ChrisMoschini: Please feel free to edit, I don't have anything I can test with right now.Burress
Modified to search the standard MVC areas for Views, and infer the Area from the Controller's namespace when not explicitly specified. Probably belongs in a lib on github/nuget or a gist or something.Resurgent
@ChrisMoschini thanks. Was actually a much bigger edit than I expected; I've been out of ASP.NET long enough that I'd probably have missed half of it.Burress
N
101

This is how I did it. I don't know why MapRoute() doesn't allow you to set the area, but it does return the route object so you can continue to make any additional changes you would like. I use this because I have a modular MVC site that is sold to enterprise customers and they need to be able to drop dlls into the bin folder to add new modules. I allow them to change the "HomeArea" in the AppSettings config.

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

Edit: You can try this as well in your AreaRegistration.RegisterArea for the area you want the user going to by default. I haven't tested it but AreaRegistrationContext.MapRoute does sets route.DataTokens["area"] = this.AreaName; for you.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );
Nashbar answered 29/7, 2010 at 17:5 Comment(1)
It works. Beware the new web.config file, it may override your old global configurations.Publicspirited
A
57

even it was answered already - this is the short syntax (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });
Assert answered 20/4, 2012 at 15:55 Comment(5)
This works great for me. I don't have any controllers at the root and only use Areas. For MVC 4 I have this replace the default in RouteConfig.cs. Thanks!Caudill
I'm using MVC4 and this was the simplest solution for me. Allows the application to use the Index view within a particular Area as the 'home page' of site.Hadley
This solution will not work in the future (from Asp.Net MVC6 and above).Starstarboard
@PatrickDesjardins : Any reason for not supporting above solution?Acherman
@Assert You answer is evergreen. It still helpful. You saved me a night.Chloe
B
16

Thanks to Aaron for pointing out that it's about locating the views, I misunderstood that.

[UPDATE] I just created a project that sends the user to an Area per default without messing with any of the code or lookup paths:

In global.asax, register as usual:

    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(), make sure to use the following order;

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

in you area registration, use

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

An example can be found at http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

I really hope that this is what you were asking for...

////

I don't think that writing a pseudo ViewEngine is the best solution in this case. (Lacking reputation, I can't comment). The WebFormsViewEngine is Area aware and contains AreaViewLocationFormats which is defined per default as

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

I believe you don't adhere to this convention. You posted

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

as a working hack, but that should be

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

IF you don't want to follow the convention, however, you might want to take a short path by either deriving from the WebFormViewEngine (that is done in MvcContrib, for example) where you can set the lookup paths in the constructor, or -a little hacky- by specifying your convention like this on Application_Start:

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

This should be performed with a little more care, of course, but I think it shows the idea. These fields are public in VirtualPathProviderViewEngine in MVC 2 RC.

Battle answered 31/1, 2010 at 16:41 Comment(5)
It's worth noting that this only applies in the MVC 2 RC - the MVC 1 VirtualPathProviderViewEngine doesn't have this property and is not area-aware. And while this question was indeed stated to be about MVC 2, a lot of people still aren't using it (and won't be for some time). So, your answer's easier for the specific question, but mine is the only one that will work for MVC1 users who stumble upon this question. I like to provide answers that don't depend on pre-release functionality that is potentially subject to change.Burress
Also it's not a "pseudo view engine" - the view engine classes were deliberately made to be extensible so that different kinds of views can be used.Burress
That was not meant to insult you, I am sorry. It is 'pseudo' in that it does not significantly change the way Views are handled, but merely replaces some values.Battle
I wasn't offended, I just wanted to clear up the fact that it's not a particularly unusual reason to derive a custom view engine, as evidenced by the fact that the relevant methods are overridable.Burress
Great tip about RegisterAreas going before RegisterRoutes. Was wondering why my code suddenly stopped working and noticed that refactor ;)Behead
S
6

I guess you want user to be redirected to ~/AreaZ URL once (s)he has visited ~/ URL. I'd achieve by means of the following code within your root HomeController.

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

And the following route in Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);
Schwarz answered 31/1, 2010 at 7:47 Comment(1)
This works, but it changes to URL on the users browser. Not ideal really.Saida
B
2

First, what version of MVC2 are you using? There have been significant changes from preview2 to RC.

Assuming you use the RC, I think you route-mapping should look differently. In the AreaRegistration.cs in your area, you can register some kind of default route, e.g.

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

The code above will send the user to the MyRouteController in our ShopArea per default.

Using an empty string as a second parameter should throw an exception, because a controller must be specified.

Of course you will have to change the default route in Global.asax so it doesn't interfere with this default route, e.g. by using a prefix for the main site.

Also see this thread and Haack's answer: MVC 2 AreaRegistration Routes Order

Hope this helps.

Battle answered 30/1, 2010 at 10:14 Comment(1)
Thanks, but im not sure this resolves the issue explained in the question. And its MVC RCSaida
E
2

Adding the following to my Application_Start works for me, although I'm not sure if you have this setting 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"
};
Earthiness answered 11/8, 2010 at 16:12 Comment(0)
K
1

What I did to get this to work is the following:

  1. I created a default controller in the root/Controllers folder. I named my controller DefaultController.
  2. In the controller I added the following code:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
    
  3. In my RouterConfig.cs I added the following:

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

The trick behind all this is that I made a default constructor which will always be the startup controller every time my app starts. When it hits that default controller it will redirect to any controller I specify in the default Index Action. Which in my case is

www.myurl.com/FolderName/ControllerName

.

Kelwin answered 13/7, 2016 at 18:0 Comment(0)
B
0
routes.MapRoute(
                "Area",
                "{area}/",
                new { area = "AreaZ", controller = "ControlerX ", action = "ActionY " }
            );

Have you tried that ?

Brutus answered 26/1, 2010 at 23:21 Comment(2)
Yes, the problem is down to the fact that now the site looks for the views in the root. The view 'ActionY' or its master was not found. The following locations were searched: ~/Views/ActionY/ActionY.aspx ~/Views/ActionY/ActionY.ascx ~/Views/Shared/ActionY.aspx ~/Views/Shared/ActionY.ascxSaida
I understand. I am going to try to find a solution. +1 for the questionBrutus
S
0

Locating the different building blocks is done in the request life cycle. One of the first steps in the ASP.NET MVC request life cycle is mapping the requested URL to the correct controller action method. This process is referred to as routing. A default route is initialized in the Global.asax file and describes to the ASP.NET MVC framework how to handle a request. Double-clicking on the Global.asax file in the MvcApplication1 project will display the following code:

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

}

In the Application_Start() event handler, which is fired whenever the application is compiled or the web server is restarted, a route table is registered. The default route is named Default, and responds to a URL in the form of http://www.example.com/{controller}/{action}/{id}. The variables between { and } are populated with actual values from the request URL or with the default values if no override is present in the URL. This default route will map to the Home controller and to the Index action method, according to the default routing parameters. We won't have any other action with this routing map.

By default, all the possible URLs can be mapped through this default route. It is also possible to create our own routes. For example, let's map the URL http://www.example.com/Employee/Maarten to the Employee controller, the Show action, and the firstname parameter. The following code snippet can be inserted in the Global.asax file we've just opened. Because the ASP.NET MVC framework uses the first matching route, this code snippet should be inserted above the default route; otherwise the route will never be used.

routes.MapRoute(

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

);

Now, let's add the necessary components for this route. First of all, create a class named EmployeeController in the Controllers folder. You can do this by adding a new item to the project and selecting the MVC Controller Class template located under the Web | MVC category. Remove the Index action method, and replace it with a method or action named Show. This method accepts a firstname parameter and passes the data into the ViewData dictionary. This dictionary will be used by the view to display data.

The EmployeeController class will pass an Employee object to the view. This Employee class should be added in the Models folder (right-click on this folder and then select Add | Class from the context menu). Here's the code for the Employee class:

namespace MvcApplication1.Models {

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

} 
Suffice answered 30/1, 2010 at 9:52 Comment(1)
Thanks, I'm not quite sure how this relates to setting a default AREA though. :-/Saida
P
0

Well, while creating a custom view engine can work for this, still you can have an alternative:

  • Decide what you need to show by default.
  • That something has controller and action (and Area), right?
  • Open that Area registration and add something like this:
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
}

Cheers!

Prolegomenon answered 14/2, 2012 at 17:50 Comment(2)
Agreed. Although I think a more appropriate place for this route definition is in the Global.asax file.Enchantment
In such a case your global.asax definitions would know about an area controller namespace existence, which I think is not right. Areas are an added functionality, which means you must be able to add/remove one without touching the global.asax definitions. In my approach to the question, I prefer an area to "take-over" the request, instead of a [global] website to "hand-over" the request.Prolegomenon
E
0

Accepted solution to this question is, while correct in summing up how to create a custom view engine, does not answer the question correctly. Issue here is that Pino is incorrectly specifying his default route. Particularly his "area" definition is incorrect. "Area" is checked via DataTokens collection and should be added as such:

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

Specified "area" in defaults object will be ignored. Code above creates a default route, which catches on requests to your site's root and then calls Default controller, Index action in Admin area. Please also note "Namespaces" key being added to DataTokens, this is only required if you have multiple controllers with same name. This solution is verified with Mvc2 and Mvc3 .NET 3.5/4.0

Enchantment answered 11/4, 2012 at 19:54 Comment(0)
T
-1

ummm, I don't know why all this programming, I think the original problem is solved easily by specifying this default route ...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
Terrel answered 1/3, 2012 at 4:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.