How to route an url with an url in it in MVC - c#

Basically I would like to take an url like:
http://example.com/community/www.foo.com/bar
...and give it to my http://example.com/community/ view with www.foo.com/bar in some variable that razor can access in the view.
How could this be done?
Note: I tried this:
routes.MapRoute(
"Community",
"{controller}/{url}",
new { controller = "Community", action = "Index", url = "" }
);
which worked with http://example.com/community/www.foo.com but not http://example.com/community/www.foo.com/bar (IIS tried to resolve the latter and gave a 404).

From MSDN:
Sometimes you have to handle URL requests that contain a variable
number of URL segments. When you define a route, you can specify that
if a URL has more segments than there are in the pattern, the extra
segments are considered to be part of the last segment. To handle
additional segments in this manner you mark the last parameter with an
asterisk (*). This is referred to as a catch-all parameter. A route
with a catch-all parameter will also match URLs that do not contain
any values for the last parameter. The following example shows a route
pattern that matches an unknown number of segments.
query/{queryname}/{*queryvalues}
In your case it will be:
routes.MapRoute(
"Community",
"{controller}/{*url}",
new { controller = "Community", action = "Index", url = "" }
);
Alternative and more robust approach is using some kind of encoding for the URL part - for example Base64URI encoding from RFC4648

Related

How to fix my MVC3 routing

I have some problem with routing in my ASP.NET MVC3 application, exactly it doesn't works as I want.
Routing definition in Global.asax
routes.MapRoute(
"News", // Route name
"{lang}/{controller}/{action}/{page}/{id}", // URL with parameters
new { lang = "pl", controller = "News", action = "Index", page = 1, id = UrlParameter.Optional } // Parameter defaults
);
So I want use it in 2 situation:
/{lang}/{controller}/{action}/{page} - to go to specific paginations' page
/{lang}/{controller}/{action}/{page}/{id} - to remember a current page, and go to page with specific id.
to generate links I use:
#Html.RouteLink(i.ToString(), "News", new { lang = ViewBag.Lang, page = i })
where i is number of page, and lang is language.
In Controller i handle it witch Action:
public ViewResult Index(string lang, int page = 1)
PROBLEM
#Html.RouteLink gives me link /pl/News/Index/2 (when '2' was clicked) but my controller doesn't handle it and page is still '1'. So when I click the same link twice it gives : /pl/News/Index/2/2 and then page equals '2'.
If somebody can explain me how it exactly works, and what can I do to solve it?
Any help would be appreciated.
I guess what you're missing here is that the routing system uses the variable values from the current request to generate the outbound rules.
For example - if you're on the page /pl/News/Index/2 you have
lang = "pl", controller = "News", action = "Index", page = 2
if you try to generate a rule like this
#Html.RouteLink(i.ToString(), "News")
the routing system will use the already present variables like lang and page and will generate exactly the same URL - /pl/News/Index/2.
Of course values that you pass in the anonymous type in Html.RouteLink are treated with priority and will override values from the current request. So in your example you should consider what URL are you at when you generate the link.
Order of precedence is:
variables passed in the anonymous type
request variables
default values defined in the route.
It seems that this part
lang = ViewBag.Lang
is useless since lang will be taken from the request if you don't supply it - so it will be preserved.
Update(might not be relevant):
You should also keep in mind that if you have more than one match for a rule - the routing system will choose the first one which satisfies the constraints(for example values from request should not conflicts with default variables)
Update2:
The routing system uses request variables up to the first explicitly specified variable(via the anonymous type) so if you specify lang explicitly - no request variables will be reused(in your case, because lang is the first segment in your url)
I think part of your problem is that you're breaking convention a bit, and since MVC relies on convention to route successfully, your mapping is a bit busted.
I would suggest rewriting your routing like so:
routes.MapRoute(
"News", // Route name
"{controller}/{action}/{lang}/{id}/{page}", // URL with parameters
new { controller = "News", action = "Index", lang = "pl", page = 1, id = UrlParameter.Optional } // Parameter defaults
);
It seems like the most common case scenario for a URL would be something like:
http://yourdomainhere/news/pl/some-sort-of-permalink/1

ASP.NET MVC route handling for misspelled URLs?

