Route value removed but still added as query string parameter - asp.net-mvc

I am extending a model to include a link property. It is based on other properties within it to generate a link:
public partial class MyModelType
{
public string FilterLink
{
get
{
var u = new UrlHelper(HttpContext.Current.Request.RequestContext);
var route = new RouteValueDictionary(u.RequestContext.RouteData.Values);
route.Remove("other");
route.Add("other", null);
route.Add("id", this.Id);
return u.Action("Index", "ControllerName", route);
}
}
}
The link generated is /ControllerName/1?other=2. If I get rid of the route.Remove("other") I get the link based on the mapped route: /ControllerName/1/2. How can I prevent it being used as a query string parameter when it is removed from the route? The UrlHelper seems to be adding it somehow, even though it is not a route value.

Check your routing configuration.
MVCs routing will append any unkown parts of your route in that way, so as long as you define it up front it should work just fine.
You probably just have this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Try adding the option like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{option}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, option = UrlParameter.Optional }
);

The issue is discussed here: http://erraticdev.blogspot.com/2011/03/removing-route-values-from-linksurls-in.html
However, the blogs proposal is convoluted compared with a simple solution in the comments: add "other = null as object" to the defaults values on your DEFAULT route.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, other = null as object }
);

Related

ASP.Net MVC: Need guide line for a specific routing

My routing code is not working.
i am showing data in tabular format with sorting and pagination. my solution is working. when i hover mouse on column then url looks like http://localhost:55831/Customers?page=2&SortColumn=CompanyName&CurrentSort=ContactName
when i click on pagination numeric link then url looks like http://localhost:55831/Customers?page=3&SortColumn=ContactName
i want my url should look like
1) http://localhost:55831/Customers/2/CompanyName/ContactName
2) http://localhost:55831/Customers/3/ContactName
so i add one routing code. here it is
routes.MapRoute(
name: null,
url: "Customers/{page}/{SortColumn}/{CurrentSort}",
defaults: new
{
action = "Index",
page = UrlParameter.Optional,
SortColumn = UrlParameter.Optional,
CurrentSort = UrlParameter.Optional
}
);
after adding the above routing code url looks bit weird. now url looks like
http://localhost:55831/Customers/1/CompanyName/CompanyName?controller=Customers
http://localhost:55831/Customers/2/CompanyName?controller=Customers
so when i click on above links then i am not redirecting to proper controller and action rather getting error.
so it means there is some problem in code which i added as routing in route.config.cs file.
so please help me to get my desired url what i mention above. thanks
EDIT
my full routing code
routes.MapRoute(
name: null,
url: "{controller}/{action}/{page}/{SortColumn}/{CurrentSort}",
defaults: new
{
action = "Index",
page = UrlParameter.Optional,
SortColumn = UrlParameter.Optional,
CurrentSort = UrlParameter.Optional
}
);
routes.MapRoute(
name: null,
url: "{controller}/{action}/{page}/{id}",
defaults: new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
page = UrlParameter.Optional,
}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This won't work because your routes have optional parameters which conflict with other routes.
For example take the url /MyController/MyAction/MyPage/213.
How would the routing be able to determine if you want: {controller}/{action}/{page}/{id} or {controller}/{action}/{page}/{SortColumn}/{CurrentSort}.
One solution might be to remove the optional parameters:
page = UrlParameter.Optional,
SortColumn = UrlParameter.Optional,
CurrentSort = UrlParameter.Optional
So your routes would look more like this:
routes.MapRoute(
name: "PageWithSort",
url: "{controller}/{action}/{page}/{SortColumn}/{CurrentSort}",
defaults: new { action = "Index" }
);
routes.MapRoute(
name: "PageWithId",
url: "{controller}/{action}/{page}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
However, here you are still going to get a conflict with PageWithId and the Default routes. So if you never use id in the Default routing then you might want to remove the optional parameter there. Or, if all routes with page always have an id then you could remove the optional parameter there. Alternatively, if page and id are truely optional you may consider removing the default route as PageWithId with the two optionals would handle both.
Side note: You should always name your routes as later it will allow you to do things like this:
#Html.RouteLink("Link Text", "RouteName", new { controller = "xxx" })
From your provided code you might consider changing
#Html.ActionLink("Company", "Index", new { page = ViewBag.CurrentPage, SortColumn = "CompanyName", CurrentSort = ViewBag.CurrentSort })
To
#Html.RouteLink("Company", "PageWithSort", new { controller = "Customers", action = "Index", page = ViewBag.CurrentPage, SortColumn = "CompanyName", CurrentSort = ViewBag.CurrentSort })

Show "ID" word in url in MVC

Edit
In a href, I've pass id as parameter. so when I redirected to action I have following url
http://localhost:17888/Receptionist/UpdateCase/12
But I want id word visible in url.
http://localhost:17888/Receptionist/UpdateCase?id=12
RouteConfig
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If I change parameter name rather than id, it is working. only id is not visible in url
UpdateCase Action:
[Authorize]
[HttpGet]
public ActionResult UpdateCase(Int64 id)
{
}
One of possible solution is change your route config to
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{param}",
defaults: new { controller = "Home", action = "Index", param = UrlParameter.Optional }
);
In that case you won't have a parameter with name id in the route table, so Url.Action can't use it and as a result you wil get expected url.
But be careful and check others actions
Question marks are added when the parameter is not specified in the URL, so removing the {id} from your route will force the question mark to be added in the URL.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

