Generate wrong outgoing url because of segment variable reuse in MVC 4 - asp.net-mvc-4

Here is my RouteConfig.cs
routes.MapRoute(null,
"{controller}/Page{page}",
new {controller = "Product", action = "Index", category = (string) null},
new {page = #"\d+"}
);
routes.MapRoute(null,
"{controller}/{category}",
new {controller = "Product", action = "Index", page = 1}
);
routes.MapRoute(null,
"{controller}/{category}/Page{page}",
new {controller = "Product", action = "Index"},
new {page = #"\d+"}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And here is the code for generating url:
#Html.ActionLink("View Cart", "Index", "ShoppingCart", null, new { #class = "btn btn-orange" })
It works well when I navigate to, for example, Product/Page2, Product/Laptop, Product/Laptop/Page2. The problem is, whenever my current URL contains Page segment, it will try to reuse that segment for generating outgoing URL. So, if I'm at Product/Page2 the above generated URL would be ShoppingCart/Page2. I don't know how to avoid this.
Please help me. Thank you so much.
EDIT!!!
I've found a workaround way. Instead of using ActionLink, I use RouteLink like this:
#Html.RouteLink("View Cart", "Default", new { controller = "ShoppingCart", action = "Index" }, new { #class = "btn btn-orange" })
But I still want to use ActionLink, so please help me.
EDIT!!!
The workaround way doesn't work when I generate a link to ShoppingCart/Checkout. It still take me to Index action in ShoppingCart controller.

Create a new route pattern specific to ShoppingCart and make it the first route by placing it at the TOP.
routes.MapRoute(null,
"ShoppingCart/{action}",
new {controller = "Product"});
);
As a rule all the specific routes should come first.

This is because of the way the routing system tries to evaluate the value of segment variables when trying to match against a route.
So when the call to render the link occurs with the following arguments:
#Html.ActionLink("View Cart", "Index", "ShoppingCart", null, new { #class = "btn btn-orange" })
the framework when evaluating the route with template
{controller}/Page{page}
will resolve the controller segment variable to be ShoppingCart however when it cannot find a value for the page segment variable (via any of the arguments in the method call), it will then try and resolve that value from the RouteData object in the ViewContext. Since you have navigated to Product/Page2, the current value of page within the routes value dictionary is 2.
You can inspect this by looking at the value of ViewContext.RouteData.Values["page"] when rendering that view.

Related

Optional middle parameter in MVC4 Route

I am trying to create a controller action that receives several parameters for filtering a scope and then the final parameter is a required paging parameter for paging the results.
//Example Action
public ActionResult Details(string time, string regionscope, string localscope = null, int page = 1) {
}
//RouteConfig.cs
routes.MapRoute(
name: "LocationWithLocalScopeRoute",
url: "/Details/{time}/{regionscope}/{localscope}/{page}",
defaults: new
{
controller = "Location",
action = "Details",
localscope = UrlParameter.Optional
}
);
Is there anyway I can exclude the localscope parameter but still have the page parameter be interpreted as the page number by the controller (while still maintaining pretty URLs)?
// Test URLs
/Details/May-17/Mid/1
/Details/May-17/Mid/Bumville/1
I know I could add a route before the existing route:
//RouteConfig.cs
routes.MapRoute(
name: "LocationWithNoLocalScopeRoute",
url: "/Details/{time}/{regionscope}/{page}",
defaults: new
{
controller = "Location",
action = "Details"
}
);
I also realize I could put page before any of the other parameters.
routes.MapRoute(
name: "LocationWithLocalScopeRoute",
url: "/Details/{page}/{time}/{regionscope}/{localscope}",
defaults: new
{
controller = "Location",
action = "Details",
localscope = UrlParameter.Optional
}
);
Was just wondering if there was a way to make it work all in one route definition while still having the Test URLs work.
No, not without creating another specific route or making localscope the last parameter.
When using routing, only the last parameter can be marked as UrlParameter.Optional. If more that one is optional, then the routing engine has no way to know which route segment applies to which method parameter.
In your case when the url is ../Details/May-17/Mid/1, is the last segment with the value of 1 the value for localscope or the value for page? There is no way to determine that, so if you were to generate the url using #Url.Action() (or one of the other methods that generate url's), then the url will be generated with query string values, not route values.

Create Link to level up controller using Razor in ASP.NET MVC4

This is my code that i'm using to create a link to top level link:
#Html.ActionLink("Edit",Url.Action("Edit","Home", new{row.Application_ID}))
// supposed to return /Home/Edit/x (application_id)
Instead, it returns:
/Home/Home/Edit/x // Current View is Home/Edit
Please help.
Edit 1:
Route Table information:
//This is for Edit
routes.MapRoute(
"Edit", // Route name
"Home/Edit/{application_id}", // URL with parameters
new { controller = "Home", action = "Edit", application_id = UrlParameter.Optional } // Parameter defaults
);
// This is the default route, I've modified it to LogOn.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Account", action = "LogOn", id = UrlParameter.Optional } // Parameter defaults
);
As a small edition to haim770 answer. if you want for some reason use Url.Action instead Html.ActionLink you are always free to write it like that:
Edit
Some developers prefere this way because it more similar to plain Html, but result is the same as Html.ActionLink
You don't need the inner #Url.Action, try this instead:
#Html.ActionLink("Edit", "Edit", "Home", new { application_id = row.Application_ID }, null);
tried this work around and it worked:
Edit
now it returns the link as Home/Edit/x :)

MVC4 URL Routing : Optional parameter