How can we create a route that will detect that the URL matches a route but that the parameter to that route is wrong?
I've had several cases recently where a user has mistyped a URL and it has been hard to debug because the route table doesn't handle these misspellings very gracefully:
This route handles the URL /Widgets/guid
routes.MapRestfulRoute(
"WidgetsItem",
"Widgets/{idString}",
new { controller = "Widgets", action = "Item" },
new { idString =
#"^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$"}
);
routes.MapRoute("WidgetsList", "Widgets", new { controller = "Widgets", action = "List" });
routes.MapRestfulRoute("CatchAll","{*url}", new {controller = "Error", action = "Http404"});
And the user entered the URL /Widgets/25246810-4b60-4db8-8567-8db8826 which looks correct but is not (there's only 28 characters in that guid provided).
Because the URL didn't match the route, the response was 404. But this is deceptive and caused a debugging problem: it looks like the Widgets/Item action is returning 404. And we spent hours trying to figure out what was wrong with that action, when in fact the action wasn't even called. It wasn't until I installed RouteDebugger that I noticed the guid was malformed.
How can we create another error route that will detect that the URL does actually match a route but that the parameter to that route is wrong?
EDIT: I can "fix" it by adding a new route that matches /Widgets/anything, and placing that route after the normal route:
routes.MapRestfulRoute(
"WidgetsItemError",
"Widgets/{idString}",
new { controller = "Widgets", action = "Item" },
new { idString = #"^.+$" } // match if it has any param at all
);
But this seems to be very inefficient - I will have to add a similar error route for every single route eg /Foo, /Bar /FooBar etc (and I've got quite a lot of routes). Isn't there a more generic way to do this?
When I dealt with guid's in my routes, I would accept a string, try to parse the string value to a guid, if it didn't parse, i'd return a different view with a message indicating that the id was invalid and to try again
The only thing you should have to change here would be to remove the route constraint and change your Action to accept a string instead of a guid (unless your action is already accepting a string)
The easiest solution is to remove the route constraint, then check for a null parameter in your controller and do whatever you want from that point.

MVC non-area route problems (alongside areas)

How should I configure the following non area routes?
/foo/{controller}/{action}/{id}
maps to controllers in namespace myapp.foo.
/{controller}/{action}/{id}
maps to controllers in namespace myapp.
I also have 2 areas, bar and baz, they are registered with registeraAllAreas.
My current setup
This is my current setup. It gives the problem below when I use the url /Home/Index.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("myapp/elmah.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
"foo", // Route name
"foo/{controller}/{action}/{id}", // URL with parameters
new { action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "myapp.Controllers.foo" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "myapp.Controllers" }
);
Multiple types were found that match the controller named 'Menu'. This
can happen if the route that services this request
('foo/{controller}/{action}/{id}') does not specify namespaces to
search for a controller that matches the request.
The request for 'Menu' has found the following matching controllers:
myapp.Controllers.MenuController
myapp.Areas.bar.Controllers.MenuController
myapp.Areas.baz.Controllers.MenuController
Clearly there's something I'm doing the wrong way.
Update
I also get the wrong adress generated when I use:
<% using (Ajax.BeginForm("SaveSomething", "Home", ...
It renders <form target="/foo/Home/SaveSomething"
I'm guessing that one cannot reliably use {controller} in two routes in the same area.
Update 2
It seems to work much better when I put the /foo route registration at the bottom.
This raises the question, what is considered a/the default route? (As the default route is reccomended to be put at the very end.)
You have two controllers that has the name MenuController so MVC doesn't know which one to use if you don't give it more information. In you areas you probably have a files named something like <YourAreaName>AreaRegistration. Open those files and update the RegisterArea method so you route the request to the right controller.
From your error message it seems like the route is getting mapped to foo/{controller}/{action}/{id}, which doesn't have a MenuController. My guess is that you have a action link on a page under foo something something. That will generate an incorrect link if you don't specify the area for the link.
Try this to use the default route with ActionLink:
#Html.ActionLink("Some text", "action", "controller", new { area = "" }, null)
If you want the request to go to a specific area just write it down in the call.
UPDATE: The problem is that when you write something like Ajax.BeginForm("SaveSomething", "Home",...) it will match the first route. You can't solve this by putting the area in the BeginForm statement as I suggested before since the foo route is not an area. You have two options, 1: move the foo part to an area, 2: put the foo route after the default route. If you put the default route before the foo route you will get a hard time rendering urls as long as you have foo in the same area as the default route (the default area), since the route engine will always find the default one first. However, you will be able to catch request to the foo route. So my best suggestion is to put the foo route in an area.

ASP.NET MVC 3 Url.Action matching

I have the following two routes defined:
routes.MapRoute(null, "" // ~/
,new { controller="Products", action="List", page=1 });
routes.MapRoute(null, "{category}/Page{page}" // ~/CategoryName/Page21
, new { controller = "Products", action = "List", page = 1 }
, new { page = #"\d+" } //page must be numerical
);
I am generating a URL using this code in the view being used by ProductsController: Url.Action("List", new {page=p, category=Model.CurrentCategory})
With the current configuration, I get this URL: /Riding/Page2
However, if I omit the default page parameter from the first route, I get this URL instead: /?category=Riding&page=2
It seems to me that Url.Action() can match both routes and is making a decision to use the second route if I have the default page parameter specified in the first route but is choosing to use the first route if that parameter is omitted.
Given that I am supplying a value for page parameter, why would eliminating it from the route's defaults make a difference in the URL I get back?
Thanks!
Try installing the NuGet package Glimpse. It has excellent route debugging, it should be able to help you.
Here's a blog post by Scott Hanselan on how to use it: NuGet Package of the Week #5

How to define a route to match 3 parameters, when I'm only interested in the third?

I'm trying to specify a route configuration that will allow a url similar to:
/{any sequence of characters}/{any sequence of characters}/{mexid}
We're currently redesigning an old system that used URLs of the previous format. Our new system has to catch these requests and redirect them to a new URL, but only if there are 3 URL parameters. It's only the third parameter that I'm interested in.
This is the route mapping that I'm using:
routes.MapRoute(
null,
"{param1}/{param2}/{mexid}",
new { controller = "ShareClass", action = "FundFactsheet", mexid = "" }
);
the problem I'm having is that this is acting as a kind of catch-all for any invalid requests that come in, where I am only interested in this being the route for URLs with a mexid in them.
how can I define a route that will only apply to URLs that contain 3 parameters, only if the URL doesn't already match another route that I've more specifically defined?
You might want to add route constraint (I haven't tested it yet but I think it should answer your question)
routes.MapRoute(
null,
"{param1}/{param2}/{mexid}",
new { controller = "ShareClass", action = "FundFactsheet" },
new { param1 = #".+", param2 = #".+", mexid = #".+" }
);
Can you remove the default for mexid?
The way you have the route right now, if you don't specify mexid then it will use "" as a default.
If you don't have anything unique about param1 and param2 then it will probably always take precedence over the default route, though.

Categories