I've reorganized my project into a more logical hierarchy:
-Controllers
-Accounts
-CustomersController
-Setup
-SystemDefaultsController
-SettingsController
-HomeController
At the moment, I'm just trying to set up my URLs to match this structure. So valid example URLs would be:
localhost:1234/Home/Index
localhost:1234/Setup/SystemDefaults/Index
localhost:1234/Setup/Settings/Index
localhost:1234/CustomerAccounts/Index
So I added a route on top of the default route in RouteConfig.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcAttributeRoutes();
//AreaRegistration.RegisterAllAreas();
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Setup",
url: "Setup/{controller}/{action}/{id}",
defaults: new { controller = "Setup", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MVCWeb.Controllers.Setup" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MVCWeb.Controllers" }
);
}
This does make the above URLs work, but it has no constraints so these URLs work when I want them to be invalid:
localhost:1234/Setup/CustomerAccounts/Index
localhost:1234/SystemDefaults/Index
Additionally, since the Setup route matches everything, if I do either of these:
#Html.ActionLink("Customer Accounts", "Index", "CustomerAccounts")
#Url.Action("Index", "CustomerAccounts")
It generates the URL as /Setup/CustomerAccounts/Index instead of /CustomerAccounts/Index.
Is there a way to do accomplish this without using an Area while still using {controller} in the route URL? Or would I have to add a route specifically for each controller under Setup?
Have you ever evaluated Attribute Routing in MVC 5? It seems like you're starting a new project, so this could be a good new start. With the [RoutePrefix] attribute, you could probably enforce what you want to achieve easily:
[RoutePrefix("accounts")]
public class CustomersController : Controller
{
// ...
}
We don't use Attribute Routing yet so I can't speak from own experience but it looks very flexible and promising.
UPDATE
I created a GIST for you where I explain how you could validate your Attributes, not during compile time, but with Unit Tests. This is a simple code snipped designed for MS Test. The validation possibilities are very broad.
Custom Logic we might add to a custom RoutePrefixAttribute? For example, the RoutePrefixAttribute allows a string as a parameter. You could rewrite it to allow only a specific Enum as parameter, which lists only possible values, and internally set the Prefix string.
Related
I am new in asp.net MVC. I want to know that is there any limit on total no of routes in routeconfig.cs.
how many routes we can create in routeconfig.cs in asp.net mvc
There is no limit for creating routes as far as I know. As you can make as many controllers you want and it does not limit as each action can represent a unique route.
You can add multiple routes in RouteConfig.cs like this :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"ShoppingManagment",
"{id}/ShoppingManagment/{action}",
new { controller="ShoppingManagment", action = "ShoppingManagment", id = UrlParameter.Optional });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index", id = UrlParameter.Optional }
);
}
To add multiple routes, kindly check this ASP.NET Routing.
Make sure the Default route is at the bottom end of your listed route table. Order matters when it comes to ASP.NET MVC Routing tables. The correct ordering is your 'most specific' route to your 'least specific route'. ie the Default route is defined at last.
there are no limits in creating routes. You can create as many route as you want in your RouteConfig.cs file. But make sure that you provide unique name value to each MapRoute function.
Is there a simple catch-all way to ensure only rewritten URLs can invoke a controller?
For example, if we have a URL www.somesite.com/about pointing to action "About" in controller "Shared", can it be ensured that any requests to www.somesite.com/shared/about end up at the rewritten URL, in this case www.somesite.com/about?
In other words, the user should not be able to just type /controller/action without being redirected to the rewritten URL.
However, we don't want to actively check and redirect but were hoping for some built-in function of MVC. The only suggestions I found along those lines were ChildActionOnly and HttpPost attributes, but they don't seem to be the answer (normal links don't work).
As mentioned, we're looking for something simple, more or less built-in - if it doesn't exist then so be it...
The built-in way of blocking routes is to use IgnoreRoute. It short-circuits routing and always makes the path throw a 404 not found.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Ignore /Home/About
routes.IgnoreRoute("Home/About");
// Register /About
routes.MapRoute(
name: "About",
url: "About",
defaults: new { controller = "Home", action = "About" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Note that under the covers, it uses the StopRoutingHandler, which can be used (as a replacement for MvcRouteHandler) in any custom Route or RouteBase implementation to make more dynamic ignore rules than this.
NOTE: It is extremely important that IgnoreRoute is registered before the route you want to ignore in the route table.
I have been asked by someone that, suppose i have defined routing for a URL in route.config and i have defined the same routing in attribute based routing. Then whose priority will be more in each case. And what is the use of attribute based routing if we can achieve the same in route.config.
Then whose priority will be more in each case.
That would depend on whether you call the routes.MapMvcAttributeRoutes() extension method before or after the conventional routes. For example:
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapMvcAttributeRoutes(); //Attribute routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
In this case the attribute based routes will be added first to the routing table and will take precedence.
And what is the use of attribute based routing if we can achieve the
same in route.config.
Attribute routes give you quite a bit more flexibility and places the routes next to the actions that will actually use them. But it's really a matter of preference. Microsoft has added attribute based routes in order to have an alternative way for defining the routes in the application compared to the conventional approach.
I have Home controller and Details action which received int id parameter.
I want map "/" url to Home controller, Details action, id = 1.
Also I want map urls like "/st15" to Home controller, Details action, id = 15.
So I wrote following attributes
[Route("~/{id:int:min(1):max(1)=1}")]
[Route("st{id:int:min(2)}")]
public ActionResult Details(int id)
{...}
The problem is url "/1" is also mapping to this action, but I need 404 for it
I would recommend leveraging the RouteConfig.cs file that is created with default MVC projects in VS2012.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Details",
url: "/st{id}",
defaults: { controller = "Home", action = "Details", id = "1" }
);
routes.MapRoute(
name: "Default",
url: "/",
defaults: new { controller = "Home", action = "Details", id = "1" }
);
}
This will try to match urls to those two designated routes in the order you add them to your RouteConfig file. Since the 'Default' route doesn't have any additional parameters on its URL definition, a call to "/1" won't match a route and you'll get a 404.
If you used a blank project, adding a RouteConfig file is a trivial matter. Just add a RouteConfig.cs file and define the class to contain the RegisterRoutes method I listed, then in your Global.asax file's Application_Start function, add a line for
RouteConfig.RegisterRoutes(RouteTable.Routes);
And you'll be good to go.
(EDIT)
I'm not certain that there still won't be conflicts, but you can combine Attribute routing with the standard routing paradigm by simply adding
routes.MapMvcAttributeRoutes();
to your RegisterRoutes function, immediately after the IgnoreRoute call, and the application will always defer to your Route attributes first, then check the defined routes.
However, since you've indicated you'd like to avoid using the standard routing approach altogether, you could simply define the attributes to take the following routes:
[Route("~/")]
[Route("st{id:int:min(2)}")]
public ActionResult Details (int id = "1")
{...}
To set a default value for the id parameter and avoid trying to handle the case in the attribute itself. Since you define a minimum value for the {id} parameter in your second Route attribute, you shouldn't have to worry about the case of "/st" trying to route to that action. It wouldn't match either defined route and so would 404.
Example : My User will enter www.xyz.com/Promo/PROMO123
where "PROMO123" is value, which i require.
above code produces error :
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
However
www.xyz.com/Promo/Index/PROMO123 will work properly,
but i dont want this.
How can i archive this
www.xyz.com/Promo/PROMO123
Have you tried Routing?
Such as
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Don't forget to add this before default one.
routes.MapRoute(
name: "PromoRoute",
url: "{controller}/{myString}",
defaults: new { controller = "Promo", action = "Index", myString = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
You need to define a route pattern for this.
If you want this to work application wide, then you will need to change the default route. But I would suggest simply adding a specific route for this controller, in addition to the default route, because you probably don't want to override the default MVC routing for the whole app or you will lose the ability to use multiple actions per controller.
See your RouteConfig, try this route (MVC pre-5):
routes.MapRoute("myRoute", "PromoRoute/{id}",
new {controller="PromoRoute", action = "Index"});
With MVC5 you can add this directly to your action assuming you've enabled attribute routes:
[Route("PromoRoute/{id}")]
public ActionResult Index(string id) {
}