ASP.NET MVC AnchorLink not using route values? - asp.net-mvc

I'm using AnchorLink on a very simple site with just two routes defined, one standard route and another area route for admin/{controller}/{action}/{id}. I'm at a URL like:
/admin/release/push/255
In that view I'm using:
#Html.AnchorLink("Confirm", "Confirm")
AnchorLink is rendering a link without the current request {id} included, ie. /admin/release/confirm! If I inspect the RouteValues I can see {id} is in there. If I explicity pass in the route values from teh current request, as in:
#Html.AnchorLink("Confirm this release", "Confirm", Url.RequestContext.RouteData.Values)
Then it works, I get the URL /admin/release/confirm/255. Why does the explicit version where I pass in the current request route values work, but the implicit one, without the route values argument, which I thought would pick up the current request route values, doesn't? I know the above is a solution, but it's ugly and there's some underlying reason why the AnchorLink without the route values isn't working as I expect?!

MVC is doing exactly the right thing here. It's not to know you require the Id parameter for your anchor link or not -- in other words its not trying anything clever to pre-parse and examine the link. It will only do that when its being rendered. And in your case without the id parameter specified somewhere its going to use the default route.
If you find yourself writing that same code all over the place you could easily extract it out into a static helper class. That can get rid of the ugliness.

Related

#Url.Action("Action", "Controller") returns "/cgi-bin?action=Action&controller=Controller"

For some reason it thinks the target is an Apache server - I suspect?
The MVC is V5.2.3 and its dependancies are correct as per nuget
. I have searched and searched to no avail.
EDIT
The code is simply #Url.Action("Action", "Controller")
So I created a brand new MVC project and using exactly the same code the correct code was returned.
http://localhost:53143/Controller/Action
EDIT 2
I removed my web.config files as the problem is not there.
I got in touch with an expert and he looked at the project and answered as below.
(BTW I have 'cgi-bin' in a route as there are old URLs out in the wild that relate to my domain previously being hosted on an Apache server and which I cannot change.)
The answer
I digged a little more into the source code of the mvc helpers and, yes, the two issues (I had a similar problem Html.BeginForm with overload causes form action to be '/cgi-bin?action=Index&controller=Home' and so the HomeController is bypassed) are related since Url.Action and Html.BeginForm boil down to calling one and the same method: UrlHelper.GetUrl... Now, what this method does is:
Retrieve the current URL including controller, action, area...
Add or replace the parameter(s) you specify,
Find the best matching route! <==
If there are any route variables - push the provided parameters into those variables.
Stick the rest of the parameters into the query string <==
I have deliberately highlighted point 3 & 5, with point 3 being the most important. So, UrlHelper.GetUrl (and Url.Action and Html.BeginForm respectively) needs a route and it searches through the available ones to find the first match.
Now, here comes the problem with your mixed webforms-mvc app - an issue which is not present when you are dealing purely with MVC: You are using MapPageRoute!!! Please, note that it is different from MapRoute. And MapPageRoute uses the PageRouteHandler class to create the route whereas MapRoute uses the MvcRouteHandler class and it makes all the difference because PageRouteHandler creates the route in such a way that it's always a good match for UrlHelper.GetUrl("ActionName", "ControllerName") with the action name and controller name being thrown into the query string as parameters (point 5).
So, what happens with your set-up is that Url.Action is searching for a route and is hitting the first one created by MapPageRoute and in your case this is:
routes.MapPageRoute("cgi-bin", "cgi-bin/{*theRestcgi-bin}", "~/home/Search.aspx");
That's where that arcane cgi-bin part of the query string comes from, giving the impression that the framework is actually searching for some virtual/physical folder.
As for the proper solution: either define a suitable route or specify the url as a simple string the way you have done. I think, your solution is the better one as you won't have to move around the route definitions in the RouteConfig class.

Is there a better way to reference a route by name in a link with AngularDart?

