I'm creating a online log viewer application which reads logs generated by many applications into a single common database. Log types are error, fatal, debug and I use all to represent all the logs.
I've a controller named AppsController which should server Views for the following requests, where "bi-reports" is one of the many application names we have.
/apps/bi-reports/
/apps/bi-reports/all
/apps/bi-reports/error/
/apps/bi-reports/2011/04/
/apps/bi-reports/2011/04/all
/apps/bi-reports/2011/error
/apps/bi-reports/2011/04/error
/apps/bi-reports/all/last-hundred
/apps/bi-reports/all/most-hundred
/apps/bi-reports/2011/last-hundred
/apps/bi-reports/2011/04/all/last-hundred
How should I configure routes set parameters in Action methods of Controller to get this working?
This is rough idea of your routing definition. I can see that you basically have three types of routes:
routes.MapRoute(
"IrrelevantDates",
"{controller}/{application}/{type}/{range}",
// defaults
new {
controller = "Apps",
action = "UnboundReport",
type = "all",
range = "no-limit"
},
// constraints
new {
type = "apps|error"
}
);
routes.MapRoute(
"RelevantYearOnly",
"{controller}/{application}/{year}/{type}/{range}",
// defaults
new {
controller = "Apps",
action = "YearlyReport",
type = "all",
range = "no-limit"
},
// constraints
new {
year = "19\d{2}|2[01]\d{2}",
type = "apps|error"
}
);
routes.MapRoute(
"RelevantYearAndMonth",
"{controller}/{application}/{year}/{month}/{type}/{range}",
// defaults
new {
controller = "Apps",
action = "MonthlyReport",
type = "all",
range = "no-limit"
},
// constraints
new {
year = "19\d{2}|2[01]\d{2}",
month = "0[1-9]|1[0-2]",
type = "apps|error"
}
);
I've set year constraint to match years between 1900 and 2199, and months so they actually have to specify a valid month 01 to 12.
If you have any additional controllers you will have to define a default route as well and put controller constraint on this one or make controller name static (when just one applies).
I would do something along these lines
''# note, this is untested VB and might need some tweaking.
routes.MapRouteLowercase("Base", "",
New With {.controller = "Home",
.action = "Index",
.year = UrlParameter.Optional,
.paging = UrlParameter.Optional},
New With {.year = "[0-9]*"})
Then your controller would have something like
Function Index(ByVal paging As String, ByVal year As Integer?) As ActionResult
''# do your pre-processing for paging and year.
Return View()
End Function
Related
I have a route rule as:
routes.MapRoute("HotToursPage",
"HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}/{param4}/{param5}",
new
{
controller = "HotTours",
action = "Index",
countryTo = UrlParameter.Optional,
resort = UrlParameter.Optional,
param1 = UrlParameter.Optional,
param2 = UrlParameter.Optional,
param3 = UrlParameter.Optional,
param4 = UrlParameter.Optional,
param5 = UrlParameter.Optional
}
);
In the code I have:
var dictionary = new RouteValueDictionary();
aaa.Add("countryTo", countryToInfo.Translit);
aaa.Add("resort", resort);
aaa.Add("param1", param1);
string url = urlHelper.Action("Index", "HotTours", dictionary);
If there are param5, param6 and other, then
url =/hottours/?countryTo=tailand&resort=bangkok¶m1=price_from_50000,
but if i remove param5, param6 and other, then all ok:
url =/hottours/tailand/bangkok/price_from_50000
Why if segment count is less then 7, all ok? I need 9 segments, but urlHelper builds wrong url in this case.
When building URLs, you have to provide all of the route values that are in the URL pattern. There is one exception - when the last parameter is optional, you don't need to include it.
Therefore, to consistently deal with segments that could be optional in a long URL pattern, you need more than one route. Each route can only have one UrlParameter.Optional and it must be the right-most segment.
routes.MapRoute("HotToursPage3",
"HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}/{param4}/{param5}",
new
{
controller = "HotTours",
action = "Index",
param5 = UrlParameter.Optional
}
);
routes.MapRoute("HotToursPage2",
"HotTours/{countryTo}/{resort}/{param1}/{param2}/{param3}",
new
{
controller = "HotTours",
action = "Index",
param3 = UrlParameter.Optional
}
);
routes.MapRoute("HotToursPage1",
"HotTours/{countryTo}/{resort}/{param1}",
new
{
controller = "HotTours",
action = "Index",
param1 = UrlParameter.Optional
}
);
NOTE: I am assuming here that your {countryTo} and {resort} parameters are required. It doesn't seem that sensible to make them optional. However, if I am mistaken, you need another route to deal with those 2 segments being optional or alternatively you should provide sensible default values for them. Generally speaking, if there are no sensible defaults for a value it should be required in the URL.
Do note that you still can only make a segment optional if none of the segments to the right of it are provided. Therefore, this combination will work:
var dictionary = new RouteValueDictionary();
dictionary.Add("countryTo", "test1");
dictionary.Add("resort", "test2");
dictionary.Add("param1", "test3");
var url = Url.Action("Index", "HotTours", dictionary);
But this combination will still build a query string:
var dictionary = new RouteValueDictionary();
dictionary.Add("countryTo", "test1");
dictionary.Add("resort", "test2");
dictionary.Add("param1", "test3");
dictionary.Add("param2", "test4");
dictionary.Add("param5", "test5");
var url = Url.Action("Index", "HotTours", dictionary);
If you want all 5 of your params to be optional (and in any order), you should use query strings, rather than putting them into the path.
routes.MapRoute("HotToursPage",
"HotTours/{countryTo}/{resort}",
new
{
controller = "HotTours",
action = "Index"
}
);
An alternative (that I don't recommend) would be to build up a series of routes that have identifier segments, which allows you to place the values in any order. See ASP.Net MVC Handling Segments with Route.
Nothing like that as you are mentioning below is the main reason
Http.sys service is coded with default maximum of 260 characters per Url segment.
An "Url segment" in this context is the content between "/" characters in the Url. For example:
The max allowed Url segment length can be changed with registry settings:
Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters
Value: UrlSegmentMaxLength
Type: REG_DWORD
Data: (Your desired new Url segment maximum allowed length, e.g. 4096)
The maximum allowed value is 32766. If a larger value is specified, it will be ignored.
Restarting the PC is required to make a change to this setting take effect.
I have created a new route with that looks like this in the RouteConfig.cs.
routes.MapRoute("Edit_Personal",
"Edit/Personal/{userID}/{refKey}/{houseID}",
new {controller = "Edit", action = "Personal",
userID = UrlParameter.Optional,
refKey = UrlParameter.Optional,
houseID = UrlParameter.Optional });
This route works fine if I pass in something like:
Edit/Personal/78887/abcd/
UserID = 78887
RefKey = abcd
HouseID = null
However, if the RefKey is never passed (it's optional), but a HouseID is passed, I get this as the result (the URL):
Edit/Personal/78887//88881 <--- Notice the two slashes between the numbers.
UserID = 78887
RefKey = 88881
HouseID = null
The results I expected are:
UserID = 78887
RefKey = null
HouseID = 88881
If you notice, the RefKey should be NULL, however, it's binding the HouseID into the RefKey parameter.
Is there a way to correct this? Am I missing something?
You need to define multiple routes like below:
routes.MapRoute(
"WithAll",
"{controller}/{action}/{UserID}/{RefKe}/{HouseID}",
new{ controller = "Edit",action = "Personal"});
routes.MapRoute(
"WithoutRefke",
"{controller}/{action}/{UserID}/{HouseID}",
new{controller = "Edit",action = "Personal"});
routes.MapRoute(
"WithoutRefkeAndHouseID",
"{controller}/{action}/{UserID}",
new{controller = "Edit",action = "Personal",UserID= UrlParameter.Optional});
Take a look at the following link for more details:
http://haacked.com/archive/2011/02/20/routing-regression-with-two-consecutive-optional-url-parameters.aspx/
I've defined the following route for a simple blog.
routes.MapRoute(
"Blog",
"blog/{year}/{month}/{day}",
new { controller = "Blog", action = "Index" },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
The url should be able "hackable" to accomplish the following:
http://abc.com/blog/2010 -> shows all
posts in 2010
http://abc.com/blog/2010/01 -> shows
all posts in January 2010
http://abc.com/blog/2010/01/25 ->
shows all posts in January 25th, 2010
I have created a controller which handles this action quite nicely. However I am having trouble creating links in my views using Url.Action().
For example this...
var d = new DateTime(2010, 1, 25);
Url.Action("Index", "Blog", new { year=d.Year, month=d.Month, day=d.Day} );
...generates a url like that looks like this:
http://abc.com/blog?year=2010&month=2&day=21
I would rather like it to generate a url that looks like the urls in the list above.
http://abc.com/blog/2010/02/21
Is there any way I can use Url.Action() or Html.ActionLink() to generate urls in the format I desire?
The issue there is that the month you're passing in to Url.Action is a single-digit month, and thus doesn't match the month constraint on the route definition. Constraints are typically run not only on incoming URLs but also when generating URLs.
The fix would be to manually call .ToString() on the month and format it as a two-digit number. You'll need to do the same for the day as well. For the year it's not an issue since all the years in our lifetimes will be four-digit numbers.
Here's sample code to format numbers:
int month = 2;
string formattedMonth = month.ToString("00", CultureInfo.InvariantCulture);
// formattedMonth == "02"
Please note that when formatting the number manually that you must use the Invariant Culture so that different cultures and languages won't affect how it gets formatted.
You'll also need to set default values for month and day so that they are not required in the URL:
routes.MapRoute(
"Blog",
"blog/{year}/{month}/{day}",
new { controller = "Blog", action = "Index", month = "00", day = "00" },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
And in your controller action check if the month or day are 0, in which case you should show an entire month or entire year.
The other issue I see you running into is that you'll need several other route entries with appropriate defaults to handle those other scenarios.
"http://abc.com/2010" will not match "blog/{year}/{month}/{day}". That's a very specific route that requires three parameters (with constraints) to match. In order to accept those other routes you'll need to create other route entries along the lines of:
routes.MapRoute(
null,
"blog",
new { controller = "Blog", action = "Index", year = 0000, month = 00, day = 00 },
new { year = #"\d{4}", month = #"\d{2}", day = #"\d{2}" }
);
routes.MapRoute(
null,
"blog/{year}",
new { controller = "Blog", action = "Index", month = 00, day = 00 },
new { year = #"\d{4}" }
);
routes.MapRoute(
null,
"blog/{year}/{month}",
new { controller = "Blog", action = "Index", day = 00 },
new { year = #"\d{4}", month = #"\d{2}"}
);
There are a few ways to handle this scenario, but now in your Blog controller and your Index action you can filter the posts results by year, month, day.
Quick example:
if(year == 0000 && month == 00 && day == 00)
var posts = postsRepository.GetAllPost();
else if(year != 0000 && month == 00 && day == 00)
var posts = postsRepository.GetPostsByYear(year);
...
You dont have to write new method for all condition. I mean you can do it with this;
eg. for NHibernate.
public IList<Blog> GetBlogs(int? day, int? month, int? year) {
IQueryable<Blog> iQBlog = Session.Linq<Blog>();
if (year.HasValue) {
iQBlog = iQBlog.Where(b => b.Year == year);
if (month.HasValue) {
iQBlog = iQBlog.Where(b => b.Month == month);
if (day.HasValue) {
iQBlog = iQBlog.Where(b => b.Day == day);
}
}
}
return iQBlog.ToList();
}
ps. It's checking parameters step by step, year -> month -> day. If user doesnt set any parameters in querystring, it will return all blogs.
I was wondering if you could show me all the various ways to declare routes in ASP.NET MVC (1 and 2). Please explain each method of defining a route, how it is used, and what case it covers.
Here is an example of what I am hoping to collect here:
routes.MapRoute("Directors",
"Directors/{filter}/{skip}",
new { controller = "Directors", action = "Index", skip = 0, filter = "" },
new { skip = #"\d+", filter = #"^[a-zA-Z]+.+" });
Directors = the name of the route. Directors/{filter}/{skip} = the url definition. new { controller = "Directors", action = "Index", skip = 0, filter = "" } = the default for this route. new { skip = #"\d+", filter = #"^[a-zA-Z]+.+" } = the constraints for this route.
My first port of call would be the ASP.NET learning pages on routing.
I think this may also be a good use of the Community Wiki feature, as the question you're asking a pretty vague question where there are not real answers, just good documentation on routes.
There seem to be a lot of issues on SO dealing with MapRoute problems. I've read through a bunch of them, but I can't see what I'm doing wrong in my implementation. I've got the following routes set up:
routes.MapRoute( _
"FilesDisplay", _
"{controller}/{action}/{year}/{month}", _
New With {.controller = "Files", .action = "Display", .year = "", .month = ""})
routes.MapRoute( _
"Default", _
"{controller}/{action}", _
New With {.controller = "Files", .action = "Index"})
and the following RouteLink:
<%=Html.RouteLink("Show", "FilesDisplay", New With {.year = 2008, .month = 5})%>
However, the resulting URL for "Show" is /Files/Index/2008/5. Why is it picking up the Index action instead of the Display action?
Edit: As a lark I changed the second route to .action = "Display" just to see if I could get the URL to change, and it still resolves to /Files/Index/2008/5.
Edit 2: I also tried:
<%=Html.ActionLink("Show", "Display", "Files", New With {.year = 2008, .month = 5}, Nothing)%>
but that also resolves to /Files/Index/2008/5. Why is it that no matter what I specify for an action it's defaulting to Index?
Upgrading to MVC RC 1.0 seemed to fix ActionLink but not RouteLink. Therefore I'm moving forward with ActionLink. I'd love to know why RouteLink doesn't want to work though.