So I have different Areas in my MVC application, and I want to define some of their routes using Attributes and some other ones with MapRoute:
Here's what is RouteConfig file:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Dashboard_route",
url: "Dashboard/{action}",
defaults: new {controller = "Dashboard", action = "Index"}
);
}
When I use Attributes ([RouteArea("someRoute")] and [Route]) everything is fine, but when it comes to MapRoute, the controllers couldn't find my Views folder under Areas Directory and I am getting this error:
The view 'getdashboard' or its master was not found or no view engine supports
the searched locations. The following locations were searched:
~/Views/Dashboard/getdashboard.aspx
~/Views/Dashboard/getdashboard.ascx
~/Views/Shared/getdashboard.aspx
~/Views/Shared/getdashboard.ascx
~/Views/Dashboard/getdashboard.cshtml
~/Views/Dashboard/getdashboard.vbhtml
~/Views/Shared/getdashboard.cshtml
~/Views/Shared/getdashboard.vbhtml
Is there anyway to define the Views directory using MapRoute?
Normally, when you create an Area there is an AreaRegistration class inside of the /Areas/<Area Name>/ directory where you register its routes.
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 }
);
}
}
And then from your Application_Start() method, you need to ensure you call AreaRegistration.RegisterAllAreas() so this registration code gets executed.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
Once it is set up that way, it looks for the views in the /Areas/<Area Name>/Views/ directory.
Related
I already have controllers and views in folders under the project name. I added an Area folder and then an area inside it and called it Home and then moved my controller and index view into it. But when I connect to the index I get an error and it looks like the path where it's looking for the index is the old path, how do I change this to the new path?
Here is what I created
In 'HomeAreaRegistration' I see this under RegstrationArea
public class HomeAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Home";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Home_default",
"Home/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
But when I run the application in IE, here is what I see in the browser! It looks like it's looking for the index.cshtml in the old path location, not the new path location in the new area 'Home'
It looks like the route engine is looking in the wrong location. So here is what my RouteConfig.cs file looks like.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Even when I try 'https://localhost:44301/Home/Index.cshtml' it throws a HTTP 404 error.
The 404 error shows the main problem itself: default routing and view engine search cannot found default Index.cshtml view file in Views directory on your project (i.e. ProjectName/Views/Index.cshtml pointed by route ~/Views/Home/Index).
First, create a class to include view location search for your custom area like this example:
public class CustomView : RazorViewEngine
{
public CustomView()
{
MasterLocationFormats: new[]
{
"~/Areas/Home/Views/{0}.cshtml",
"~/Areas/Home/Views/{1}/{0}.cshtml"
}
ViewLocationFormats: new[]
{
"~/Areas/Home/Views/{0}.cshtml",
"~/Areas/Home/Views/{1}/{0}.cshtml"
}
PartialViewLocationFormats = ViewLocationFormats;
FileExtensions = new[]
{
"cshtml"
};
}
}
Then, include all areas and your custom view engine into Global.asax:
protected void Application_Start()
{
// register all area locations
AreaRegistration.RegisterAllAreas();
// clear default view engine
ViewEngines.Engines.Clear();
// add your custom view engine here
// the custom view engine should loaded before default view engine (e.g. Razor)
ViewEngines.Engines.Add(new CustomView());
ViewEngines.Engines.Add(new RazorViewEngine());
}
If you have RouteConfig class on App_Start directory, make sure RegisterAllAreas has included before default route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Additionally, add namespace of the controller name when required or the above solution still doesn't work:
public class HomeAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Home";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Home_default",
"Home/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "ProjectName.Areas.Home.Controllers" }
);
}
}
NB: Create Home directory under Views if you want to follow route convension ~/Areas/Views/Home/Index, and put Index.cshtml file into it.
References:
How to set a Default Route (To an Area) in MVC
How to register areas for routing
your area folder structure look like this
Reister your area in Global.asax
AreaRegistration.RegisterAllAreas();
and try with this url
http://localhost:44301/Home/Home/Index
you have to do below corrections in your solution:
1) Add a Folder Home in Views and place index.cshtml in it.
Folder structure for view must be: Home(Area name) > Views > Home (same name as controller) > index.cshtml (as shown in picture)
2)change namespace of your Homecontroller to (Solution name).Areas.Home.Controllers
3)Also you have to refer following route pattern for area:
localhost/AreaName/Controller/Action
which in your case:
https://localhost:44301/Home/Home/Index
Hope this might solve your problem
I'm facing an issue with Web API 2 routing for literal segments.
In one project, I have ASP.NET MVC and WebAPI2 running together, the project is also running MVC areas.
Under each area, there is API folder which contains APIs. I'm facing an issue when trying to request the following url:
{host}/accesscontrol/api/reporting/bookings.
accesscontrol here is the area name
reporting is the controller
bookings is a literal segment.
The error I'm getting:
No action was found on the controller 'Reporting' that matches the request.
This is the controller that should receive this request:
[RoutePrefix("accesscontrol/api/reporting")]
public class ReportingController : ApiController
{
[Route("bookings")]
[ResponseType(typeof(Booking))]
[HttpGet]
public async Task<IHttpActionResult> Bookings(string q = null)
{
//Code to get data
return Ok(bookings);
}
}
When I remove [Route('Bookings')] attribute, the request is working well regardless if Bookings segment is there or not.
This is the configuration of routing under area registration class:
public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
"AccessControlApi_default",
"accesscontrol/api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
context.MapRoute(
"AccessControl_default",
"accesscontrol/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
How can I let the app to understand those literal segments under areas?
Edit
I'm calling RegisterAllAreas in Global.asax.cs file, as follow:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
I managed to make it work just add action after your controller in your MapHttpRoute and change [Route("bookings")] attribute to [ActionName("bookings")] and everything works.
context.Routes.MapMvcAttributeRoutes();
context.Routes.MapHttpRoute(
"AccessControlApi_default",
"Accesscontrol/api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional});
I hope this will help you out.
I resolved my issue by moving: AreaRegistration.RegisterAllAreas(); from global.asax.cs file to RouteConfig.cs file.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] {"WebPortal.Controllers"}
);
}
}
Let's say I have an area named Admin in my MVC application. This area has a HomeController with an Index action like so:
using System.Web.Mvc;
namespace AreasPractice.Areas.Admin.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return Content("Admin.HomeController");
}
}
}
The area registration for the Admin area has the following defaults for the area related route:
using System.Web.Mvc;
namespace AreasPractice.Areas.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 { controller = "Home", action = "Index", id = UrlParameter.Optional }
//, new[] { "AreasPractice.Areas.Admin.Controllers" }
);
}
}
}
My application's root area also has a HomeController with an Index action and a route config like so:
using System.Web.Mvc;
namespace AreasPractice.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return Content("From home controller of the root");
}
}
}
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 = "Home", action = "Index", id = UrlParameter.Optional }
, namespaces: new[] { "AreasPractice.Controllers" }
);
}
}
Now, I add an AdminController with an Index action in the root area of my application like so:
using System.Web.Mvc;
namespace AreasPractice.Controllers
{
public class AdminController : Controller
{
public ActionResult Index()
{
return Content("Root -> Admin Controller -> Index action.");
}
}
}
When I run the application, I am expecting to see something interesting, like, may be an exception to the effect, "I can't figure out what route you want."
But when I run the application, it runs just fine and a request for:
/Admin/
yields to the HomeController in the Admin area.
This is because, apparently, and as I recall, the routing mechanism works on a priority basis. It finds the first match and goes with it.
And it is apparently finding that the Admin_default route satisfies the request pattern even before it applies the Default route.
My question(s):
Is my understanding so far correct?
What do I do to play with it? What if I want it to go to the
AdminController in the root area of the application?
Okay, I got the answer after some more thinking. If I just moved the area registration in the global.asax file to after the default route has been registered, it now goes to the AdminController of my root instead of going to the Admin area's HomeController.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Remove from here
// AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Put here after the default route has been
// registered via the RouteConfig.RegisterRoutes
// call above
AreaRegistration.RegisterAllAreas();
}
}
I'm currently playing around with areas and routing within them. What I'm trying to achieve is to be able to have a URL that looks like this;
PracticeAdmin/Practice/[Practice Name]
which I would then be able to add things like Edit and Delete to the end of.
I have achieved this in the past when not working with areas by adding this annotation to the action
[Route("PracticeAdmin/Practices/{practiceName}")]
public ActionResult Details(string practiceName)
this would produce the URLs that I would like. The problem I am having is that when I am trying to do this when using areas I get links that look like this;
PracticeAdmin/Practices?practiceName=Practice1
which is not what I am looking for.
The code that I am using to try and produce this with is
PracticeAdminAreaRegistration.cs
using System.Web.Mvc;
namespace TrainingPortal.Areas.PracticeAdmin
{
public class PracticeAdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "PracticeAdmin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"PracticeAdmin_default",
"PracticeAdmin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "TrainingPortal.Areas.PracticeAdmin.Controllers" }
);
}
}
}
RouteConfig.cs
namespace TrainingPortal
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TrainingPortal.Controllers" }
);
}
}
}
I have called MapMvcAttributeRoutes here which I believe should mean that the routes are registered even within areas. I have also tried putting the necessary code within PracticeAdminAreaRegistration to do the same thing with no effect.
PracticeAdminController.cs
namespace TrainingPortal.Areas.PracticeAdmin.Controllers
{
public partial class PracticesController : Controller
{
private TpContext db = new TpContext();
// GET: PracticeAdmin/Practices
public virtual ActionResult Index()
{
return View(db.Practices.ToList());
}
[Route("PracticeAdmin/Practice/{practiceName}")]
public virtual ActionResult Details(string practiceName)
{
if (string.IsNullOrWhiteSpace(practiceName))
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Practice practice = db.Practices.FirstOrDefault(m => m.PracticeName.ToLower() == practiceName);
if (practice == null)
{
return HttpNotFound();
}
return View(practice);
}
...
Obviously it carries on with other methods but they all follow the same approach as this one.
Index.cshtml snippet
#Html.ActionLink("Delete", MVC.PracticeAdmin.Practices.Delete(item.PracticeName))
#Html.ActionLink("Delete2", "Delete", new { practiceName = item.PracticeName })
Within PracticeAdminArea/Views/Practices/Index.cshtml I have tried using both T4MVC and the normal ActionLink approach which generate exactly the same link (unsurprisingly).
Summary
I have no idea why the Routes I have specified don't appear when trying to create an ActionLink in an area, so I was wondering whether anyone is able to point me in the direction of how I would be able to fix this and get the URL to look how I would like it to?
After a bit of playing around I managed to get it working. The way that I ended up fixing it was by calling AreaRegistration.RegisterAllAreas() from the RouteConfig.RegisterRoutes() after having called MapMvcAttributeRoutes()
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "TrainingPortal.Controllers" }
);
}
}
As you're not allowed to call this method twice (or ASP.NET gets rather upset with you having registered the same route names twice) I removed the call to AreaRegistration.RegisterAllAreas() from Global.asax leaving it looking like this;
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Unfortunately this alone didn't solve the problem, I also had to make a couple modifications to the Controller as well. The modifications were to add the RoutePrefix and RouteArea attributes to the Controller like this;
[RouteArea("PracticeAdmin")]
[RoutePrefix("Practice")]
public partial class PracticesController : Controller
{
This had the added benefit that when specifying the route for a particular action through the Route attribute you didn't have to specify those parts any more, so originally an action's signature would have looked like this;
// GET: PracticeAdmin/Practices/{practiceName}/Members
[Route("PracticeAdmin/Practices/{practiceName}/Members")]
public virtual ActionResult Members(string practiceName)
{
it would now look like this;
// GET: PracticeAdmin/Practices/{practiceName}/Members
[Route("{practiceName}/Members")]
public virtual ActionResult Members(string practiceName)
{
After making all those changes, the website is behaving as expected.
I have an MVC 4 web application which consists some areas. I have a problem with the routing rules of an area named "Catalog". The RouteConfig.cs file is:
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 },
);
}
and Global.asax as follows:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
And CatalogAreaRegistration is something like this:
public class CatalogAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Catalog";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Catalog_default",
"Catalog/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
The problem is when i debug, RouteCollection routes does not include rules that are defined in the area. I used routedebugger and saw that routes collection does not consists rules of "Catalog" area. It has only rules in the RouteConfig.
I have no idea what is the problem. Thanks in advance.
I think due to Visual Studio's caching, some of the dll's aren't compiled properly and this situation can happen. If you do, delete all temp files from following locations:
C:\Temp
C:\Users\%Username%\AppData\Local\Microsoft\VisualStudio
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files
Path\To\Your\Project\obj\Debug
Update :
AppData\Local\Temp\Temporary ASP.NET Files
Then restart the Visual Studio. This is how i resolved.
Just add the namespace of your controllers to the AreaRegistration:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
**namespaces: new string[] { "Web.Admin.Controllers" }**
);
}