title: ASP.NET MVC Custom View Routing
body: <p>Our solution hierarchy is as follows:</p>
  
  <p>Controller\Category\View</p>
  
  <p>Ex:
  Controllers\DataAnalysis\DataRetrieve</p>
  
  <p>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</p>
  
  <p>I.E: localhost:1234\DataAnalysis\DataRetrieve</p>
  
  <p>Should map to</p>
  
  <p>View\DataAnalysis\DataRetrieve\Index.cshtml</p>
  
  <p>Similarly, any url requests including the action should retrieve the corresponding view</p>
  
  <p>I.E: localhost:1234\DataAnalysis\DataRetrieve\TestAction</p>
  
  <p>Should map to</p>
  
  <p>View\DataAnalysis\DataRetrieve\TestAction.cshtml</p>
  
  <p>Currently, we're using the default routing</p>
  
  <pre><code>    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 }
          );
      }
  </code></pre>
  
  <p>Which means if I type the URL shown above, it ignores the category and fails to return the view.</p>
  
  <p>Is there a way to customize routing to get the behavior above?</p>
  
  <p>--Edit</p>
  
  <p>To clarify my question based on the comments, here's a screenshot of my solution explorer</p>
  
  <p><img src="https://i.stack.imgur.com/eDOcv.png" alt="enter image description here"></p>
  
  <p>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:</p>
  
  <pre><code>        routes.MapRoute(
              name: "ExampleRouting",
              url: "{category}/{controller}/{action}"
          );
  </code></pre>
  
  <p>But here's the issue. I'd also like to organize my file structure like this:</p>
  
  <p><img src="https://i.stack.imgur.com/Be7dr.png" alt="enter image description here"></p>
  
  <p>By default, when I try to retrieve the index of dataretrieve, it looks under Views\DataRetrieve\Index not Views\DataAnalysis\DataRetrieve\Index.</p>
  
  <p>How can I change this behavior?</p>
  
  <p>Edit2------------------------</p>
  
  <p>Based on the answer, I've added a custom view engine, registered it in Application_Start, updated my routing. Still having an identical issue.</p>
  
  <p>Global.asax</p>
  
  <pre><code>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();
      }
  }
  </code></pre>
  
  <p>RouteConfig.cs</p>
  
  <pre><code>    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 }
          );
      }
  </code></pre>
  
  <p>SPCViewEngine (Custom View Engine)</p>
  
  <pre><code>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));
      }
  }
  </code></pre>