i tried to split my module in areas
my modules are
1. Login
2. User Admin
3. Master
i created the area and my first start up page is (area = login, controller = login, action = loginpage)
My code route config codes are
RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional },
namespaces: new[] { "SRR.Areas.Login.Controllers" }
);
}
}
LoginAreaRegistration.cs
public override string AreaName
{
get
{
return "Login";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
//Trace.WriteLine("Login");
context.MapRoute(
"Login_default",
"{controller}/{action}/{id}",
new { controller = "Login", action = "Login", id = UrlParameter.Optional },
namespaces: new[] { "SRR.Areas.Login.Controllers" }
);
}
UserAdminAreaRegistration.cs
public class UserAdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "UserAdmin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"UserAdmin_default",
"UserAdmin/{controller}/{action}/{id}",
new { controller = "Menu", action = "MenuPrivilages", id = UrlParameter.Optional },
namespaces: new[] { "SRR.Areas.UserAdmin.Controllers" }
);
}
}
Here my starting page login is coming fine but the useradmin module is views are not showing it through the error has "The resource cannot be found".
Thanks in advance
Login area registration missies the area specification in the pattern definition; change "{controller}/{action}/{id}" to "Login/{controller}/{action}/{id}"
What happens if your remove the namespaces constraints from the route registrations? Are the controllers (all of them Login and UserAdmin) in correct namespaces? I guess the Login controllers are showing because of the missing area specification as i mentioned above and the Login* controllers aren't in the correct namespaces.
You RegisterRoutes method misses a line before the default route registration:
AreaRegistration.RegisterAllAreas();
The issue that I believe is you have the controller 'login' and action method 'login' twice, one in area and other in root. So that the namespace must differ but you have the similar namespace for both the routes which is wrong. By default it is not necessary to specify the namespace for the default route since it'll map the root files. This might be the reason why the application could not find which log in to call.
Related
I am new to MVC and editing an existing application. Currently I see the following in RouteConfig.cs:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Util",
"util/{action}",
new {controller = "util"});
routes.MapRoute(
"Catchall",
"{*url}",
new {controller = "Main", action = "CatchUrl"});
}
}
Inside the Main controller there is logic on that basically does a RedirectToRoute and sets the area, controller, action, and querystring called location to a certain value.
public class MainController : Controller
{
public ActionResult CatchUrl()
{
var agencyId = 9;
var routeValues = new RouteValueDictionary
{
{"area", "area1"},
{"controller", "dashboard"},
{"action", "index"},
{"location", "testLocation"}
};
return RedirectToRoute(routeValues );
}
}
This seems to work fine, when you give it an invalid area it correctly goes to the default one.
I also see a file called CustomAreaRegistration.cs:
public abstract class CustomAreaRegistration : AreaRegistration
{
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
AreaName + "Ajax",
AreaName + "/{controller}/{action}",
new { action = "Index" }
);
context.MapRoute(
AreaName + "ShortUrl",
AreaName + "/{controller}",
new {action = "Index"}
);
}
}
I am having trouble understanding how the Area routing works and how it knows how to go to the correct controller.
Furthermore, I am trying to get it so that when you visit
/{area}/ it does some logic and redircts you to the correct controller. Similar to how CatchUrl works
My attempt:
routes.MapRoute(
"AreaIndex",
"{module}/",
new {controller = "Main", action = "Index"});
MainController :
public class MainController : Controller
{
public ActionResult Index()
{
var requestHost = HttpContext.Request.Url?.Host;
var location= requestHost == "localhost" ? Request.QueryString["location"] : requestHost?.Split('.')[0];
var routeValues = new RouteValueDictionary
{
{"area", ControllerContext.RouteData.Values["module"]},
{"controller", "dashboard"},
{"action", "index"},
{"location", location}
};
return RedirectToRoute(routeValues );
}
public ActionResult CatchUrl()
{
var routeValues = new RouteValueDictionary
{
{"area", "area1"},
{"controller", "dashboard"},
{"action", "index"},
{"location", "testLocation"}
};
return RedirectToRoute(routeValues );
}
}
And I get
No route in the route table matches the supplied values.
I am not sure why CatchUrl works and mine does not.
I actually don't get what you're asking, but by just looking at the code, that's not the standard way to create/use Areas in MVC 3,4 and 5.
You shouldn't need to write logics inside each controller and do the redirects.
In my RouteConfig, I usually just have the default route mapping. And when you have the needs for Areas, you can right click on the MVC web project in Visual Studio and click'Add -> Area'. That will create a folder with the area name inside an Areas folder right under the root of the web project. And within the area folder, you should find the AreaRegistration.cs for the area name and mappings.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "dashboard", action = "index", id = UrlParameter.Optional },
namespaces: new[] { "Company.Project.Web.UI.Controllers" }
);
}
}
And let's say you want to create an area called 'Admin':
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"admin_default",
"admin/{controller}/{action}/{id}",
new { action = "index", id = UrlParameter.Optional },
namespaces: new[] { "Company.Project.Web.UI.Areas.Admin.Controllers" }
);
}
}
Lastly, I think a screenshot might be helpful.
-- updates --
Based on the comments, if you want the route /apple?location=home to go to Apple Controller and its Home method, while the route /orange?location=dashbard to go to Orange Controller and its Dashboard method, it's better to define a route in RouteConfig.
Ideally you wish you can have something like this in RouteConfig:
routes.MapRoute(
name: "Area-CatchAll",
url: "{area}?location={controller}"
);
But that's not possible as MVC will error out saying "The route URL cannot start with a '/' or '~' character and it cannot contain a '?' character.".
Instead, you can direct the traffic to a controller and you can define the area and location as parameters.
routes.MapRoute(
name: "Area-CatchAll",
url: "{area}",
defaults: new { controller = "Area", action = "Translate" }
);
public class AreaController : Controller
{
// The {area} from route template will be mapped to this area parameter.
// The location query string will be mapped to this location parameter.
public ActionResult Translate(string area, string location)
{
// This also assumes you have defined "index" method and
// "dashboard" controller in each area.
if (String.IsNullOrEmpty(location))
{
location = "dashboard";
}
return RedirectToAction("index", location, new { area = area });
}
}
Again, I wouldn't create route like that to redirect traffic to areas, if I don't have to.
I am trying to get it so that when you visit
/{area}/ it does some logic and redircts you to the correct controller. Similar to how CatchUrl works
First of all, lets be clear about this. The catch-all route is not routing it is redirecting. It is issuing an HTTP 302 response to the browser, which tells the browser to lookup a new location on your server. This sort of thing is not very efficient because it requires an extra round trip across the network, but it is the only way to get the URL in the browser to change from the server side (if that is what you intend to do).
Routing, on the other hand, is taking the initial request and sending it directly to a specific resource (controller action) without the extra round trip across the network and without changing the URL in the browser.
Your routes are already setup to use
/Area/Controller
to route to the correct controller here:
public abstract class CustomAreaRegistration : AreaRegistration
{
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
AreaName + "Ajax",
AreaName + "/{controller}/{action}",
new { action = "Index" }
);
context.MapRoute(
AreaName + "ShortUrl",
AreaName + "/{controller}",
new {action = "Index"}
);
}
}
(assuming you have a subclass of this for each area that sets the AreaName).
If you indeed want to route (not redirect) the /module/ URL to the DashboardController.Index method in the corresponding Area, you change your ShortUrl to make the controller optional and default it to the DashboardController.
public abstract class CustomAreaRegistration : AreaRegistration
{
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
AreaName + "Ajax",
AreaName + "/{controller}/{action}",
new { action = "Index" }
);
context.MapRoute(
AreaName + "ShortUrl",
AreaName + "/{controller}",
new { controller = "Dashboard", action = "Index" }
);
}
}
This will send the URL /Foo/ to the Foo area's DashboardController.Index method, but send the URL /Foo/Bar/ to the Foo area's BarController.Index method.
The above assumes that you are diligent enough to ensure that none of your AreaNames are the same as controller names in the non-area part of your project. If they are, you will never be able to reach those non-area controllers without some extra configuration such as a route constraint.
The MainController.Index method isn't needed at all (unless you have some specific reason why you want to change the URL in the browser).
Scenario:
I have 3 Areas named- Albums, Singers , Music
Now each of these areas have controllers with same name. For instance every area has LoginController.
Now currently I am getting following exception
Multiple types were found that match the controller named 'Login'
This can happen if the route that services this request does not specify namespaces to search for a controller that matches the request.
If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
This is auto generated Configuration by Visual Studio on Area Creation
public override string AreaName
{
get
{
return "Albums"
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Albums_Default"
"Client/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
This is my intial configuration in RoutesConfig.cs
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Application_Name" }
);
Now how to configure the routes that without any modification in url, the desired view is rendered.
Please try this one :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL
new { controller = "Home", action = "Index", id = "" }, // Defaults
new[]{"AreasDemoWeb.Controllers"} // Namespaces
);
}
Help Link 1
Help Link2
I have an Area named Admin with a model named CMSPage. My controller is named CMSPagesController. I would like to create a custom route so I can use simply Page instead of CMSPage, so I thought by creating the following custom route, it would work but nope:
routes.MapRoute(
"AdminPages",
"Admin/Pages/{action}/{id}",
new { controller = "CMSPages", action = "Index", id = UrlParameter.Optional }
);
Could someone please lead me in the right direction?
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication1
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Blog", // Route name
"Archive/{entryDate}", // URL with parameters
new { controller = "Archive", action = "Entry" } // Parameter defaults
);
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);
}
}
}
The order of the routes that you add to the route table is important. Our new custom Blog route is added before the existing Default route. If you reversed the order, then the Default route always will get called instead of the custom route.
The custom Blog route matches any request that starts with /Archive/. So, it matches all of the following URLs:
/Archive/12-25-2009
/Archive/10-6-2004
/Archive/apple
The custom route maps the incoming request to a controller named Archive and invokes the Entry() action. When the Entry() method is called, the entry date is passed as a parameter named entryDate.
Oops on my part. I forgot that there was the area registration process that happens as well. The issue is that I want to access this controller from the area in which I created it (Admin). So, the custom route registration has to happen there. Not in my RouteConfig.cs (see below). Thanks for the response Neeraj, you're answer is not wrong, just not correct for my question which was in regards to an area.
using System.Web.Mvc;
namespace WebApplication1.Areas.Admin
{
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
// This is where the custom route has to be registered for me to access
// it from my area.
context.MapRoute(
"Admin_pages",
"Admin/Pages/{action}/{id}",
new { action = "Index",
controller = "CMSPages",
id = UrlParameter.Optional }
);
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
}
I have a Contacts controller in the main/top area, and I have an area named "Contacts".
I get POST 404s to the Contacts controller if I register my areas before I register my top-level routes:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.DefaultBinder = new NullStringBinder();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
And, if I register my areas after my routes, my 404s to the Contacts controller goes away, but my routes to the Contacts area are now 404s.
...lots of duplicate controller name questions logged, but I haven't found a specific scenario where the area is the same name as the controller.
...probably an easy fix. Would appreciate help. :-D
fwiw, I am registering my Contacts area with an explicit namespace:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MyMvcApplication.Controllers" }
);
}
There are two things to consider
In Application_Start() method register areas first AreaRegistration.RegisterAllAreas();.
In case of conflicting name, use the namespaces in RouteConfig.cs file of App_Start folder as well as all the routes defined in routes (like ContactsAreaRegistration.cs)
To replicate your scenario, I created a sample application and able to access successfully both URLs given below:
http://localhost:1200/Contacts/Index
http://localhost:1200/Contacts/contacts/Index
The structure of my application looks like:
Here inside ContactsAreaRegistration.cs file we are having following code:
public class ContactsAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Contacts";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Contacts_default",
"Contacts/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.Contacts.Controllers" }
);
}
}
Hope it will help you. If you need I can send sample application code which I have created. Thanks.
For MVC5, I did what #Snesh did but that didn't fully work. It would only resolve the controllers in my area but not in the root of the project if they had the same name. I wound up having to specify the namespaces as parameters in both the RegisterArea method and the RegisterRoutes method in my RouteConfig.cs.
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// This resolves to the Controllers folder at the root of the web project
namespaces: new [] { typeof(Controllers.HomeController).Namespace }
);
}
AreaRegistration.cs
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Handheld_default",
"Handheld/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { typeof(Areas.Handheld.Controllers.HomeController).Namespace }
);
}
I'm quite stuck I might say dispite all other posts found on the site.
My solution has two areas Front and Back, and I don't want to use the default root controllers and views provided by default.
My FrontAreaRegistration.cs is like :
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Front",
"Front/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
, new[] { "Show.Areas.Front.Controllers" }
);
}
My BackAreaRegistration.cs is like :
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Back_default",
"Back/{controller}/{action}/{id}",
new { controller = "Account", action = "LogOn", id = UrlParameter.Optional }
, new[] { "Show.Areas.Back.Controllers" }
);
}
And Global.asax like :
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Getting folowing exception :
Multiple types were found that match the controller named 'Home'. This
can happen if the route that services this request
('{controller}/{action}/{id}') does not specify namespaces to search
for a controller that matches the request. If this is the case,
register this route by calling an overload of the 'MapRoute' method
that takes a 'namespaces' parameter.
The request for 'Home' has found the following matching controllers:
Show.Areas.Front.Controllers.HomeController
Show.Areas.Back.Controllers.HomeController
Problem is I can't reach the Home controller from Front area. Even if correct namespace added to context.MapRoute method overload ...
Any help will be appreciated.
The error is raised because you don't specify Area name in your request. Because of that "Default" route (from Global.asax) is matched for request and tries to search "Index" action of "Home" controller. As far as there two matches (for both areas) exceptions is thrown.
There are few ways to fix this. One possible is to modify Global.asax:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new[] { "Show.Areas.Front.Controllers" }
).DataTokens.Add("Area", "Front");
But in this case "Default" route will work for Front area only.