In my MVC4 application, I want to show the data to users according to the client. I want to achieve this using URL routing.
e.g.
1. http://mysite.com/abc/account/login
2. http://mysite.com/xyz/account/login
Here, 'abc' and 'xyz' are referred as clientcode. If user enters first url, then the application will connect to database for client 'abc'. If user enters second url, then it will connect to database for client 'xyz'.
I want to achieve the above functionality. So how should i make use of routing.
I have used following code in RouteConfig -
routes.MapRoute(
name: "Default",
url: "{clientcode}/{controller}/{action}/{id}",
defaults: new { clientcode = UrlParameter.Optional, controller = "Account", action = "Login", id = UrlParameter.Optional }
);
Thanks in advance.
You can only make the last parameter optional in ASP.NET MVC. Instead of changing the default route, create a new route for this specific URL. Make sure you add this one before the default route.
routes.MapRoute(
name: "Login",
url: "{clientcode}/account/login",
defaults: new { clientcode = UrlParameter.Optional, controller = "Account", action = "Login" }
);
You can achieve this by reading ControllerContext.RouteData. The RouteData object contains Values RouteValueDictionary that you can read and query for clientcode. For example:
var clientCode = ControllerContext.RouteData.Values["clientcode"];
if (String.IsNullOrWhiteSpace(clientCode))
{
// Set to a default value
clientCode = "default_value";
}
// Do something with clientCode
return ViewResult(); // or RedirectToAction("ActionName")
That's about it.

What Causes the browser URL line to get filled with Route in MVC4 asp.net

When I go to my home page
http://localhost:5119/
it redirects to
http://localhost:5119/Home
(that is what shows in my browser url bar, I want it to not show /Home)
I understand that is where my default route goes but what I can't figure out what is causing my URL line to be rewritten in the browser. The default example in Visual Studio 2012 does not have this issue when you go to the base URL. I'm attaching a picture of my route debug (that does not seem to help me, but may have some value).
Thanks, -Peter
Adding This After. It is relevant parts of route code
// HOME
int currentYearInt;
Int32.TryParse(currentYear, out currentYearInt);
routes.MapRoute("HomeRouteAll", "Home/{yearInt}",
new
{
/* Your default route */
controller = "Home",
action = "Index"
});
// DEFAULT ROUTE
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You are seeing this because of the route Home/{year} above your default route that has a default value for the {year} parameter.
The route engine makes the following decisions:
Start at top of route list
Look for something that matches what your route values (controller = "Home", Action="Index").
At the first match, return and that is your URL.
Since you have a matching controller (Home) and action (Index) as well as a default value for the year parameter, the route engine is matching to the route Home/{year}, thus giving the url http://domain.com/Home.
The quick fix would be either a) make year not have a default value (Home/2013), b) move whatever that is over to a different controller (NewName/{year}), c) move that to a different action (NewIndex/{year}) or d) update your default route to use the year parameter instead of id
routes.MapRoute(
"Default",
"{controller}/{year}/{action}",
new {controller = "Home", action = "Index", year = 2013});
EDIT
I am not really sure what you have as far as the tryParse stuff in your route definitions, but in my testing, this seemed to accomplish what you are wanting to do:
routes.MapRoute(
name: "Test",
url: "Home/{year}",
defaults: new { controller = "Home", action = "Index"},
//this line causes the year to be an integer
constraints: new { year = #"\d+" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Behaviors:
http://domain.com/ -> calls Home controller, Index action with a null value for the year parameter (no redirect to Home/2013)
http://domain.com/Home/2010 -> calls Home Controller, Index action with 2010 for the year parameter
http://domain.com/Home/ -> call Home Controller, Index action with null value for the year.
If you define a default year in the first of the two routes, then going to http://domain.com/Home will call Home controller, Index action with 2013 for the year and the redirect will still not occur.
Lastly, I suspect your Home/Index action looks something like this:
public ActionResult Index(int year){...}
If when you hit the Home/Index action, you want to automatically have the 2013 populated, then change your int to a nullable parameter and do it there rather than your routes.
public ActionResult Index(int? year){
if(!year.hasValue){
year = 2013;
}
By performing this logic here instead of the route, you should prevent the redirect to Home/ as you have no matching {year} parameter.

Asp.net MVC custom routing with SEO

routes.MapRoute(
"Route",
"{id}/{*seostuff}",
new {controller = "Home", action="Index", seo = UrlParameter.Optional});
that will allow you to map urls such as http://www.somesite.com/11/whatever/goes-here/will-be-whatever-you/want
Here is the original post Asp.net MVC custom routing
Hi guys!
-what I want to know is how can this be code in controller? I have a static page like this Product/Phone/i-phone.aspx which is under the product it has a folder phone.. . any suggestion guys? thank you very much.. .
You can define the route you have described...
routes.MapRoute(
"Route", // ShopsToRent/0/B31 5EH/9/0/0/0/0/0
"{id}/{seo}", // URL with parameters
new
{
controller = "ControllerName",
action = "ActionName",
page = UrlParameter.Optional,
title = ""
} // Parameter defaults
);
I personally prefer to have a Keyword at the start of the url as this gives you an additional keyword (eg www.keywords.com/keywords )and allows future additions to the site...
routes.MapRoute(
"Route", // ShopsToRent/0/B31 5EH/9/0/0/0/0/0
"KEYWORD/{id}/{seo}", // URL with parameters
new
{
controller = "ControllerName",
action = "ActionName",
page = UrlParameter.Optional,
title = ""
} // Parameter defaults
);