How To Prevent Multiple ASP.NET MVC Route Mappings - c#

I have an ASP.NET MVC routing question. First, let me explain my areas setup. It's quite simple.
Areas
|
+--Foo
|
+--Controllers
|
+--BarController.cs
I have a folder in my areas called "Foo" and controller called "BarController.cs" The Bar controller has several methods named "DoStuff1()", "DoStuff2()", etc.
My website uses the following URLs:
/foo/bar/15
/foo/bar/dostuff1
/foo/bar/dostuff2
The first URL requires an id and uses the default Index() method in the Bar controller to populate the webpage with a view and model.
In the second and third URLs, I'm using them for jQuery ajax calls.
Here is the code from my area registrion
context.MapRoute(null, "Foo/Bar/DoStuff1", new
{
action = "DoStuff1",
controller = "Bar"
});
context.MapRoute(null, "Foo/Bar/DoStuff2", new
{
action = "DoStuff2",
controller = "Bar"
});
context.MapRoute(null, "Foo/Bar/{id}", new
{
action = "Index",
controller = "Bar"
});
My problem is that for each new controller method I create, I have to add another route mapping in the area registrion file. For example, if I add the method DoStuff3(), I'll need to add this to the area registration:
context.MapRoute(null, "Foo/Bar/DoStuff3", new
{
action = "DoStuff3",
controller = "Bar"
});
How can I create a generic route mapping to handle the URLs I mentioned above that doesn't require new additions to the area registration file for new controller methods?

You can pull out the controller action.
Write the URL like this:
"Foo/Bar/{action}"
Additionally, you can pull out the controller as well, and write
"Foo/{controller}/{action}"
In this case, action = "Index" provides a default value of "Index" if no action parameter is provided.
In this case, you need to disambiguate between "Foo/Bar/{action}" and "Foo/Bar/{id}". Since matching is done in order, you'll want to put the id route first, and add a numeric constraint to the id parameter. This allows valid numeric ids to match it, and action names to skip down to the next route. Your two routes would look like this:
context.MapRoute(null, "Foo/Bar/{id}", new
{
action = "Index",
controller = "Bar"
},
new { id = #"\d+" });
context.MapRoute(null, "Foo/Bar/{action}", new
{
action = "Index", //optional default parameter, makes the route fall back to Index if no action is provided
controller = "Bar"
});

The default routing that comes with MVC templates are good for most of the needed route configurations.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Related

Is it possible to have a parameter named action in MVC4?

PLEASE MARK AS DUPLICATE - Its already answered here
Is it possible to have a parameter named action in MVC4?
Trying to do this results in the parameter returning me the name of the controller action rather than the parameter value.
/Somecontroller/Someaction?action=value
When I try to access the parameter action, I get "Someaction" as the result rather than "value".
Trying to bind the parameter to a different name doesn't help either.
public ActionResult Someaction([Bind(Prefix = "action")] String id)
Edit: I have not found 'Action'/'action' in reserved MVC keywords either.
I haven't tested this, but I would assume you could try changing the url of your default route to something other than action
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
to
routes.MapRoute("Default", "{controller}/{cAction}/{id}",
new { controller = "Home", cAction = "Index", id = UrlParameter.Optional });
this should then allow you to use action as a parameter in your Action. I'm not certain why you'd want to do this, I would probably build a route url that accepted my action and built it as part of the url similar to this:
/SomeController/SomeAction/value where value is your "action" parameter.
EDIT based on comments:
I successfully created a route that goes to an aspx url'd route
routes.MapRoute(null, "third-party.aspx", new { controller = "Home", action = "Index" });
Obviously you can add whatever controller/action you want here and where you want to handle it, then you access that route via /third-party.aspx?action=value and it seemed to work for me

Url.Action doesnt work with non standard MVC Route

I am having trouble making #Url.Action work with Area's that have a non standard route structure.
For instance if I register this route in my Dashboard area:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}",
new { controller = "View", action = "Display", id = UrlParameter.Optional }
);
and then in my layout view I call:
#Url.Action("Select", "View", new { area = "Dashboard" })
I get a proper url: /Dashboard/View/Select
However, if I change the route to include an optional secondary id like this:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display", id = UrlParameter.Optional, secondaryid = UrlParameter.Optional }
);
Then the same call to #Url.Action(...) doesn't return any url. If I specify those optional parameters with real values like so:
#Url.Action("Select", "View", new { area = "Dashboard", id = 1, secondaryid = 2 })
I do get a god return value of: /Dashboard/View/Select/1/2
The problem is that for some of my actions in this area don't need the id or secondary id and I want the url to be generated without them. If i set them to (int?)null it still doesn't work.
Am I doing something wrong? Shouldn't Url.Action(...) return the URL without the id and secondaryid tokens if I dont specify them in the routeValues parameter?
Having multiple optional parameters does funky things to your routes. Basically, the route engine cannot (has trouble) matching one or no optional parameters where there is a group of them. For more information, check out this blog post on the same issue.
Since you don't always need id or secondary id, just make a couple of routes to handle those cases.
context.MapRoute(
"Dashboard_IdAndSecondaryId",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default_WithSecondaryId",
"Dashboard/{controller}/{action}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/",
new { controller = "View", action = "Display", id = UrlParameter.Optional}
);
Now, when you send just an Id, just a secondaryId or both, you will have routes that will match. We can remove the optional parameter declarations in the first two routes, because in order to match that route, you would have to send the required parameters. Without sending the required parameters, you would want only the last route to match.
The last route is your default route when only Id or none is sent in the action link. I believe that this order works as well, keeping in mind you want your routes to go from most specific to least specific given that they are processed in order.

