ASP.NET MVC Custom View Routing
If I understood your question correctly, you can create your own view engine which resolves view location at runtime and plug into your application.
Create your own custom view engine.
public class MyViewEngine : RazorViewEngine
{
public MyViewEngine()
: base()
{
ViewLocationFormats = new[] {
"~/Views/{1}/%1/{0}.cshtml",
"~/Views/{1}/%1/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
PartialViewLocationFormats = new[] {
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var catagoryName = controllerContext.RouteData.Values["category"].ToString();
return base.CreatePartialView(controllerContext, partialPath.Replace("%1", catagoryName));
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var catagoryName = controllerContext.RouteData.Values["category"].ToString();
return base.CreateView(controllerContext, viewPath.Replace("%1", catagoryName),masterPath);
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
var catagoryName = controllerContext.RouteData.Values["category"].ToString();
return base.FileExists(controllerContext, virtualPath.Replace("%1", catagoryName));
}
}
And register it here
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//Register your View Engine Here.
ViewEngines.Engines.Add(new MyViewEngine());
}
Update route config, default should be
routes.MapRoute(
name: "Default",
url: "{controller}/{category}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", category = "DefaultCategoryName", id = UrlParameter.Optional }
);
TtT23
Updated on June 15, 2022Comments
-
TtT23 almost 2 years
Our solution hierarchy is as follows:
Controller\Category\View
Ex: Controllers\DataAnalysis\DataRetrieve
Now I'd like to map the routing so that when the user just types the name of the view in the url, it automatically maps the url to the corresponding controller
I.E: localhost:1234\DataAnalysis\DataRetrieve
Should map to
View\DataAnalysis\DataRetrieve\Index.cshtml
Similarly, any url requests including the action should retrieve the corresponding view
I.E: localhost:1234\DataAnalysis\DataRetrieve\TestAction
Should map to
View\DataAnalysis\DataRetrieve\TestAction.cshtml
Currently, we're using the default routing
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional } ); }
Which means if I type the URL shown above, it ignores the category and fails to return the view.
Is there a way to customize routing to get the behavior above?
--Edit
To clarify my question based on the comments, here's a screenshot of my solution explorer
Now if I call localhost:12346/DataAnalysis/DataRetrieve, this should take me to the index. Routing this isn't a problem as I can do something like this:
routes.MapRoute( name: "ExampleRouting", url: "{category}/{controller}/{action}" );
But here's the issue. I'd also like to organize my file structure like this:
By default, when I try to retrieve the index of dataretrieve, it looks under Views\DataRetrieve\Index not Views\DataAnalysis\DataRetrieve\Index.
How can I change this behavior?
Edit2------------------------
Based on the answer, I've added a custom view engine, registered it in Application_Start, updated my routing. Still having an identical issue.
Global.asax
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); ViewEngines.Engines.Add(new SPCViewEngine()); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); } }
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{category}/{action}/{id}", defaults: new { controller = "Login", action = "Index", category = "Login", id = UrlParameter.Optional } ); }
SPCViewEngine (Custom View Engine)
public class SPCViewEngine : RazorViewEngine { public SPCViewEngine() : base() { ViewLocationFormats = new[] { "~/Views/{1}/%1/{0}.cshtml", "~/Views/{1}/%1/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; PartialViewLocationFormats = new[] { "~/Views/%1/{1}/{0}.cshtml", "~/Views/%1/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { var categoryName = controllerContext.RouteData.Values["category"].ToString(); return base.CreatePartialView(controllerContext, partialPath.Replace("%1", categoryName)); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { var categoryName = controllerContext.RouteData.Values["category"].ToString(); return base.CreateView(controllerContext, viewPath.Replace("%1", categoryName), masterPath); } protected override bool FileExists(ControllerContext controllerContext, string virtualPath) { var categoryName = controllerContext.RouteData.Values["category"].ToString(); return base.FileExists(controllerContext, virtualPath.Replace("%1", categoryName)); } }
-
TtT23 over 10 yearsYour answer seems like the right way to do it, but for some reason, it's still not working. Requesting localhost/DataAnalysis/DataRetrieve results in 404 and none of the override methods in the customized viewengine are being invoked when I put debug points on them. However, When I try localhost:12346/DataRetrieve/TestAction without the category in front, it works. Edit: Never mind, the latter part doesn't work either.
-
ssilas777 over 10 yearsDid you register the view engine in application start also update the route config? I tried this in my localhost it works for me also debug point should hit on every request.
-
TtT23 over 10 yearsYes. I'll edit my question with the updated content momentarily.
-
TtT23 over 10 yearsWow.. just calling ViewEngines.Engines.Clear() solved the issue.. I guess it was still using the default viewengine. Many thanks!