带约束的属性路由

Attribute Routing with Constraint

如果我有这样的 ActionResult 方法:

public ActionResult AllSummaries(int? page, DateTime? yesterday)

而不是像这样的路线:

http://serverName/projectName/controllerName/AllSummaries?yesterday=04/03/2017

我希望它是:

http://serverName/projectName/controllerName/AllSummaries/04/03/2017

因此,在 ActionResult 之上,我如何添加约束以使日期时间仅以 MM/dd/yyyy 格式显示日期?

[Route("allsummaries/yesterday/{yesterday:}")]
public ActionResult AllSummaries(int? page, DateTime? yesterday)

我不需要验证日期和日期是 1 位还是 2 位。它总是 2 位。

感谢任何帮助。

更新

现在出现 404 错误:

这是我正在使用的link:

http://serverName/projectName/controllerName/allsummaries/yesterday/3/4/2017

这是我的操作:

[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);
    }

我生成的路由来自控制台应用程序,它将通过 windows 服务自动发送电子邮件,所以我不能使用 @Url.Action...等等。我像这样对 link 进行硬编码:

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>";

可以单独解析日月年, 然后创建日期。

您的代码将是这样的:

[Route("allsummaries/yesterday/{day}/{month}/{year}")]
public ActionResult AllSummaries(int? page, int day, int month, int year)
{
    var yesterday = new Date(day, month, year);
}

[Route("allsummaries/yesterday")]
public ActionResult AllSumaries(int? page)
{
}

问题是日期中的斜线,它将被解释为路径分隔符。路由框架只解析路径分隔符之间的参数,除非你使用贪婪的参数语法,即 {*yesterday}。但是,如果您这样做,URL 路径的任何其他部分都将被消耗。例如,如果用户将 URL 更改为 allsummaries/yesterday/04/03/2017/foo 之类的内容,则 04/03/2017/foo 将作为 yesterday 传入,并且您的操作会爆炸。

你有两个选择。

  1. 您可以使用不同的日期格式,例如 ISO:yyyy-MM-dd,这将使您的 URL /allsummaries/yesteday/2017-04-03,并且您可以捕获日期部分单个参数:{yesterday}.

    [Route("allsummaries/yesterday/{yesterday}")]
    
  2. 您可以分解日期组件,然后在操作中将它们重组为 DateTime

    [Route("allsummaries/yesterday/{month?}/{day?}/{year?}")]
    

    然后,在您的操作中:

    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);
        }
    

编辑

我不想混淆主要问题,但如果您选择遵循第二个选项,将日期分解为多个部分,则需要注意一个问题。如果你有像 /allsummaries/yesterday/04/03/2017 这样的显式 URL,模型绑定器将能够将“04”和“03”解析为 int。但是,如果您尝试 创建 URL,使用 Url.ActionUrl.RouteUrl 等,您将需要提供参数值像“04”,而不是一个整数,否则你最终会得到像 /allsummaries/yesterday/4/3/2017 这样的 URL。你可以通过类似的方式做到这一点:

@Url.Action("AllSummaries", new
{
    month = date.ToString("MM"),
    day = date.ToString("dd"),
    year = date.Year
})

换句话说,您需要使用ToString来获取两位数的值,而不是直接使用date.Monthdate.Day

您可能还应该通过向这些参数添加正则表达式约束来保护 URL 免受篡改:

[Route("allsummaries/yesterday/{month?:regex([0-9]{2})}/{day?:regex([0-9]{2})}/{year?:regex([0-9]{4}}")]