ASP.NET MVC Url.Action routing error - asp.net-mvc

I've been using this some while now, but I can't seem to figure out, where could be mistake in this simple code:
<a href="<%= Url.Action("Page", new { page=(Model.PageIndex + 1) }) %>" >a</a>
With this routing table:
routes.MapRoute(
"Paging",
"Home/Page/{page}",
new { controller = "Home", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
And of course this method
public ActionResult Index(int? page)
I am getting instead of expected address http://localhost:58296/Home/Page/1 the one http://localhost:58296/Home/Page?page=1
When using
<%= Html.RouteLink("a", "Paging", new { page=(Model.PageIndex+1) }) %>
it works.. Please, where is my mistake? I want image link, so if there is a way to insert it into Html.RouteLink, I would appriciate that info too.
Thanks in advance.

There's a bunch of items to cover here for you to fully understand what's happening. Sorry, this will be a bit long.
routes.MapRoute(
"Paging",
"Home/Page/{page}",
new { controller = "Home", action = "Index" }
);
First, this is the route you want to hit. You do not include the action route parameter, '{action}', in the route path. The only action this route can possibly take is the value you have specified as the default action value "Index".
<a href="<%= Url.Action("Page", new { page=(Model.PageIndex + 1) }) %>" >a</a>
Second, in your link you are setting an action of Page. The route you are expecting does not accept action as a parameter and the only action it is aware of is Index. When Url.Action looks for possible routes in your route table, it will skip the route you want because that route does not accept an action of Page. The default route is valid though because you are implicitly supplying a controller, Home, explicitly supplying an action, Page, allowing the framework to supply a default for id, string.Empty, and any other parameters are tacked on as query parameters, page.
When you changed the Url.Action to "Index", the Url.Action method inspected the routes table and found the route with the action of Index specified for the Home controller with a parameter of page and everything is happier.
Hope that helps and isn't too confusing.

I don't know why, but
<%= Url.Action("Index", new { page=(Model.PageIndex + 1) }) %>
works and it displays and directs to /Home/Page/1 . If someone could explain it to me, I would be gratful.

Related

Issues in MVC 2 Routing controller Index Page

I have Created a new Controller called Consultants. Then I create action method Index()..
I gave route like the following,
routes.MapRouteLowercase(
"consultants",
"consultants/index",
new { controller = "Consultants", action = "Index" }
);
In view, ActionLink method is,
<%: Html.ActionLink("Consultant Home", "Index", "Consultants", null, new { title = "Back home" })%>
But it is not routing. It is showing Resource cannot be find
Please correct my issues...
It looks like you've created a custom RouteCollectionExtensions called MapRouteLowercase (or at least I'm not familiar with it). I'd test that to make sure it's working as you expect by changing your route to this:
routes.MapRoute(
"consultants",
"consultants/index",
new { controller = "Consultants", action = "Index" }
);
Otherwise, you may have another route map causing issues, so make sure that route config is at the very top of your routing. Order plays an important role in how the route engine determines the correct url when searching for patterns. So order from specific to general.
For example, if you did something like this, it would cause issues with your current route:
routes.MapRoute(
"dateRoute",
"consultants/{date}",
new { controller = "Consultants", action = "Dates", date = UrlParameter.Optional }
);

asp.net mvc - Using multiple URLs to the same action

I have a controller called TaskListsController with an action called Edit.
The Edit action accepts a nullable int parameter. If it receives null, it adds a TaskList. If it receives an int, it edits the TaskList with the same ID. The logic is almost identical in each case.
I would like to configure my routing in such a way that the URL 'TaskLists/Add' maps to the Edit action with null as the parameter, and the URL 'TaskLists/Edit/{id}' maps to Edit and passes it the ID in the URL.
I can get this to work from the point of view of entering URLs in the browser and having them routed to the action correctly, but where it's falling down is where the system generates URLs from Html.ActionLink.
I am generating 'Add' links using:
Html.ActionLink("Add task list", "Edit", "TaskLists")
And I'm generating 'Edit' links using:
Html.ActionLink(taskList.Name, "Edit", new { Id = taskList.Id })
..where taskList is an instance of the TaskList class.
If I use this routing:
routes.MapRoute(
"TaskLists/Add", // Route name
"TaskLists/Add", // URL with parameters
new { controller = "TaskLists", action = "Edit" });
routes.MapRoute(
"TaskLists/Edit/{id}", // Route name
"TaskLists/Edit/{id}", // URL with parameters
new { controller = "TaskLists", action = "Edit", id = UrlParameter.Optional });
...the 'Add' link is generated correctly ('TaskLists/Add') but the 'Edit' link comes out 'TaskLists/Add?Id=1'.
If I put the routing commands the other way around:
routes.MapRoute(
"TaskLists/Edit/{id}", // Route name
"TaskLists/Edit/{id}", // URL with parameters
new { controller = "TaskLists", action = "Edit", id = UrlParameter.Optional });
routes.MapRoute(
"TaskLists/Add", // Route name
"TaskLists/Add", // URL with parameters
new { controller = "TaskLists", action = "Edit" });
...then the 'Edit' links are generated correctly ('TaskLists/Edit/x'), but the 'Add' links comes out 'TaskLists/Edit'.
Is there a way I can have my cake and eat it?
Using named routes (Html.RouteLink("linkText", "routeName")) could be a cleaner way of defining this, as then you're dealing with a clean route in the view also. This would also mean that you will hit the correct route every time without worry.
Update the Name parameter on your routes accordingly, then add the following to your view:
Html.RouteLink("Add task list", "NewTaskList")
and
Html.RouteLink(taskList.Name, "EditTaskList", new { Id = taskList.Id })

MVC3 routing issue with nullable parameter

I have an odd issue. I have a controller action which takes a couple of optional parameters
Function Index(sectionID As Integer?, title As String) As ActionResult
Return View()
End Function
I then have added a specific route for this action method so that we get pretty urls for this page
routes.MapRoute( _
"By_Section", _
"home/{sectionID}/{title}", _
New With {.controller = "Home", .action = "Index", .sectionID = Nothing},
New With {.sectionID = "\d+"}
)
This all works. However, when I am on a page where the sectionID is set (for example http://localhost/home/index/1/test), the following piece of code produces an odd output.
<%= Url.Action("Index", "Home")%>
Instead of showing http://localhost/home/index as you might expect, it shows http://localhost/home/index/1/test. So it appears it is picking up the sectionID and title from the current url and automatically inserting them into the Url.
How can I prevent this from happening?
Thanks
James
Yes, this is expected behaviour, the routing system will reuse parameter values from the current request if you haven't provided a new value explicitly. The best option when rendering links is to specify explicit values for all of your routing parameters.
<%= Url.Action("Index", "Home", new { sectionID = (int?)null }) %>

After adding a top level route in aspnet mvc an unrelated form post quit working

Initially this demo application I'm working on had just one page with a simple form to login
<form action="/Login/AuthenticateUser" method="post">
<label for="username_or_email">Username or email</label>
<input id="handle" name="handle" type="text" value="">
<label for="password">Password</label>
<input id="pass" name="pass" type="password" value="">
</form>
And 2 routes defined
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"User",
"{controller}/{handle}",
new { Controller = "User", action = "Index", handle = UrlParameter.Optional }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Post", action = "Index", id = UrlParameter.Optional }
);
}
This was working without issue and has the following controller method associated
[HttpPost]
public ActionResult AuthenticateUser(FormCollection collection)
{
//some code here ...
return View("Index");
}
Later it was decided that I needed a route to view user information directly so I wanted something like http://localhost/User/handle
So I crafted a route and I can view this without issue. But the next time I attempted to login with the original form (above) it never hits the http post method in my controller (but instead the http get "Index" method). also to note - after this post occurs the url in the browser appears to be correct, in this case showing http://localhost/Login/AuthenticateUser.
Strange behavior undoubtedly related to the new route. Also to note - I don't have a route defined for this auth user method.
Any way I can have these work together? In addition what caused this strange behavior? Is the route itself not correct?
Thank you in advance
The following route:
routes.MapRoute(
"User",
"{controller}/{handle}",
new { Controller = "User", action = "Index", handle = UrlParameter.Optional }
);
Is routing all your requests to the Index action by default. You have not allowed for overriding the action route value, only the handle route value. So /Login/AuthenticateUser takes you to the Index action of Login controller with handle as AuthenticateUser. The route below it will never be used because {controller}/{handle} covers pretty much all requests.
I'm not sure why it worked the first time, but it should have never worked. Could be that you didn't build the application after you added the route I quoted above.
Update:
If you want to use something as general as {controller}/{string}, you'll need to make sure that no other routes conflict with it and still have all other controllers/actions route properly. I would recommend against using something as general as {controller}/{string}, but one way around that is if you make your general routes look like this:
routes.MapRoute(
"User",
"Page/{controller}/{action}",
new { controller = "User", action = "Index" }
);
And place this above the {controller}/{string}. Again, having a very general route map to something very specific can cause a headache in the future.
Install this route debugger and use it to check what your routes look like:
http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx

Default route for all extreme situations

In my routing I would like to have something like not found route handler.
For example I have created one mapping like
routes.MapRoute(
"default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id="" }
);
routes.MapRoute(
"Catchall",
"{*catchall}",
new { controller = "Home", action = "Lost" }
);
but when user inserts address something like /one/two/three/four/bla/bla it will be cached with the Catchall mapping.
But when user inserts something that should match with default mapping,
(like /one/two/ ,but this controller or action is not implemented)
I would want that Catchall mapping would accept this request,
because all other mappings failed. But instead of this I get an error.
Should I override some mapping handlers to catch the exception if controller or action getting an exception?
The issue here is that it's not the Route's responsibility to make sure that "one/two" maps to a file. That responsibility falls to the ViewEngine. Since "one/two" is a valid route, it will be chosen.
If you want to handle the error of an invalid route, what I would recommend you do is simply use the built in ErrorHandling "page" to display whatever message you would have done in the Catchall.
I don't think this is the best solution, but you could always be more specific on your routes:
routes.MapRoute(
"home and action",
"home/index/{id}",
new { controller = "Home", action = "Index", id="" }
);
... repeat for the rest of your actions ...
routes.MapRoute(
"article catch all",
"home/{article}",
new { controller = "Home", action = "ArticleSearcher", article="" }
);
This would attempt to match a direct action, and if no action is found, pass what would normally be the {action} part of the default route to the 'ArticleSearcher' action as an article string parameter.
The downside is having to explicitly create each controller/action route.

Resources