Nesting areas in asp.net mvc 3 - asp.net-mvc

I'm having trouble getting nested areas to work.
I would like to use a structure within the Areas folder, to organize my areas. I.e.:
Areas
Admin
Index (Default)
Locations
Controllers
Models
Views
...
Applications
Index (Default)
Calendar
Controllers
Models
Views
...
...
How do I go about structuring that with routes and how do I register each area. "Admin" and "Applications" are what I would call sections, and then the actual areas are located within a section.
I would have preferred using a route with an extra element, section, like so:
routes.MapRoute(
"Applications_default",
"{section}/{area}/{controller}/{action}/{id}",
new { section = "Applications", area = "Index", action = "Index", controller = "Home", id = UrlParameter.Optional }
);
Would that be possible?
I think I'm missing something with routes, because adding the route is one thing, but what would I name the area (AreaName property)? "Admin/Index"? "Admin.Index"? "Index" could be used in other places..
For now I'm trying the "normal" area registration using:
public override void RegisterArea(AreaRegistrationContext context) {
context.MapRoute(
"Admin_Index_default",
"Admin/Index/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
But when I go to http://localhost:60864/Admin/Index/Home/Index I get an error saying: "The resource cannot be found." :(
How do I structure my routes to comply with my desired tree structure?
I looked at another question: Can I nest areas in ASP.NET MVC?, but it did not solve my problem :(
Any help and guidance would be appreciated!
Thanks in advance

You should not restructure or reorganize your Areas folder. Keep the default, where each area has a Controllers, Models, and Views folder (plus AreaRegistration.cs, etc). Otherwise, you may be dealing with a spiderweb of namespace problems. Also, you will have to tell the razor engine which folders to check to find your views. Trust me your life will be happier if you just follow the conventions when it comes to areas.
If you want to create a deep URL structure, do it with routes. Your routes can be completely independent of your folder structure in MVC (unlike in webforms).
Do you try this route with a fresh MVC project? Meaning, no re-arrangement of the Areas folders? It should work, as long as your Admin area has a HomeController with an Index action:
public override void RegisterArea(AreaRegistrationContext context) {
context.MapRoute(
"Admin_Index_default",
"Admin/Index/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}

I don't think this is supported. For deep URLs I suggest using MvcCodeRouting, and you can forget about routing issues.

Related

Splitting up a controller and routing to controllers via 2 URL levels

I have a reports controller (ReportsController), that i've decided to split up as it contains too many actions. I currently have three pages that the controller serves:
app/reports/settings/
app/reports/charts/
app/reports/tables/
I decided to use the ReportsController just for the settings page, and I have created two seperate controllers to handle the other pages - Reports__ChartsController & Reports__TablesController.
I added the following two routes to my routes config:
routes.Add(
new Route("reports/charts/{action}/{id}",
new RouteValueDictionary(
new { controller = "Reports__Charts", action = "Index", id = UrlParameter.Optional }),
new HyphenatedRouteHandler()
)
);
routes.Add(
new Route("reports/tables/{action}/{id}",
new RouteValueDictionary(
new { controller = "Reports__Tables", action = "Index", id = UrlParameter.Optional }),
new HyphenatedRouteHandler()
)
);
The approach works really well, but I was wondering if there is a better way? The downside to this approach is that if I wanted to split out other controllers I would need to keep adding entries to my route config specific to each controller, which could enlarge my route config to the point where it's hard to maintain.
I assume what I wanted to do (splitting up controllers) is a common requirement, but i've not come across a standard method of implementing it via google.
This problem is exactly why we pounced on attribute based routing as soon as it was released. One route per action, in a clear concise manner, and moving actions between controllers is seamless, especially if you make use of the controller level attributes [RouteArea] and [RoutePrefix]. The sooner you make the switch, the happier you'll be. We've never looked back.

.net MVC Areas and subfolders

I am using Areas in my project but I would like to organise my views within these areas into sub folders.
For example, I have an area called Members which contains a Controller called Settings and another Controller called MemberCategory. MemberCategory is in effect a subfolder of Settings.
I would like when I access the Member Category view for my url to resolve to Members/Settings/MemberCategory
At present it resolves to Members/MemberCategory
Is it possible to nest the views into subfolders and change the controller to point to
return View("Members/Settings/MemberCategory");
Or do this need to done with routing?
Any examples would be appreciated.
I have resolved this problem with Routes and not nesting the views into subfolders.
In my Area Registration file I have added the following above the default route:
context.MapRoute(
"MemberCategory",
"Members/Settings/MemberCategory",
new { controller = "MemberCategory", action = "Index" }
);
context.MapRoute(
"MemberCategoryAction",
"Members/Settings/MemberCategory/{action}/{id}",
new { controller = "MemberCategory", action = "Index", id = UrlParameter.Optional }
);
Not sure if this is the most elegant way of doing this but it works in my case.
You can also do something like:
return this.View("../MailTemplates/ResetPassword");
To get to the view you want to use. The code editor will not be able to resolve this, but it works.
I think this is cleaner than:
return this.View("~/Areas/Cms/MailTemplates/ResetPassword");

What is wrong with my area routing in my bin deployed MVC4 app?

I have just deployed an MVC4 .NET 4.0 app to my web host, for 'live' deployed testing. Non -area routes are working fine, e.g. my
#Html.ActionLink("Register as a Client", "Register", "Account", new { registrationType = "Client"}, null)
link works fine, and the link opens the correct page. However, with a link to an area based action like this:
#Html.ActionLink("Authors", "Index", "Home", new { Area = "Author", registrationType = "Author" }, null)
the link actually rendered to the browser is missing action and controller, i.e.
http://mylivedomain.com/?Area=Author&registrationType=Author
It may be worth noting that the css bundling feature of MVC4 was not working after deployment, and I rolled back to using classic style links to individual stylesheets.
MAYBE RELATED: My question: Why is unmodified template code in my MVC4 app trying to register areas twice?
JUST IN: Removing the default action from the area route mappings for the default rouite solved this problem. There was no default controller to start with, in the VS2012 template code.
Try checking this.ControllerContext.RouteData.DataTokens["area"] in your area's action methods.
I've had a situation with similar issue in which the area name was simply an empty string.
Setting the DataToken in the action method might provide you a quick fix but it doesn't answer the question why MVC doesn't set the area name.
In your case because you specify area in query string, the routes from Global.asax.cs should apply. I belive you have something like this there:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).DataTokens.Add("area","Author");
}
This code makes Home/Index default route and skips Home/Index in url. If you change controller = "Home" on something else for example controller = "Home2" you will have displayed full link for Home/Index and new default route Home2/Index. Similar apply to default routes in areas if you have them specified.
In your global.asax.cs make sure you have this line in your Application_Start:
AreaRegistration.RegisterAllAreas();
In each area, you should have AreaRegistration.cs something like this:
public class testAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "test";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"test_default",
"test/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
With the #Html.ActionLink
use this format
#Html.ActionLink("Authors", "Index", "Home", new { Area = "Author", registrationType = "Author" }, new{})
Solved my issue.
Do you happen to inherit PortableAreaRegistration from MvcContrib in any of your areas?
We used to have exactly the same symptoms (works locally, not on server) until we removed all the PortableAreaRegistrations from Portable Areas and reverted to using simply AreaRegistration since MVC4 can register a portable area without any additional libraries.

ASP.NET MVC: Many routes -> always only one controller

I have very simple question. My site, based on ASP.NET MVC, can have many urls, but all of them should bring to the one controller. How to do that?
I suppose I need some magic in Global.asax but I don't know how to create route that will redirect any url to the specific controller.
For example I have url /about, /product/id etc. but all of them should be really bring to the content/show where the parts of url will be recognized and the decision what information to show will be make. It's some like CMS when you cannot define routes in advance. Is this information enough?
Thanks
This sounds like a horrible idea, but, well, if you must;
routes.MapRoute(
"ReallyBadIdea",
"{*url}",
new { controller = "MyFatController", action = "MySingleAction" }
);
This routes everything to a single action in a single controller. There's also {*path} and other URL patterns should you want slightly more flexibility.
Ideally you should try and specific with your routes, for example if you have a URL that is /products/42 and you want it to go to a generic controller you should specify it explicitly like
routes.MapRoute(
"Poducts",
"products/{id}",
new { controller = "Content", action = "Show", id = UrlParameter.Optional }
);
then you would specify another route for something else like /customers/42
routes.MapRoute(
"Customers",
"customers/{id}",
new { controller = "Content", action = "Show", id = UrlParameter.Optional }
);
this may seem a little verbose, and creating a single route might seem cleaner, but the issue a single route is you will never get a 404 and will have to handle such things in code.

Managing routes/Global.asax maintainability

Is there any best practice for how to best define and organize routes in MVC?
The company I work for runs a very extensive, complex ecommerce site with ~600K unique visitors/day.
Here's the problem: in our Global.asax.cs, we've got this HUGE list of approximately 75 route definitions in our RegisterRoutes():
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Is there a better way to define these routes other than having this gigantic list in the Global.asax.cs?
Because we've got a bunch of developers and half of them are incompetent and I can't go back refactoring these routes, it can take literally a couple minutes to figure out what controller is responsible for delivering a URL's View.
What can I do?
One developer toiled away building a prototype that allows us to do this in our Global.asax.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.Include(new RootController());
routes.Include(new AccountController());
routes.Include(new HelpController());
routes.Include(new SearchController());
// etc., for each controller
}
In this prototype, Include() is an extension method and all Controllers inherit from IRoutedController, which provides Include() an IEnumerable<Route> list of Routes to add to the RouteCollection.
But with this prototype, we have a new problem: instead of looking through a list of route.MapRoute() calls to find which controller a specific URL invokes, we now have to guess which Controller is responsible for a specific URL and check its IRoutedController list of routes to see if the URL actually invokes that Controller we guessed. Not so hard, but sometimes takes just as long as examining our list of 75+ Routes in Global.asax.cs.
Is this a better solution?
Is there any good solution?
Should we just keep adding routes to Global.asax.cs; should we give the prototype the green light; or should we do something else? (Assume that you cannot refactor existing route URLs to make them more logical.)
I'm sorry, but what are you talking about? :P
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This single route entry effectively allows you to call Any action method of Any controller with any parameters.
Is this a better solution?
Probably not.
Is there any good solution?
Yes. But you need to share more of your routes to be sure.
Should we just keep adding routes to Global.asax.cs; should we give the prototype the green light; or should we do something else?
No, No, and Yes. You need to share a decent amount of routes to have a better idea of the situation.
(Assume that you cannot refactor existing route URLs to make them more logical.)
But you can make sure new ones fit in the default route, so the problem doesn't keep growing i.e. {controller}/{action}
I would have stored the route information externally in xml file or database and loaded it in global.asax. I can add extra column/attribute that will have example url being routed so that I can search it quickly. Not to mention, I can update route information w/o rebuilding the project (of course, if there are new controllers, views etc then hey would need to be packaged in a new dll).

Resources