Why would this route result in 404 Not Found when hitting url /users/3 to call upon the route with a page number only in ASP.NET MVC:
routes.MapRoute(
"Users", // Route name
"users/{page}/{sortColumn}/{sortDirection}", // URL with parameters
new { controller = "User", action = "Index", page = UrlParameter.Optional, sortColumn = UrlParameter.Optional, sortDirection = UrlParameter.Optional }, // Parameter defaults
new { page = #"\d+", sortColumn = #"[\w-]+", sortDirection = #"asc|desc" } // Route constraints
);
Shouldn't it be okay for me to load the route simply with the page parameter specified, as the sortColumn and sortDirection parameters are both set to UrlParameter.Optional?
Update:
Ok after digging up another StackOverflow answer on a similar topic, it appears if you choose to have an optional route, the constraint must also be optional. So changing my route constraints to:
new { page = #"\d*", sortColumn = #"[\w-]*", sortDirection = #"(asc|desc)?" }
which simply tests for zero or more matches makes my pages load up on all accounts.
However, if I try to make a route link that reflects that (#Html.RouteLink("test 2", "Users", new { page = 2 })), the hyperlink is generated as /users not /users/2! Strange thing though, if I manually hit /users/2 then the RouteLink is written as /users/2 lol Argh! :)
Perhaps I do have to make separate routes, but this is really not desirable as I don't want to have to call upon differently named routes in Html.RouteLink()....
Thoughts?
Because you provide two parameters, but it expects 3 optional ones.. so it does not know which one is missing ...
In general you cannot use two consecutive optional parameters.
References
http://weblogs.asp.net/imranbaloch/archive/2010/12/26/routing-issue-in-asp-net-mvc-3-rc-2.aspx
http://connect.microsoft.com/VisualStudio/feedback/details/630568/url-routing-with-two-optional-parameters-unspecified-fails-on-asp-net-mvc3-rc2
Related
I've been working on a large MVC application over the past month or so, but this is the first time I've ever needed to define a custom route handler, and I'm running into some problems. Basically I have two parameters to pass. The first one is required and the second one is optional.
I'm following this answer here.
Here is my custom route:
routes.MapRoute(
"MyRoute",
"{controller}/{action}/{param1}/{param2}",
new {
controller = "MyController",
action = "MyAction",
param1 = "",
param2 = "" // I have also tried "UrlParameter.Optional" here.
}
);
And my action method signature:
public ActionResult MyAction(string param1, string param2)
If I try the URL http://[myserver]/MyController/MyAction/Test1/Test2 then it works like I expect it to, with param1 = "Test1" and param2 = "Test2"
If I try the URL http://[myserver]/MyController/MyAction/Test1 then both parameters are null.
Hopefully somebody can tell me what I'm doing wrong here, because I'm lost.
I assume that you created new route and left the default one that is very similar to yours. You should be aware that collection of routes is traversed to find first matching route. So if you have left the default one:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
above your route then it will match request to http://[myserver]/My/MyAction/Test1 and call MyController.MyAction and set "Text1" to parameter named id. Which will fail because this action is not declaring one named id.
What you need to do is to move your route as first in routes list and make it more specific then it is now:
routes.MapRoute(
"Route",
"My/{action}/{param1}/{param2}",
new
{
controller = "My",
action = "MyAction",
param1 = "",
param2 = ""
});
This will force all traffic routed trough My to match this route.
hi you create your rout like this i think this will hep you
routes.MapRoute(
"Regis", // Route nameRegister
"Artical/{id}", // URL with parameters
new { controller = "Artical", action = "Show", id = UrlParameter.Optional } // Parameter defaults
);
Try this
routes.MapRoute("MyRoute",
"myRoute/{param1 }/{param2 }",
new { controller = "MyController", action = "MyAction", param2 = UrlParameter.Optional },
new { param2 = #"\w+" });
you can specify one parameter as optional by using "UrlParameter.Optional" and specified second one with DataType means if you pass integer value then DataType (#"\d+") and for string i have mention above.
NOTE: Sequence of parameter is very important Optional parameter must pass at last and register your new route Before Default Route In Gloab.asax.
then you action link like
Test
OR with one parameter
Test
In you Controller
public ActionResult MyAction(string param2,string param1)
{
return View()
}
I have a controller named Raportare that has two actions: ReportA and ReportB.
Both return an excel file based on the parameters supplied.
public ActionResult ReportA(int? month, int? year)
{
...
}
public ActionResult ReportB(int? month, int? year)
{
...
}
My global.asax has the following routing rules for this :
routes.MapRoute(
"ReportA",
"{Raportare}/{ReportA}/{month}/{year}",
new { controller = "Raportare", action = "ReportA", month = UrlParameter.Optional, year = UrlParameter.Optional});
routes.MapRoute(
"ReportB",
"{Raportare}/{ReportB}/{month}/{year}",
new { controller = "Raportare", action = "ReportB", month = UrlParameter.Optional, year = UrlParameter.Optional });
However when I go mysite.com/Raportare/ReportB/5/2012 it's returning the ReportA file.
It works fine if I go to mysite.com/Raportare/ReportB?month=5&year=2012.
Probably I'm doing something wrong in the routing rules but I can't figure it out.
You don't need to add a route for every action - they work like templates and the 3rd parameter is just default values.
routes.MapRoute(
"reports",
"Raportare/{action}/{month}/{year}",
new {
controller = "Raportare",
action = "ReportA",
month = UrlParameter.Optional,
year = UrlParameter.Optional
}
);
Put this before your default rule in Global.asax.cs, otherwise the default might match first.
Then mysite.com/Raportare/ReportB/5/2012 will invoke the ReportB action because it has been specified in the url.
mysite.com/Raportare will invoke ReportA, because it is the default action.
The routes you've created are basically the same. That's why the first one which matches request string succeeds and first action gets invoked.
Here I have:
routes.MapRoute(
"test", // Route name
"DataWarehouse/Distribution/{category}/{serialNo}",
new { controller = "DataWarehouse",
action = "Distribution",
category= UrlParameter.Optional,
serialNo = UrlParameter.Optional }
);
Category and serialNo are both optional params. When the routing is like: DataWarehouse/Distribution/123, it always treat 123 as the value for category.
My question is how I can make it to know the 1st param could be either category or serialNo, i.e. DataWarehouse/Distribution/{category} and DataWarehouse/Distribution/{serialNo}.
DataWarehouse/Distribution/{category}/{serialNo}
Only the last parameter can be optional. In this example category cannot be optional for obvious reasons.
If you know what your parameters will look like you can add a route constraint to differentiate both routes
Ex if your serial are 1234-1234-1234 and your category are not like this:
routes.MapRoute(
"serialonly", // Route name
"DataWarehouse/Distribution/{serialNo}",
new { controller = "DataWarehouse",
action = "Distribution",
category= UrlParameter.Optional,
serialNo = UrlParameter.Optional },
new{serialNo = #"\d{4}-\d{4}-\d{4}"}
);
routes.MapRoute(
"test", // Route name
"DataWarehouse/Distribution/{category}/{serialNo}",
new { controller = "DataWarehouse",
action = "Distribution",
category= UrlParameter.Optional,
serialNo = UrlParameter.Optional },
,
new{serialNo = #"\d{4}-\d{4}-\d{4}"}
);
I had a similar problem, i was trying to route based on data ({year}/{month}/{day}) where month or day could be optional. What I found was that I couldn't do it with a single route. So I solved it by using 3 routes, from generic to specific (year, year and month, year and month and day). I am not completely pleased with it, but it works.
So provided that you are looking for DataWarehouse/Distribution/{category} and DataWarehouse/Distribution/{category}/{serialNo} routes, i think this would work for you.
I have a custom route without constraints that generates a Restful URL with an ActionLink.
Route -
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" }
Generates -
http://localhost:2875/Blog/12/1/2010
From -
<%=Html.ActionLink("Blog Entry - 12/01/2010", "Retrieve", "Blog", new { d = 12, m = 01, y = 2010 }, null)%>
If I add constraints like so.
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" },
new { d = #"\d{2}", m = #"\d{2}", y = #"\d{4}" }
It generates -
http://localhost:2875/Blog/Retrieve?d=12&m=1&y=2010
Extra information: it is added before the custom route.
Any ideas?
Cheers
I was working on the same issue while writing my blog.. In the end I realised that my Urls will have to use 1 digit month numbers.. change your route definition to this, and it will work:
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" },
new { d = #"\d{1,2}", m = #"\d{1,2}", y = #"\d{4}" }
Or you can pass 2 digit strings as your day/month route values.. but you might miss this in some places and have dead links, so I'd recommend the route constraints fix..
If you DO find a workaround - drop me a mail pls ^_^
Artiom is essentially right. Since your ActionLink code specifies single digit integers in the route values, the single digit fails against your constraint. So, you can either change the constraint as Artiom suggests, or slightly modify the ActionLink code so the route values are "strings" (in double quotes):
Html.ActionLink("Blog Entry - 12/01/2010", "Retrieve", "Blog", new { d = "12", m = "01", y = "2010" }, null)
I have a question regarding RouteLink vs. ActionLink.
Consider the following route
routes.MapRoute("Routename1",
"{someEnum}/SpecificAction/{id}/{stringId}",
new { controller = "MyController", id = (int?)null, stringId= (string)null, action = "SpecificAction" },
new { someEnum= "(EnumVal1|EnumVal2)" }
);
The weird {someEnum} part is because I use a general controller for all values of an enum that form the typical controller part of a url. For instance, /EnumVal1/Action/ and /EnumVal2/Action/ use the same controller. That's not part of the problem, however.
Consider the following two ways of linking:
<%=Html.RouteLink("Click me","Routename1", new { id = 32, stringId = "Yatzy" })%>
<%=Html.ActionLink("Click me", "SpecificAction", "EnumVal1", new { id = 32, stringId = "Yatsy" }, null)%>
The RouteLink generates the correct url, which would be /EnumVal1/SpecificAction/32/Yatzy
The ActionLink generates an url that looks like /EnumVal1/SpecificAction/32?stringId=Yatzy
Why is this? Could someone explain this to me, please.
RouteLink can only ever use the one route you specify. ActionLink will use the first matching route, whether it's the one you intended or not. Your two examples are probably matching different routes.
Phil Haack's routing debugger would help clarify this.