Nested areas in MVC 2 / MVC 3 / MVC 4 - asp.net-mvc-3

Since MVC 2 we can create areas easily. Now my question is related to nested areas (areas inside of areas).
Select my "father" area folder, Right-mouse-click > Add > NO option for a new Area.
Is it possible to do it in some other way ? or will this option be available in the near future?

I realise this is an old question but I'll answer it in case anyone else is trying to figure it out. A solution to this is to create areas that use a different routing value at a lower level than area, so for example your RouteConfig would look something like this:
public class RouteConfig
{
/// <summary>
/// A function that registers the default navigation route.
/// </summary>
/// <param name="routes">The RouteCollection to act on.</param>
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var route = routes.MapRoute(
name: "Default",
url: "{area}/{subArea}/{controller}/{action}/{id}",
defaults: new { area = "DefaultArea", controller = "Home", action = "Splash", id = UrlParameter.Optional, section = "Customer" },
namespaces: new string[] { "Application.Controllers" });
}
}
And one of your sub-area registrations might look like this:
public class ApplicationSubAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "ApplicationSubArea";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"SubArea_default",
"Area/SubArea/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new string[] { "Application.Areas.AreaName.SubAreaName.Controllers" }
);
}
}
After reading that, does "area" still look like a word? Because it doesn't to me.
P.S. You can do this recursively as many times as you like (theoretically) such that for example you could do
url: "{area}/{subArea}/{subSubArea}/{subSubSubArea}/{evenMoreSubArea}/{controller}/{action}/{id}",
in your RouteConfig.cs and
"Area/SubArea/SubSubArea/SubSubSubArea/EvenMoreSubArea/{controller}/{action}/{id}",
in your area registration.

For now there isn't any information telling if there will be nested areas.
In the future maybe this will change.

Using the idea of Multi-project areas as a start, I guess you could recursively create more nested areas.

Maybe something like this could help. It's more like a study which is in mvc-contrib.
I saw it for version 1 don't know if it's compatible for MVC2
It's the concept of sub-controllers: http://mhinze.com/subcontrollers-in-aspnet-mvc/

At this time MVC supports only Main Application and then Areas in next level and NOT nested Areas, but you can look at This Nuget Package that adds the following functionality to your project:
Organize your controllers and views using namespaces (no more areas) that can go as deep as you want.
Default constraints for primivite types that can be overridden on a per-parameter or per-site basis.
Intelligent grouping of similar routes for efficient matching.
Support for a root controller.
Support for overloaded actions.
Support for hierarchical (a.k.a. RESTful) routes.
Support for user-defined custom routes.
Detection of ambiguous routes.
Formatting of routes (e.g. to lowercase, hyphen-separated, underscore-separated, etc).
Render your routes as calls to the MapRoute extension method, for debugging.
Support for embedded views (as assembly resources).

You do not want to have nested aereas.
There is something wrong in your Software design.
the most common case is, that you use areas as Html Renderer,
therefore are the Display Templates.

Related

How to override a web api route?

I am trying to standardize an extension model for our REST API development team. We need to provide default implementation of routes, while allowing for custom implementations of routes that replace the default as well.
As a simple example if we have a GET route api/users like this:
public class DefaultUsersController : ApiController
{
[HttpGet]
[Route("api/users", Order = 0)]
public IEnumerable<string> DefaultGetUsers()
{
return new List<string>
{
"DefaultUser1",
"DefaultUser2"
};
}
}
We expect the default work like this:
Now a developer wants to change the behavior of that route, he should be able to simply define the same route with some mechanism to imply their implementation should be the one used, instead of the default. My initial thinking was to use the Order property on the Route attribute since that's what it appears to be there for, as a way to provide a priority (in ascending order) when an ambiguous route is discovered. However it's not working that way, consider this custom implementation that we want to override the default api/users route:
public class CustomUsersController : ApiController
{
[HttpGet]
[Route("api/users", Order = -1)]
public IEnumerable<string> CustomGetUsers()
{
return new List<string>
{
"CustomUser1",
"CustomUser2"
};
}
}
Notice the Order property is set to -1 to give it a lower priority value than the default, which is set to 0. I would have thought this would be used by the DefaultHttpControllerSelector, but it isn't. From the DefaultHttpControllerSelector:
And we end up with this exception being returned in the response:
Is it possible Microsoft just missed the logic/requirement to use Order as a route disambiguator and this is a bug? Or is there another simple way to override a route, hopefully with an attribute?
I have pretty much the same problem. I am creating a starter site, but I want users to be able to redefine to behaviour of a Controller, especially if there is a bug.
I use Autofac to resolve the Controller, but even when I register the new controller as the old one, the original one gets selected.
What I'll do is probably go with URL Rewriting. Especially since this issue is temporary in my case. However, I would be interested if someone has a better option.

mvc two areas with same name in different project

I have a project with the following structure:
Solution
- CMS.core
---Areas
------Admin
---------Controllers
- Site.Web
---Areas
------Admin
---------Controllers
Everytime I try to route to a controller under site.web/areas/admin/controllers it appears to only look in cms.core/areas/admin/controllers.
Does this make sense? How do I route mvc to multiple areas of the same name located in different projects?
You need to provide the namespace in which the controller resides.. for example:
routes.MapRoute(
"SiteWebAdminRoute",
"site.web/areas/{controller}/{action}/{id}",
new { Area = "SiteWeb", controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "Site.Web.Areas.Admin.Controllers" });
..or something similar. Notice the string array down the bottom though. If you look in the Intellisense, it shows you that this string array represents any namespaces where this route should look for controllers. You can do the same with your other area route.. except you would provide a different namespace to look in.