I have many named routes in my AngularDart app. I create links the old fashioned way, like this:
Go
That seems brittle. If I change the path or change the strategy away from hash change, I need to change all my links.
Can I do something like:
<a ng-link="activities">Go</a>
Where activities is the name of the route from my routes config.
For now you can use router to generate those URLs for you.
router.url('activities', {});
The second parameter (should probably be optional) is a map of parameter values. For example, if you have a path like /activity/:activityId then you can do:
router.url('activity', {'activityId', '12345'});
URL generator also honors current state of routes, so lets say you had an active route like foo.bar.baz, and foo was parameterized and you somehow got a hold of bar route (ex. via RouteProvider or queried router.root.getRoute('foo.bar')) then you don't need to know the values of the foo route parameters to generate the URL for baz, you can do:
Route bar = router.root.getRoute('foo.bar');
router.url('baz', {}, startingFrom: bar);
For now you will need to manually insert the generated URL into the template:
link

Difference between Url.RouteUrl() & Url.Action() in MVC3

I am in the process of generating a URL dynamically in my cshtml page.
What is the difference between Url.RouteUrl() & Url.Action()?
Which one should I use to generate the URL & what difference do both have in terms of implementation ?
Thanks in advance.
RouteUrl generated the url based on route name. If you have multiple routes with similar parameters the Action method may pick a wrong one - it works based on the order of route definitions. This may take place when your routes have optional parameters.
If you want to make sure that a certain route url will be used you need to call RouteUrl passing this route name. Route names are unique and clearly identifies a route.
One more difference is that Action is MVC specific (it uses controller and action names), while RouteUrl is generic is and can be used without MVC (you can have routing in WebForms).
Url.RouteUrl allows you to specify a particular route by name. This will force the usage of that route. Url.Action will simply pick the first route that matches the criteria.

mvc route random number of parameters

I am trying to construct a route that routes to a specific product page.
The product exists in a category. Categories may also exist in categories. I am trying to construct the URL like
my-site.com/products/category/category/category/product
The amount of categories is able to change but the last parameter will always be the name of the product.
Is there any way to construct a route for this?
A solution exactly for you
I faced the same problem in the past and solved it a bit differently to what others will advise you here. Most of solutions will talk about *catch-all parameter. In your case this means that you'd have to parse the product out yourself. Manually. Because catch-all parameter may only be the last parameter in route definition.
Catch-all anywhere in the route
If you think of it carefully, you can actually realise that catch all parameter can actually be defined anywhere in the route as long as you have all other segments present. So I've written such route class that does all that and successfully runs in production on a heavy traffic website.
My blog post has all the information about it as well as all the code that will solve your problem:
Custom Asp.net MVC route class with catch-all segment anywhere in the URL
This makes it possible for you to define your route as:
products/{*categories}/{productId}
If you think of the catch-all parameter even further you could also get to the point that a single route definition could have several catch-all parameters as long as at least one segment between them is static. But my class isn't able to do this, because your scenario with just one arbitrary segment set is much more common.

Why does the Action Link depend on the current request?

For example, when I visit http://www.nerddinner.com/Home/About/, the About tab points to http://www.nerddinner.com/Home/About/ which is what I expect.
However, If i were to visit http://www.nerddinner.com/Home/About/WhyDoesThisLinkChange, not only is this page valid but the About tab also points to http://www.nerddinner.com/Home/About/WhyDoesThisLinkChange. Why does ASP.NET MVC do this and how do i prevent it from doing it?
Can you show your routing setup? Something is wrong with it I'd suggest. The second example URL shouldn't map to the same action.
You probably have a parameter in the routing - and correctly MVC is preserving this parameter.
In principal if you have
/product/view/17
Controller = product
Action = view
{productId} = 17
So in this case it makes sense to preserve the Id of the product across requests to the same action.
But in your case you don't really want both those URLs to map to the same place. That said once you are there - with the second URL - it makes sense for MVC to use the same URL to get back to the same action.
UPDATE: If you want to explicitly stop a parameter being accepted for this URL then you need to make the parameter accepting routing option more specific so it excludes this URL, or put in a routing option above it without a parameter than accepts only the Home/About URL.

Resources