How do you override route table default values using Html.ActionLink?
Global.asax route values
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, filterDate = DateTime.Now.AddDays(-1), filterLevel = "INFO" } // Parameter defaults
);
Here's my actionlink
@Html.ActionLink(item.MachineName, "Machine", new { id = item.MachineName, filterLevel = "hello" }, null)
When the filterlevel is specified in the actionlink, it generates a url like this:
http://localhost:1781/LoggingDashboard/log/level/VERBOSE
Which is the same page as I am currently on. If I change the actionlink to use a property other than one that has a default value in the route table (yes, if I use filterDate it messes up too), it generates a link like this:
@Html.ActionLink(item.MachineName, "Machine", new { id = item.MachineName, foo = "bar" }, null)
http://localhost:1781/LoggingDashboard/log/Machine/C0UPSMON1?foo=bar
Is this behavior correct? Should I not be able to override the defaults set up开发者_运维问答 in the route table? I have confirmed that if I remove the filterLevel default from the route table this works as I expect:
http://localhost:1781/LoggingDashboard/log/Machine/C0UPSMON1?filterLevel=VERBOSE
---EDIT--- sorry, here is the action
public ActionResult Machine(string id, DateTime filterDate, string filterLevel)
{
...
var model = new LogListViewModel { LogEntries = logEntries };
return View(model);
}
For the bounty I want to know how to override the "default" values that are specified in the routes from global.asax. i.e. I want to be able to override filterLevel and filterDate.
SLaks already said what is probably the best way to handle this problem. I don't know if this will work, but, what happens if you put this above the existing route (so there would be two in your global.asax now)?
routes.MapRoute(
"Filtered",
"{controller}/{action}/{id}?filterLevel={filterLevel}&filterDate={filterDate}",
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional,
filterDate = DateTime.Now.AddDays(-1),
filterLevel = "INFO"
}
);
Also, it just occurred to me that the reason you don't like SLaks' solution is that it could be repetitive. Since you only have one route, these parameters probably indicate a global functionality, instead of an action-scoped functionality. You could fix this by adding the values in an action filter on each controller, or your could use a custom route handler to apply this globally. Either of these would allow you to take the filterLevel
and filterDate
fields out of your route definition and still get the scope you want. Then it should be no problem to pass the parameters in a querystring with Html.ActionLink()
.
To do this with the route handler, change your route definition to:
routes.Add(
new Route(
"{controller}/{action}/{id}",
new RouteValueDictionary(new{ controller = "Home", action = "Index", id = UrlParameter.Optional}),
new CustomRouteHandler()));
Your implementation of the route handler would be something like this:
public class CustomRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var routeValues = requestContext.RouteData.Values;
if(!routeValues.ContainsKey("filterLevel"))
{
routeValues.Add("filterLevel","INFO");
}
if(!routeValues.ContainsKey("filterDate"))
{
routeValues.Add("filterDate", DateTime.Now.AddDays(-1));
}
var mvcRouteHandler = new MvcRouteHandler();
return (mvcRouteHandler as IRouteHandler).GetHttpHandler(requestContext);
}
}
I thought the defaults were always for entries defined in the URL, that you can't define a default to omit something not in the core URL, and anything else is passed as a querystring.
Interesting question.
HTH.
You should specify the default value in your method, like this:
public ActionResult Machine(string id, DateTime? filterDate = null, string filterLevel = "INFO")
{
filterDate = filterDate ?? DateTime.Now.AddDays(-1);
var model = new LogListViewModel { LogEntries = logEntries };
return View(model);
}
If there are default values that are not specified in the URL pattern, then you can't override them because they are used to determine route selection when matching routes for URL generation.
Let me give you an example. Suppose you had the following two routes.
routes.MapRoute(
"Default1", // Route name
"foo/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional
, filterLevel = "INFO" } // Parameter defaults
);
routes.MapRoute(
"Default2", // Route name
"bar/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional
, filterLevel = "DEBUG" } // Parameter defaults
);
Notice that there's a default value for "filterLevel", but there is no "{filterLevel}" parameter within the URL pattern.
Which URL should match when you do this?
@Html.ActionLink(item.MachineName, "Machine",
new { id = item.MachineName, filterLevel = "RANDOM" }, null)
If you could override the default value for filterLevel, then you'd expect both of the routes to match. But that doesn't make sense. In this case, neither matches because filterLevel isn't in the URL pattern and therefore the supplied filterLevel must match the default value. That way, you can do this:
@Html.ActionLink(item.MachineName, "Machine",
new { id = item.MachineName, filterLevel = "INFO" }, null)
//AND
@Html.ActionLink(item.MachineName, "Machine",
new { id = item.MachineName, filterLevel = "DEBUG" }, null)
to generate a URL for the first and second route respectively.
This confusion is why I always recommend to always use named routes.
精彩评论