Dynamic Routing in ASP.NET MVC 3

I am building a blog engine using MVC 3 and razor. In this scenario, I have given options like a user can have multiple blogs (similar to blogger.com)
Now say a user 'yasser' has the following 3 blogs
TechStory
GameGeek
MeMyStory
so I want all other users to access these blogs by the following urls
www.domainName.com/blogs/TechStory
www.domainName.com/blogs/GameGeek
www.domainName.com/blogs/MeMyStory
And more blogs can be added hence more such url will be acessed in future.
I know that something needs to be done with Routing, but being new to MVC dont seems to get it. Please can some one guide me on this.
Add this route on top of your Default one:
routes.MapRoute(
"Blog",
"Blogs/{blogName}",
new { controller = "Blogs", action = "Index" }
);
Your controller will look like this:
public class BlogsController : Controller
{
public ActionResult Index(string blogName)
{
BlogModel model = // find blog by blog name
return View(model);
}
}
Also, one suggestion: Keep your controller names in singular mode: BlogController instead of BlogsController. Change URL and Routing accordingly if you decide to do so.

Creating custom RequestContext in ASP.NET MVC

I'm creating a CMS using ASP.NET MVC, and by design, I've decided that each plugin (add-on) should have a key in the incoming HTTP request. Thus, I have this general route in my host application:
{pluginKey}/{controller}/{action}/{id}
I've created a custom controller factory which implements IControllerFactory and of course, it has a method to create controllers base on the ReqeustContext and controller name. However, I want to create an artificial HttpContext (alongside all other relevant objects like HttpRequest, RequestContext, RouteData, etc.) so that controllers of plugins won't misinterpret these URL segments wrongly. In other words, I want to cut the first part of the incoming URL, and make plugins think that they're processing this URL:
{controller}/{action}/{id}
How can I achieve this?
While you could create a new implementation of all the context classes, it seems like a bit of overkill. Why not use a derived Route Handler that applies the filtering functionality before returning the HttpHandler? Here's an example:
// To avoid conflicts with similarly named controllers, I find it to be good practice
// to create a route constraint with the set of all plugin names. If you don't have
// this function already, you should be able to access it with reflection (one time
// per app lifecycle) or you hard-code them. The point is to have a regex which ensures
// only valid plugins will get selected
string[] pluginNames = GetPluginNames();
string pluginNameRegex = string.Join("|",pluginNames);
Route pluginRoute = new Route (
url: "{pluginKey}/{controller}/{action}/{id}",
defaults: null,
constraints: new RouteValueDictionary(new { pluginKey = pluginNameRegex }),
routeHandler: new PluginRouteHandler()
});
// The custom route handler can modify your route data after receiving the RequestContext
// and then send it to the appropriate location. Here's an example (markdown code/untested)
// Note: You don't have to inherit from MvcRouteHandler (you could just implement IRouteHandler
// but I'm assuming you want Mvc functionality as the fallback)
public class PluginRouteHandler : MvcRouteHandler
{
public PluginRouteHandler(IControllerFactory controllerFactory)
: base(controllerFactory)
{}
protected override IHttpHandler GetHttpHandler(RequestContext requestContext){
if(ValidatePluginRoute(requestContext))
{
// we are going to remove the pluginKey from the RequestContext, It's probably wise
// to go ahead and add it to HttpContext.Items, in case you need the data later
requestContext.HttpContext.Items["pluginKey"] = requestContext.RouteData.Values["pluginKey"];
// now let's get ride of it, so your controller factory will process the
// requestContext as you have described.
requestContext.Values.Remove("pluginKey");
// the route will now be interpreted as described so let the flow go to the MvcRouteHandler's method
}
return base.GetHttpHandler(requestContext);
}
static bool ValidatePluginRoute(RequestContext requestContext){
return requestContext.RouteData.ContainsKey("pluginKey");
}
}

ASP.NET MVC3 Nested Resources within an Area

I come from a Rails background and I'm having problems wrapping my head around Microsoft's MVC framework.
Today it's Routing. Rails gives you namespaces (e.g. Admin) which is the equivalent of Areas in .NET MVC3. Rails also allows you to define nested resources within your routes that will give you for example /posts/1/comments/1/edit and in your action you basically get params[:post_id] and params[:id].
I need something similar in ASP.NET MVC3 but not sure how to go about this. Googling for this results in at least 30 different ways to accomplish this and non of them mention areas.
It feels like I should add/modify something within here:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
But not sure where. Any suggestions?
I think you're in the right file (your AreaRegistration.cs file). I prefer being a little more explicit with my routes rather than using the default 'catch all' type of route that they provide. So here's an example of how I'd handle this:
Add something like this before the existing route (or get rid of the existing one all together) in the RegisterArea method
context.MapRoute(
"Edit_Comment",
"posts/{postId}/comments/{commentId}/edit",
new { controller = "Comment", action = "Edit" }
);
Then in your CommentController.cs you would have the following action:
public ActionResult Edit(int postId, int commentId)
{
// Do your edit logic then return an ActionResult
}

Resources