MVC routing issue

Currently I'am working on an MVC project in which I try to get a kind of dynamic routing working. My idea would be that i left the original route in the global.asax.cs, so this one will take care of every controller I make. For example the Contact and Account controllers.
Above controllers will have url's like
/Contact/
/Account/Logoff/ etc.
The second route I want to add is the one that is a kind of default route when there are no controllers found. In that case I assume this will be a route to a page or pagedetails.
Url's for example will be :
/BBQ/
/BBQ/Accesoires/
I have three routes added in the global.asax.cs which I think are correct. (Also in the correct order). Below I have added the routes:
routes.MapRoute(
"DefaultRoute", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Page", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"DefaultPageRoute",
"{category}",
new { controller = "Page", action = "Index", category = UrlParameter.Optional });
routes.MapRoute(
"SecondLevelPageRoute",
"{category}/{subCategory}",
new { controller = "Page", action = "PageDetails", category = UrlParameter.Optional, subCategory = UrlParameter.Optional });
with this setup the calls to the controllers work fine, but to the pages like /BBQ/ it gives below error:
Server Error in '/' Application.
The resource cannot be found.
If I comment the first route and go the the /BBQ/ url it works like a charm. What am I overseeing in this routetable?
You put the default route first, so it is trying to go to a route defined by {controller = "BBQ", Action = "Index" }
That route should be the very last route. However, you need more detail in your routes. Just having a category route will cause problems.
For example, if this route is first
routes.MapRoute(
"DefaultPageRoute",
"{category}",
new { controller = "Page", action = "Index", category = UrlParameter.Optional });
Then a call to the URL /Contact/ will assume that you want to go to Page/Index/Contact not /Contact/Index/{id}. I would use a more specific route that signifies that you are browsing a category like:
routes.MapRoute(
"DefaultPageRoute",
"Category/{category}",
new { controller = "Page", action = "Index", category = UrlParameter.Optional });
So you will need to use a url www.mysite.com/Category/BBQ to view what you want, but I don't think that's all bad.

Pass parameters through URL in an ASP.NET MVC Application

I need to access a parameter without name.
Ex: I have a controller test, I want to get "data" in mysite.com/test/data. Without calling the action data.
I know how to do that passing it though Index action.
public ActionResult Index(string id)
That way I'd only need to type mysite.com/test/Index/data to get "data", but I don't want to have to type Index.
Does anyone know how to do it?
EDIT:
Thanks a lot to #andyc!
AndyC I used what you said and created a test. It worked =D
Now I can type mysite.com/something and if something is not an controller it redirects to my profile page.
This is what is working for me
routes.MapRoute(
"Profile",
"{id}",
new { Controller = "Profile", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Setup a custom route
In your global.asax file, put (Inside the RegisterRoutes method):
routes.MapRoute(
"MyShortRoute",
"view/{id}",
new { Controller = "test", action = "Index" }
);
The first parameter is a name, the second is the URL format and the last parameter is the default values (in this case if you leave id empty it'll default to id 0.
Notice that I don't use test/{id} as this would also match test/Edit, where edit is an action that you do not want passed as a parameter (I can't think of another way to avoid this, especially if you're using strings instead of ints for your parameters).
Remember to order your routes appropriately in your global.asax file. Put more specific routes before less specific routes. When the system is searching for a route to take, it does not attempt to find the most specific match but instead it starts from the top, and uses the first match it finds.
Therefore, this is bad:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "test", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Specific",
"test/{id}",
new { controller = "test", action = "Index", id = 0 }
);
In this example, if you browse to test/somevalue it will match the FIRST entry, which is not what you want, giving you the testcontroller and the somevalue action.
(As your adding a more specific route, you will want it near the top, and definitely before your default).

How can I create a friendly URL in ASP.NET MVC?

How do I generate friendly URLs within the ASP.NET MVC Framework? For example, we've got a URL that looks like this:
http://site/catalogue/BrowseByStyleLevel/1
The 1 is Id of the study level (Higher in this case) to browse, but I'l like to reformat the URL in the same way StackOverflow does it.
For example, these two URLs will take you to the same place:
https://stackoverflow.com/questions/119323/nested-for-loops-in-different-languages
https://stackoverflow.com/questions/119323/
EDIT: The friendly part of the url is referred to as a slug.
There are two steps to solve this problem. First, create a new route or change the default route to accept an additional parameter:
routes.MapRoute( "Default", // Route name
"{controller}/{action}/{id}/{ignoreThisBit}",
new { controller = "Home",
action = "Index",
id = "",
ignoreThisBit = ""} // Parameter defaults )
Now you can type whatever you want to at the end of your URI and the application will ignore it.
When you render the links, you need to add the "friendly" text:
<%= Html.ActionLink("Link text", "ActionName", "ControllerName",
new { id = 1234, ignoreThisBit="friendly-text-here" });
This is how I have implemented the slug URL on my application.
Note: The default Maproute should not be changed and also the routes are processed in the order in which they're added to the route list.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
routes.MapRoute("Place", "{controller}/{action}/{id}/{slug}", new { controller = "Place", action = "Details", id = UrlParameter.Optional,slug="" });
you have a route on the global.asax
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = ""}
// Parameter defaults )
you can define your own route like :
controller is the cs class inside the the controllers folder.
you can define your id - with the name you choose.
the system will pass the value to your actionResult method.
you can read more about this step here : http://www.asp.net/learn/mvc/tutorial-05-cs.aspx

Categories

Resources