Stan92
Stan92

Reputation: 453

Asp.net MVC routing using subfolders

I m trying to use subfolders within the Controllers folder:

The structure looks like this:

I've several problems. When I perform a return RedirectToAction("Index", "welcome") from my LoginController, the url looks like http://mywebsite.local/settings/welcome I thought I will get a 404 error..

How to make the redirection launches http://mywebsite.local/welcome and get a 404 error when I launch http://mywebsite.local/settings/welcome

Do I really need to use Areas?

This is my RouteConfig.cs

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            name: "settings",
            url: "settings/{controller}/{action}/{id}",
            defaults: new { controller = "Users", action = "Index", id = UrlParameter.Optional }
        );            

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional }
        );            

Upvotes: 1

Views: 3940

Answers (4)

MikeTeeVee
MikeTeeVee

Reputation: 19392

Area Hype:
I wouldn't recommend applying Areas just because you can.
That would be like "giving a man a hammer and suddenly everything's a nail".
In this case we have a cool feature called "Areas" and (without knowing the underlying architecture)
   others recommended it the instant someone asks about sub-folders.
Areas weren't designed for the sole purpose of giving you an extra Route Parameter.
The "Areas" original intent is to give you a clear SoC (Separation of Concerns).

For Example:
Say you have two separate web application experiences and have decided to roll them under one Solution.
Your call-center may need to look up detailed information and enter in data on individual customers,
   while your managers and executives will peruse higher-level reporting and rarely enter in data.
In this scenario it may make sense to split your business logic into "Reporting" and "CallCenter" Areas.

Sub-Directories:
In MVC, the Sub-Folders you use under "Controllers" are Ignored when it comes to Routing.
SubFolders are perfectly fine with how the Questioner is using them to organize his Controllers.
Adding a SubFolder name to his URL makes for a more human-readable URL too.
He just made a mistake in the exclusivity of his first Mapping Route.
The problem is it was matching on everything.
Just because you have "settings/" in your MapRoute doesn't mean it will apply only to incoming URL's.
MVC will use your MapRoute Logic to figure how you would like to write your URL's too!

The Fix (Option 1 - Use MapRoute):

routes.MapRoute(
    name: "Settings",
    url: "Settings/{controller}/{action}/{id}",
    defaults: new { action = "Index", id = UrlParameter.Optional },//Remove the Default Controller as we want to explicitly require it and constrain it. - 08/26/2018 - MCR.
    constraints: new { controller = "Users|Admins" }//If you have other Controllers under a SubFolder (say we also had AdminsController.cs), then simply Pipe-Delimit them. - 08/26/2018 - MCR.
);

This is the way I've chosen to go, but if you want tighter control, you could use the Option below instead.

The Fix (Option 2 - Use Route-Attributes):
First, make sure you enable this feature by adding routes.MapMvcAttributeRoutes():

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapMvcAttributeRoutes();//Add this BEFORE MapRoute for Route-Attributes to work.

Next, Decorate your Controller with the following Attributes:
   (You may even include Default/Optional Values, just like in your MapRoute.)

[RoutePrefix("Settings/Users")]//Add your ParentFolder and Include your Controller name too.
[Route("{action=Index}/{id?}")]//You need this if you are using the RoutePrefix Attribute.  Without this, you will need to define "[Route]" above every Action-Method. - 08/26/2018 - MCR.
public class UsersController : Controller


Note:
If you use Route-Attributes instead of MapRoute, then you will not be able to hit the
   Controller without the "Settings" Prefix.
With the Custom and Default MapRoutes, you could have accessed your controller either way.
By decorating your Controller with these Attributes, you now force it to only use this exact path.
This may be what you want, but if you start IIS Express from Visual Studio on one of your Views,
   then it will not find it because Visual Studio does not know to add the RoutePrefix for you.
I say this, so you are not discouraged when you start debugging and think it doesn't work.

See this link for more information about Attribute-Routing:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/

Upvotes: 2

Hanny Setiawan
Hanny Setiawan

Reputation: 501

Looks like the answer not answering the question, because IMHO, we use Area when we need mini program under our main program,

ie. Forum, (Comprehensive) Blog, Marketplace (under our main site) either forum.mainsite.com or mainsite.com/forum

So you DONT need Area in your case

Solutions :

FYI, routing are something that have nothing to do with your architecture / structure / foldering in your applications.

In Example your ControllerName is SettingsUsersController

routes.MapRoute(
    name: "settings",
    url: "settings/users/{action}/{id}",
    defaults: new { controller = "SettingsUsers", action = "Index", id = UrlParameter.Optional }
);

in your case, you can fix your routing like this (this is for making sure you have pretty url but still simple Controller structure):

routes.MapRoute(
    name: "settings",
    url: "settings/{controller}/{action}/{id}",
    defaults: new { controller = "SettingsUsers", action = "Index", id = UrlParameter.Optional }
);

Your ControllerName would be SettingsUsersController

SettingsUsersController.cs

public class SettingsUsersController : Controller
{
    public ActionResult Index()
    {
        return View("~/Views/SettingsUsers/Index.cshtml", db.YourDBName.ToList());
    }
}

And why 404? I think because you are not "routing" correctly your View, you should do make subfolder like this under your Views Folder Views/SettingsUsers/Index.cshtml

Upvotes: 0

shannon
shannon

Reputation: 8784

The folder structure of your controllers here has very little relevance to what you are seeing. You could maintain that structure and accomplish your goal if the route would work.

However, you've specified two matching routes that can be used to encode the same action, and it is simply giving the first one priority. See, routes don't just work forwards, MVC uses them in reverse, too, to process your ____Action() statements. You could specify a named route (e.g. "settings") in your RedirectToAction("Index", "welcome"), by using RedirectToRoute instead, or manually specify all the routes that have prefixes in your route table. but why start your project off with intentionally conflicting routes like this?

As Joe R said, Areas will make your life easier. Why? Mainly because there is an additional built-in route parameter to do exactly what you want. Really, the question becomes "why avoid Areas?"

Upvotes: 1

Joe Ratzer
Joe Ratzer

Reputation: 18549

Do I really need to use Areas?

No, but you're trying to re-invent the wheel - creating a structure a bit like Areas. I'd recommend you go with Areas, it will make your life easier.

Upvotes: 7

Related Questions