I have to create a site structure like this
/demo/
/demo/admin/create
/demo/admin/edit/id
I can create a DemoController which will contains admin. But how to show create/edit pages? The create/edit pages can be accessible only after user is logged in. Where to put create/edit pages?
If you are certain that you should implement strictly that URL strusture, then maybe "areas" solution would fit you (though not sure, just had a brief view). But I think, that for a small project you could simply make:
separate "admin" controller (that would lead to /demo, /admin/create, /admin/edit/id);
or you could possibly use custom ASP.NET Routing;
As for the authorization, you should look into ASP.NET Web Application Security and User authentication and authorisation in ASP.NET MVC
All you need to do is create a route for /demo/admin, then assign that route to a new controller called DemoAdminController. To make this only accessible to logged in users, you use the Windows Forms authentication system. A sample is provided with the default application generated by MVC.
i agree an area should do the trick or you can add a custom route that points the the controller if you want to lock down the whole section as an admin only section i think areas would be the way to go on this one
Pretty old question, Google landed me here right now.
There is also another way to reach the goal: the Route and RoutePrefix attribute.
Just a small chunk of code for reference.
[RoutePrefix("demo")]
public class DemoController : Controller
{
[Route("")]
public ActionResult Index() { } // default route: /demo
[Route("admin/create")]
public ActionResult Create() { } // /demo/admin/create
[Route("admin/edit/{id}")]
public ActionResult Edit(int id) { } // /demo/admin/edit/5
}
For this to work, the attribute routing must be enabled. In most case is enough to add:
routes.MapMvcAttributeRoutes();
in RouteConfig.cs.
Example:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Dashboard", action = "Index", id = UrlParameter.Optional }
);
}
I think you are looking to use Areas. See docs here:
http://msdn.microsoft.com/en-us/library/ee671793.aspx
Related
I have a work-around, but I'd really like to know why this doesn't appear to work in MVC. (.Net 4.6.1)
I have a controller which I want to use a RoutePrefix:
[RoutePrefix("entry")]
public class DefaultController : Controller
{
[HttpGet]
[Route(), Route("Index")]
public ActionResult Index()
{
// ...
}
}
In the route config:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "entry", action = "index", id = UrlParameter.Optional }
The issue is that with this configuration, running under local IIS to test, navigating to "localhost/testApp/entry" works, "localhost/testApp/entry/index" works, however the default "localhost/testApp/" results in a 404.
This has been doing my head in because on a fresh project with the default controllers and a default set to "home" and "index" the "localhost/testApp" would render Home/Index without an issue.
I narrowed it down to the RoutePrefix being the issue. If I remove the RoutePrefix and change the defaults to: new { controller = "default", action = "index", id = UrlParameter.Optional }
Then "localhost/testApp" works, but obviously this requires using /default for other routes rather than /entry.
Also, if I leave the prefix in (switching the default controller back to "entry") and add Route("~/") to the Index method, then "localhost/testApp" also works as according to the doco that a ~ route overrides the route prefix.
I'd like to know if there is an explanation why RoutePrefix doesn't seem to play nice with default routing? I'm fine with adding a ~/ route for that default action, but it seems I'm missing some understanding on how RoutePrefix is intended to be used.
Action attribute routing has the highest priority. If you use it only route attributes will be working, everything else will be ignored. You can to one action as many routes as you need.
Since you have 2 variants - Route() and Route("Index") it works only for 2 urls -"localhost/testApp/entry" and "localhost/testApp/entry/index".
if you remove Route() it will work only for one url-lcalhost/testApp/entry/index".
If you add 3rd Route("~/") it will work for 3rd url "localhost/testApp"
Sign ~ means that any prefixes should be ignored, it starts from root.
So you can not use default conventional routing on Index action since it is only obeys routing attributes.
Also, you have a controller [RoutePrefix("entry")] attribute routing too and it that next highest priority and because of this it overrides your convention routing in the config file. This is why default routing doesn't work for this controller and it doesn't go to Index automaticaly. To make default route work you need to remove route prefix and fix web config
defaults: new { controller = "default", action = "index", id = UrlParameter.Optional }
Current default controller = "entry" doesn't exist at all.
So you have two choices to have Index as default route action - remove all attribute routing and lost all another extra routes or add one more.
Thanks to Serge for helping point out a bad assumption I had about [RoutePrefix]. The problem here turns out that [RoutePrefix] is not a substitute name for a controller, (though that is how it behaves on the surface) but rather a prefix to each individual action. While the mapping in the URL will be identical:
Example 1:
public class EntryController
{
public ActionResult Index() { ... }
}
Example 2:
[RoutePrefix("Entry")]
public class DefaultController
{
[Route("Index")]
public ActionResult Index() { ... }
}
Both of these examples would resolve "localhost/testApp/entry/index", however only the first mapping would be considered as a match for {controller}/{action} and resolve a "defaults" mapping of "entry/index".
So if an action /w Attribute-based routing needs to be made a root default you need to explicitly declare it as the root using [Route()] if there is no [RoutePrefix], or [Route("~/")] if there is a [RoutePrefix]. since it won't be included in the {controller}/{action} routing. (Verified by removing the Default {controller}/{action} routing entirely).
I got simple web application with 1 default routing:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Game", action = "Index", id = UrlParameter.Optional }
);
My controller contains the following actions:
public class GameController : Controller
{
public ActionResult Index()
{
// some actions
return View();
}
[HttpPost]
public ActionResult CreateGame(Game game, User user)
{
// some actions
return View("Game");
}
[HttpPost]
public ActionResult JoinGame(User user)
{
// some actions
return View("Game");
}
}
Also under Views/Game folder I got "Index" and "Game" views.
But when I start application from time to time (NOT ALWAYS!) it requests
http://localhost:55815/Game/Game
instead of
http://localhost:55815 or http://localhost:55815/Game/Index
Your application default route is working fine.
The debugger starts url http://localhost:55815/Game/Game because the file Game.cshtml is currently opened in your Visual Studio.
The solution is on your VisualStudio project configuration.
Choose specific page with empty value instead of current/active Page.
Actually,
/Game/Game
and
/Game/Index
are both the same. When you check you default routing file you can see that your main controller is written with predefined controller and action. So the program automatically resolve this url according to that. Mostly try your web application not in visual studio debug but put it under IIS than if you need debugging, debug IIS instance.
Good luck
I'm newbie to MVC. I could integrate MVC 5.2 to my existing web forms Visual Studio 2012 Update 4 project. I created my first controller and all worked as expected. Even I was able to leverage the windows forms authentication from my existing project when accessing the MVC view. But when created my second controller it began messing up.
It is my route mapping:
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.EnableFriendlyUrls();
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
I have two controllers both located in ~/Controllers. My first controller is:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
//return View();
return Redirect("~/Default.aspx");
}
public ActionResult CloseSession()
{
return Redirect("http://www.yahoo.com");
}
}
The second controller:
public class CajaWebController : Controller
{
//
// GET: /CajaWeb/
public ActionResult Index()
{
return View();
}
public ActionResult CloseSession()
{
return Redirect("http://www.cnn.com");
}
}
I don't know is it relevant to the problem but I'll include how the MVC view is reached. My VS2012 start url is
http://localhost/Fortia/CajaWeb.
Fortia is my app name. Because I declared Web Forms authentication and
<location path="CajaWeb">
<system.web>
<authorization>
<allow roles="Fortia" />
<deny users="*" />
</authorization>
</system.web>
</location>
when starting to debug the old WebForms app authentication mechanism is called, the old WebForms login page invoked and after a successful login finally my CajaWebController, Index() action is called. Before creating CajaWebController it was the HomeController who was called, but I assume MVC now deduces the correct controller is CajaWeb because of the targeted url being
http://localhost/Fortia/CajaWeb.
The invoked view contains the following code:
<a href='#Url.Action("CloseSession", "CajaWeb")'>Close session</a>
The problem is when clicking the generated link the MVC calls HomeController.Index() action despite I explicitly set CajaWebController.CloseSession() in the #Url.Action...
I looked at the generated link and it looks wrong:
<a href='/Fortia/__FriendlyUrls_SwitchView?action=CloseSession&controller=CajaWeb'>
it encoded the parameter separator & into & But anyway I tried handcoding the href as
http://localhost/Fortia/__FriendlyUrls_SwitchView?action=CloseSession&controller=CajaWeb
but the result was the same.
What is wrong?
It would seem the ASP.NET Friendly Urls package you're using is interfering with the urls MVC is generating. The library seems to be meant for WebForms anyway
If it works without, then leave it like that as MVC's urls are already quite SEO-friendly when controller and action names are meaningful to their content.
I think the problem is that the route
http://localhost/Fortia/CajaWeb
doesn't match any routes so it ends up going to the default route specified in RouteConfig. You need to configure a route or create an area in your app called "Fortia".
I m trying to use subfolders within the Controllers folder:
The structure looks like this:
Controllers (Folder)
LoginController.cs
WelcomeController.cs
Settings (Folder)
UsersController.cs
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 }
);
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.
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/
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?"
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
A specific page in my web application has a URL www.example.com/Test/Index
However, I want to make this URL accessible when the user simply inputs www.example.com/Test instead of the whole thing.
So how can this be done using C# alone? Any help will be highly appreciated!
You can use a redirect. In ASP.NET this is Response.Redirect. In MVC this is RedirectToAction("Index"). This will cause their browser to then request the other URL.
If you want the URL to not be changed/redirected, and show www.example.com/Test, then you can use a server side redirect. ASP.NET: Server.Transfer. In MVC you can just return Index(); but this can be problematic sometimes. A better option is to use a default route:
public class MvcApplication : System.Web.HttpApplication
{
...
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Test", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
You should get this code automatically with any newly created MVC 3 application. you just have to custimize it for the controller you want to respect this default route.