If I have an ActionResult method like so:
public ActionResult AllSummaries(int? page, DateTime? yesterday)
Instead of the route being like:
http://serverName/projectName/controllerName/AllSummaries?yesterday=04/03/2017
I would like it to be:
http://serverName/projectName/controllerName/AllSummaries/04/03/2017
So on top of the ActionResult, how do I add a constraint to make the datetime only show the date in format MM/dd/yyyy
?
[Route("allsummaries/yesterday/{yesterday:}")]
public ActionResult AllSummaries(int? page, DateTime? yesterday)
I do not need to validate against whether or not the date and day are 1 digit or 2 digit.. it will always be 2 digit.
Any help is appreciated.
UPDATE
Now getting 404 errors:
Here is the link that I am using:
http://serverName/projectName/controllerName/allsummaries/yesterday/3/4/2017
Here is my action:
[Route("controllerName/allsummaries/yesterday/{month?}/{day?}/{year?}")]
[ValidateInput(false)]
public ActionResult AllSummaries(int? page, int? day, int? month, int? year)
{
if (day.HasValue && month.HasValue && year.HasValue)
{
var yesterday = new DateTime(year.Value, month.Value, day.Value);
}
The route that I am generating is from a console application that is going to send out emails automatically via windows service, so I can't use @Url.Action
...etc.. I am hardcoding the link like so:
mail.Body = mail.Body + "<div>" + "<p>" +
"http://serverName/projectName/controllerName/allsummaries/yesterday/" +
DateTime.Today.AddDays(-1).Day +
"/" + DateTime.Today.AddDays(-1).Month + "/" +
DateTime.Today.AddDays(-1).Year + "</p>" + "</div>";
The issue is the slashes in the date, which will be interpreted as path separators. The routing framework only parses params between path separators, unless you use the greedy param syntax, i.e. {*yesterday}
. However, if you do that any further portions of the URL path will be consumed. For example, if a user changed the URL to something like allsummaries/yesterday/04/03/2017/foo
, then 04/03/2017/foo
would be passed in as yesterday
and your action explodes.
You have two options.
You can use a different date format, like ISO: yyyy-MM-dd
, which would make your URL /allsummaries/yesteday/2017-04-03
, and you could capture the date portion with a single param: {yesterday}
.
[Route("allsummaries/yesterday/{yesterday}")]
You can break up the date components and then recompose them into a DateTime
in the action:
[Route("allsummaries/yesterday/{month?}/{day?}/{year?}")]
Then, in your action:
public ActionResult AllSummaries(int? page, int? month, int? day, int? year)
{
var yesterday = DateTime.Today.AddDays(-1); // default
if (month.HasValue && day.HasValue && year.HasValue)
{
yesterday = new DateTime(year.Value, month.Value, day.Value);
}
EDIT
I didn't want to confuse the main issue, but if you choose to follow the second option, breaking up the date into components, there is an issue you'll need to be aware of. If you have an explicit URL like /allsummaries/yesterday/04/03/2017
, the modelbinder will be able to parse the "04" and "03" into int
s. However, if you try to create the URL, using something like Url.Action
, Url.RouteUrl
, etc., you will need to feed the params values like "04", rather than an int, or you'll end up with URLs like /allsummaries/yesterday/4/3/2017
. You could do that via something like:
@Url.Action("AllSummaries", new
{
month = date.ToString("MM"),
day = date.ToString("dd"),
year = date.Year
})
In other words, you would need to use ToString
to get the two digit value, rather than date.Month
or date.Day
directly.
You should also probably protect the URL a little from tampering, by adding a regex contraint to these params:
[Route("allsummaries/yesterday/{month?:regex([0-9]{2})}/{day?:regex([0-9]{2})}/{year?:regex([0-9]{4}}")]