MVC 4 - Remove /Home/ from address bar and retain folder name for others

I am using MVC 4 and need to remove /Home/ folder from address bar...
Eg:
http://localhost:61700/Home/AboutUs
Need to be changed as...
http://localhost:61700/AboutUs
I did that by changing the default controller in "RouteConfig.cs"
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
//url: "{controller}/{action}/{id}",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
The above code is working as expected. I do have another folders as
brand, admin etc... here I want to show the url as
http://localhost:61700/brand/productInfo ... But I am getting server
error here as Server Error in '/' Application.
Can somebody suggest me, where am I doing wrong?
Screenshots here for more info:
This is your current RouteConfig.cs configuration:
routes.MapRoute(
name: "Default",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You're telling Asp.net, when a request arrives, assume the first parameter as the action and the second parameter as the id. Right now you're not telling Asp.net to parse any parameter as the controller. Because of this it uses the default value (given as the third parameter of the MapRoute method) which is in this case Home.
In that case when parsing the request http://localhost:61700/AboutUs the values end up being:
controller: Home (it uses the default controller)
action: AboutUs (from the first parameter)
id: null (this doesn't matter right now)
When parsing the request http://localhost:61700/brand/productInfo the values end up being:
controller: Home (it uses the default controller because you haven't specified where to get the controller name from)
action: Brand (from the first parameter)
id: "productInfo"
The error you're getting is because there isn't a Brand action method in HomeController.cs with a parameter of type string named id.
Asp.net processes incoming requests by trying to match with the routes configured and it uses the first route that matches.
There are several ways to achieve what you want, which include but are not limited to:
Manually mapping every action in your HomeController.cs (choosing this method will depend on the amount of actions in your HomeController). This would look like:
routes.MapRoute(
name: "AboutUs",
url: "AboutUs",
defaults: new { controller = "Home", action = "AboutUs" }
);
routes.MapRoute(
name: "ContactUs",
url: "ContactUs",
defaults: new {controller = "Home", action = "ContactUs" }
);
// etc...
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Note how the default route is the last one, this is important because it is less specific than the others and if put before would match the request and want to look for an AboutUsController.
You could use route constraints. This would look like:
route.MapRoute(
name: "HomeControllerRoutes",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { action = "AboutUs|ContactUs|etc..." } //Here you would put all your action methods from home controller that you want to accces as /{action}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you want to read more about route constraints, I found this article that explains that the constrains parameter can receive a regular expression (I suggest you modify the regular expression above to make it case insensitive) or an IRouteConstraint.
Update:
I just read your comment about having 160+ actions in your HomeController that would make your regular expression in my second suggestion quite long. In that case the other options you have could be:
Using a regular expression that rejects all other controller names, but that would violate the open/closed principle (OCP) and every time you add another controller you would have to add it to the regular expression.
Create the regular expression from the metadata of you HomeController class. This would look like
string.Join("|", typeof(HomeController).GetMethods().Select(info => info.Name))
Or you could take a look at IRouteConstraint to see if you could figure out a more elegant solution.
I have no experience with IRouteConstraint
Add this in your route.config / glibal.asax and don't change your default routes. Add following above it.
routes.MapRoute(
name: "About",
url: "AboutUs",
defaults: new { controller = "Home", action = "AboutUs" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have 160+ views in the home controller
You don't mention how many views you have in the other controllers, nor how complicated they need to be.
Rather than keep the default controller/action and add routes for every view in home, you can add a route for each controller and then have your default route without a controller path.
While this means you do need a route for every controller, it's better than one for every view.
routes.MapRoute(
"AdminRoute",
"Admin/{action}/{id}",
new { controller = "Admin", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"BrandRoute",
"Brand/{action}/{id}",
new { controller = "Brand", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"HomeRoute",
"{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(
"DefaultRoute",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
(afaicr you don't need the default route as all your views would be covered by the other 3 routes)
Note the path for 'HomeRoute' doesn't have a controller part.
As long as they are in this order any url with /Admin/ or /Brand/ will be picked up first.

URL not getting read correctly by MVC 4 routing

Hi I have a URL as follows in my MVC 4 application
http://localhost/ABC/Home/DeleteApp/3000
and I have configured RouteConfig.cs as follows
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
It goes to the Correct DeleteApp Controller but with the ID value Null. It shows the correct URL when I enable firebug.
What could possibly go wrong?
it debugs and come to here but the id field as null;
public void DeleteApp(string id)
{
try
{
// delete logic
}
catch (Exception e)
{
//
}
}
Possibles causes :
-> Check your "id" variable name (must be exactly the same name in the RouteConfig and in the controller action, it is case sensitive)
-> You have other routes mapped before this one that match the pattern
If you have a route mapped as {controller}/{action}/{something} and another route mapped as {controller}/{action}/{something_else}, when you call the url "Home/DeleteApp/3000", it can't tell if 3000 is "something" or "something_else" so it will take the first match.
To make this working, you have to use "Route Constraints"
http://www.codeproject.com/Articles/641783/Customizing-Routes-in-ASP-NET-MVC
You need to do it like so:
routes.MapRoute(
name: "ABC",
url: "ABC/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you want to have url in this format http://localhost/ABC then you will have to modify the route as below by adding ABC to {controller}/{action}/{id} expression.
routes.MapRoute(
name: "Default",
url: "ABC/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

MVC: Multiple Routes for 1 Controller

I want users to be able to access the "/Linecard" page of my ASP.Net MVC site using "/Linecard" or "/Manufacturers" as the URL... so same controller, 2 different possible URLs.
I tried adding the following:
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
Adding this after the "Default" route doesn't work at all and I get a 404 error when I go to "/Manufacturers". Putting it BEFORE "Default" works, but then only "/Manufacturers" shows up in the URL when I click menu links since it is the first match. I would like "/Linecard" to always show as the URL.
Any pointers? Is there a certain constraint I can use to accomplish this? Thanks!
I had the same problem when we moved to extension-less URLs. We needed to continue to support one route with extensions. I got around it by having my default route apply to everything except the old URL, then after that mapping one specifically for the exception
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// if controller specified does not match 'manufacturers' (case insensitive)
new { controller = "^((?i)(?!manufacturers).)*$" },
new string[] { "Namespace.Of.Controllers" }
);
routes.MapRoute(
"Manufacturers", // Route name
"Manufacturers/{action}/{id}", // URL with parameters
new { controller = "Linecard", action = "Index", id = UrlParameter.Optional },
new string[] { "Namespace.Of.Controllers" }
);
You could also set an order when mapping your routes with the defaults at the end like so
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

Resources