Using one similar route url for two different parameter types - asp.net-mvc

So I have following routes in place in my project, what I want to do here use one similar looking route url but direct it to two different actions based over the parameter provided, if the given parameter is a string it should pick the Search route and if the parameter is a digit/integer than it should pick the GetCategory route and to achieve this I have written the following code after seeking some existing online help here over SO, but this doesn’t seems to be working as expected.
No matter what the parameter value is it always picks whichever route appears first in the list.
//routes.MapRoute("GetCategory", "{pId}", new { controller = "Student", action = "Post", pId = #"^\d{1,3}$" });
routes.MapRoute("Search", "{category}", new { controller = "Student", action = "Search", category = UrlParameter.Optional });
routes.MapRoute("GetCategory", "{pId}", new { controller = "Student", action = "Post", pId = #"^\d{1,3}$" });
Can anybody help me out with what am I missing here?

Just tested this approach
routes.MapRoute("GetCategory", "{pId}", new { controller = "Student", action = "Post" },new {pId = #"\d"});
routes.MapRoute("Search", "{category}", new { controller = "Student", action = "Search", category = UrlParameter.Optional });
You should apply restrictions on your params within constraints argument, not in defaults section.

Have you tried moving the GetCategory route up above the other one. The problem with the Search route is you don't have a constraint on it so it would match anything. If you moved the GetCategory route to the top and provided a string, it wouldn't match since it is not a digit and would move to the next one.

Related

redirect creating unwanted text in path

I'm working on an existing project that is not routing redirects as I would expect.
my redirect is like this:
return RedirectToAction("Display", "Ladder", new { DisplayDate = DateTime.Now.ToString("yyyyMMdd"), #id = details.CommunityId, slug = details.Slug, InnerId = activityId });
The url it takes me to is: http://localhost:8050/the-staples-club/communitydocuments/161/ladder-display/opq6l4et4bd7/20160429
The url is almost correct, except that a mysterious 'communitydocuments' has been inserted which causes the url to fail. When I remove that part, it works fine. As you can probably tell, I don't understand too much about routing, but where in the project could I investigate to stop it creating 'communitydocuments'?
edit: In routeconfig.cs there are a couple custom routes, but nothing explaining where 'communitydocuments' is coming from. I tried creating a custom route as follows but it still does the same thing:
routes.MapRoute(
name: "LadderDisplay",
url: "{slug}/{id}/{controller}-{action}/{innerid}/{displayDate}",
defaults: new
{
controller = "Ladder",
action = "Display",
displayDate = UrlParameter.Optional,
}
,
constraints: new
{
displayDate = #"\d{8}"
}
);

Redirect To Action method not mapping correctly

I'm calling RedirectToAction but it isn't working properly.
I want the resulting URL to look like this:
https://localhost:44301/ManageSpaces/123/overview
but it looks like this and is missing the action portion of the URL:
https://localhost:44301/ManageSpaces/123
Here is my RedirectToAction call.
return RedirectToAction("overview", new RouteValueDictionary(
new {controller = "ManageSpaces", action = "overview", id = 123}));
Here is what my route looks like in RouteConfig:
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces", action = "overview"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);
Maybe it is taking the default route. Rename, remove, or comment out the default route to see if that has any effect.
You have made your action route value optional by providing a default value. Optional values are ignored when resolving the URL.
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces", action = "overview"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);
If you want to include the action in the URL, you have to make it a required argument.
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);

Generic ASP.NET MVC Route Conflict

I'm working on a Legacy ASP.NET system. I say legacy because there are NO tests around 90% of the system. I'm trying to fix the routes in this project and I'm running into a issue I wish to solve with generic routes.
I have the following routes:
routes.MapRoute(
"DefaultWithPdn",
"{controller}/{action}/{pdn}",
new { controller = "", action = "Index", pdn = "" },
null
);
routes.MapRoute(
"DefaultWithClientId",
"{controller}/{action}/{clientId}",
new { controller = "", action = "index", clientid = "" },
null
);
The problem is that the first route is catching all of the traffic for what I need to be routed to the second route. The route is generic (no controller is defined in the constraint in either route definition) because multiple controllers throughout the entire app share this same premise (sometimes we need a "pdn" sometimes we need a "clientId").
How can I map these generic routes so that they go to the proper controller and action, yet not have one be too greedy? Or can I at all? Are these routes too generic (which is what I'm starting to believe is the case).
My only option at this point (AFAIK) is one of the following:
In the contraints, apply a regex to match the action values like: (foo|bar|biz|bang) and the same for the controller: (home|customer|products) for each controller. However, this has a problem in the fact that I may need to do this:
~/Foo/Home/123 // Should map to "DefaultwithPdn"
~/Foo/Home/abc // Should map to "DefaultWithClientId"
Which means that if the Foo Controller has an action that takes a pdn and another action that takes a clientId (which happens all the time in this app), the wrong route is chosen.
To hardcode these contstraints into each possible controller/action combo seems like a lot of duplication to me and I have the feeling I've been looking at the problem for too long so I need another pair of eyes to help out.
Can I have generic routes to handle this scenario? Or do I need to have custom routes for each controller with constraints applied to the actions on those routes?
Thanks
Add constraints to your routes by removing the null and replacing it with the constraint needed for that route:
For PDN, use a regular expression for digits:
routes.MapRoute(
"DefaultWithPdn",
"{controller}/{action}/{pdn}",
new { controller = "", action = "Index", pdn = "" },
new { pdn = #"\d+" }
);
For ClientID, use a regular expression for all characters:
routes.MapRoute(
"DefaultWithClientId",
"{controller}/{action}/{clientid}",
new { controller = "", action = "index", clientid = "" },
new { clientid = #"[A-Za-z]+" }
);
Since I don't keep the minutia of regular expression stuff in my head, I generally use a cheat sheet.
You should add some route constraints that say the PDN route matches numbers and the ClientId matches strings
I usually create a series of matches to use throughout my route declaration like so:
readonly static string ALPHA_MATCH = #"[\da-zA-Z]";
readonly static string DIGIT_MATCH = #"\d+";
then add the constraints to the routes like this:
routes.MapRoute(
"DefaultWithPdn",
"{controller}/{action}/{pdn}",
new { controller = "", action = "Index", pdn = "" },
new { pdn = DIGIT_MATCH }
);
routes.MapRoute(
"DefaultWithClientId",
"{controller}/{action}/{clientId}",
new { controller = "", action = "index", clientid = "" },
new { clientId = ALPHA_MATCH }
);

Route Links - Url.Action

I'm trying to return my links so they display as /Area_1419.aspx/2/1.
I've managed to get that result in example 2 but I don't understand why it works, as I would exspect example 1 below to work.
I don't see how Example 2 knows to go to the Area_1419 controller?
Route
routes.MapRoute(
"Area_1419 Section",
"Area_1419.aspx/{section_ID}/{course_ID}",
new { controller = "Home", action = "Index" }
);
Links Example 1
<a href='<%=Url.Action("Area_1419",
new { section_ID="2", course_ID="1" })%>'><img .../></a>
Returns: /Home.aspx/Area_1419?section_ID=2&course_ID=1
Links Example 2
<a href='<%=Url.Action("index",
new { section_ID="2", course_ID="1" })%>'><img .../></a>
Returns: /Area_1419.aspx/2/1
Remember - URLs are detached from your controllers and their actions.
That means - even bizzare URL such as "trolololo/nomnomnom/1/2/3" might and might not call Home/Index or any other controller/action combo.
In your case - example 2 actually does not know how to go to Area_1419 controller.
Url.Action figures out url from these route details:
"Area_1419.aspx/{section_ID}/{course_ID}"
But link still will call Home controller Index action because of default route values:
new { controller = "Home", action = "Index" }
Assuming that you got Area_1419 controller with Index action, your route should look like:
routes.MapRoute(
"Area_1419 Section",
"Area_1419.aspx/{section_ID}/{course_ID}",
new { controller = "Area_1419", action = "Index" } //changes here
);
This is what you are calling.
UrlHelper.Action Method (String, Object)
Generates a fully qualified URL to an action method by using the specified action name and route values.
This method overload does not try to figure out appropriate controller. It assumes that you know it (takes it out from current route values) and understands first string argument as an action name.
Try to use this one.
UrlHelper.Action Method (String, String, Object)
Generates a fully qualified URL to an action method by using the specified action name, controller name, and route values.
In your case:
Url.Action("Index","Area_1419", new { section_ID="2", course_ID="1" });
You can use Url.RouteUrl(), in your case
Url.RouteUrl("Area_1419 Section", new { controller = "Home", action = "Index", section_ID="2", course_ID="1"}
to be sure you use the correct route name, and get the correct URL no-matter-what.

ASP.NET MVC - Mapping more than one query string parameter to a pretty url

I am a bit stuck on the design of my seo friendly urls for mvc....Take for example the following url:
http://myapp/venues/resturants.aspx?location=central&orderBy=top-rated
With my mvc app i have mapped it as follows:
http://myapp/venues/list/resturants/central/top-rated
{controller}/{action}/{category}/{location}/{order}
Now the only problem is that location and order are optional...so it should be possible to submit a request like: http://myapp/venues/list/resturants/top-rated . This proves to be a problem when the request hits the controller action, the location parameter has picked up "top-rated", naturally.
Any suggestions? I' am considering using explicit querystrings to handle more than one parameter but this is really my last option as i dont want to sacrifice SEO too much.
Has anyone eles run into such dilemmas? And how did you handle it?
Thanks in advance!
Click on your profile link and look at the URLs for Stats, Recent, Response, etc.
Examples:
https://stackoverflow.com/users/52065?sort=recent#sort-top
https://stackoverflow.com/users/52065?sort=stats#sort-top
with no sort it defaults to stats
https://stackoverflow.com/users/52065
Optional paramters should be query parameters
Assuming that the allowed values for location and order are unique (i.e. when they come in, you can tell them apart, or else if they only supply one, how are you going to know if it's a location or an order?), then you could just take two parameters and work out what they are in the controller.
Route: {controller}/{action}/{param1}/{param2}
Controller action:
public ActionResult MyAction(string param1, string param2)
{
string location;
string order;
if (!ParseLocation(param1, out location))
{ ParseLocation(param2, out location); }
// ...
}
Not particularly elegant, but does let you have the URLs you want.
You will always have this issue if you have multiple optional parameters. Either make one or both of them non-optional (and positioned earlier in the query string than the optional one) or use the querystring parameter notation.
ok guys just posting a solution i've been playing with so far.
I have set up my routes using constraints as follows:
routes.MapRoute(
"VenuesList",
"venues/list/{category}/{location}/{orderBy}",
new { controller = "venues", action = "list", category = "", location = "", orderBy = "" },
new { location = "central|east|west|south", orderBy = "top-rated|price" }
);
routes.MapRoute(
"VenuesListByLocation",
"venues/list/{category}/{location}",
new { controller = "venues", action = "list", category = "", location = "" },
new { location = "central|east|west|south" }
);
routes.MapRoute(
"VenuesListByOrder",
"venues/list/{category}/{orderBy}",
new { controller = "venues", action = "list", category = "", orderBy = "" },
new { orderBy = "top-rated|price" }
);
routes.MapRoute(
"VenuesListDefault",
"venues/list/{category}",
new { controller = "venues", action = "list", category = "" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
The idea is that if the validation fails it will go to the next route in the list...eventually hitting the default.
Needs some more testing but has worked well so far...
Why don't you create a property in the page for each possible querystring parameter?
This way you can handle it any way you choose with just a few lines of code...

Resources