【问题标题】:Multiple optional parameters routing多个可选参数路由
【发布时间】:2015-02-25 20:38:18
【问题描述】:

我的 webapi 项目中有以下路由定义。我有一个参数没有传递的问题。例如;

当我调用 /Controller/Action/param2/startdate/enddate 时,我为 param2 传递的值被用于 param1,反之亦然。问题是,RoutingModule 无法检测到提供的路由值是用于 param2 而不是 param1

如果我在 url 中使用查询字符串但不想使用查询字符串,它可以工作。感谢您的帮助。

有什么方法可以达到我的期望吗?

config.Routes.MapHttpRoute(
    name: "RetrieveHistory",
    routeTemplate: "{controller}/{action}/{param1}/{param2}/{startDate}/{endDate}",
    defaults: new
    {
        controller = "Vend",
        action = "RetrieveUtrnHistory",
        param1 = RouteParameter.Optional,
        param2 = RouteParameter.Optional,
        starDate = RouteParameter.Optional,
        endDate = RouteParameter.Optional
    });

谢谢

【问题讨论】:

  • 如果您看到一个缺少参数的 URL,您如何知道哪个是缺少的参数?如果你能给我这个问题的答案(你是怎么知道的),我可以帮你创建一组路线,让你做你想做的事。如果你不能做到这一点,你仍然可以改变编写 URL 的方式,我仍然可以帮助你。因此,请通过对我的第一个问题的答案来更新问题,或者显示一些示例 URL 并告诉您对每个问题的期望参数数学。有很多方法可以实现您的需求,但您必须清楚地解释它。
  • 我不知道为什么我在这里发布问题。我对路由不是很有经验
  • 我不是在问你如何路由......我只是在问你如何知道 URL 作为“人类”是不同的。我会给你一些想法......

标签: c# asp.net-mvc-4 asp.net-web-api url-routing asp.net-web-api-routing


【解决方案1】:

要解决您的问题,您必须考虑以下几点:

  • 您可以注册多个路线。第一个可以处理 URL 的注册路由将处理它。
  • 您可以使用除斜线 / 之外的其他内容作为分隔符,以区分路线的某些部分
  • 您可以使用参数约束(通常是正则表达式)来更轻松地发现参数是否属于一种或其他类型
  • 你可以为你的参数指定默认值,如果你这样做了,动作方法必须有默认值(除非 MVC,它只要求它们可以为空或引用类型)

由于您没有说明您的 URL 的外观,我将向您展示我自己的示例。

假设您有一个 TestController Web API Controller 类,其操作如下:

// GET api/Test/TestAction/ ...
[HttpGet]
public object TestAction(int param1, DateTime startDate, DateTime endDate, 
                         int? param2 = null)
{
    return new
    {
        param1 = param1,
        param2 = param2,
        startDate = startDate,
        endDate = endDate
    }.ToString();
}

注意:对于默认路由,名为 GetXxx 的 Web API 控制器方法可用于 HTTP GET,名为 PostXxx 的方法可用于 HTTP POST 等等。但是,在 URL 模板中包含 Controller 和 Action 后,您必须使用 [HttpXxx] 属性使您的方法可用于所需的 HTTP 方法。

中间的可选参数

在第一个示例中,我假设param1 和param2 都是整数,而stardDate 和endDate 是日期:

http://myhost/api/Mycontroller/Myaction/12/22/2014-12-01/2014-12-31
http://myhost/api/Mycontroller/Myaction/22/2014-12-01/2014-12-31

如果您希望第一个 URL 匹配如下参数:

param1 = 12; param2 = 22; startDate = 2014-12-01; endData = 2014-12-31

第二个像这样:

param1 = 12; param2 = null; startDate = 2014-12-01; endData = 2014-12-31

您需要注册两条路由,一条将匹配每个可能的 URL 结构,即

// for the 1st
routeTemplate: "api/{controller}/{action}/{param1}/{param2}/{startDate}/{endDate}"
// for the 2nd
routeTemplate: "api/{controller}/{action}/{param1}/{startDate}/{endDate}"

请注意,在这种情况下,两个路由是互斥的,即单个 URL 只能匹配其中一个路由,因此您可以在任何其他路由中注册它们。

但是,您必须注意第二个 URL 没有为 param2 定义值,而 TestAction 方法需要它。这是行不通的:您必须在控制器的方法和路由注册中包含此参数的默认值:

  • 动作参数int? param2 = null(C#要求可选参数是最后一个)。
  • 路由必须包含默认值:defaults: new { param2 = RouteParameter.Optional }

这是解决中间问题中可选参数的方法。通常,您需要根据可选参数的数量定义多个路由,并在 Web API 操作方法中使用默认值声明这些参数。

注意:正如我在上面写的,在 MVC 中,您不需要在方法参数中指定默认值即可使其工作

参数约束

为路由参数指定约束有两个后果:

  1. 保证参数值具有预期的格式
  2. 最重要的是,路由只会在格式符合预期的情况下处理 URL。因此,这有助于您使您的网址更具选择性,从而使其更加灵活。

你只需要在路由注册上添加一个constraint参数,像这样:

config.Routes.MapHttpRoute(
    name: "Multiparam2",
    routeTemplate: "api/{controller}/{action}/{param1}/{param2}/{startDate}/{endDate}",
    constraints: new
    {
        startDate = @"20\d\d-[0-1]?\d-[0-3]?\d", // regex
        endDate = @"20\d\d-[0-1]?\d-[0-3]?\d" // regex
    },
    defaults: new object { }
);

请注意,必须指定 defaults 参数,即使它为空。

注意:这种情况下的约束是一个正则表达式,它只匹配年份 20XX 中的日期,月份表示为单个数字,或 0x 或 1x,日期为单个数字或 0x、1x , 2x 或 3x,用破折号分隔。所以这个正则表达式将匹配2012-1-1 或2015-12-30,但不匹配1920-12-30。您应该根据您的需要调整正则表达式。

最后的可选参数

此时我已经解释了如何支持可选参数,以及如何为它们指定格式(约束)以匹配路由模板。

定义可选参数的常用方法是在 URL 模板的末尾进行,在这种情况下,如果路由中缺少参数,则它们必须全部位于路由的末尾。 (将其与中间的 optional 进行比较:它们需要不同的路线)。

在本例中,如果要使param2、startDate和endDate可选,则必须在路由注册中定义它们,并在action方法中设置默认参数值。

最终代码如下所示:

[HttpGet]
public object TestAction(int param1, int? param2 = null, DateTime? startDate = null, 
                         DateTime? endDate = null)
{
    return new
    {
        param1 = param1,
        param2 = param2,
        startDate = startDate,
        endDate = endDate
    }.ToString();
}



config.Routes.MapHttpRoute(
    name: "Multiparam1",
    routeTemplate: "api/{controller}/{action}/{param1}/{startDate}/{endDate}",
    constraints: new
    {
        startDate = @"20\d\d-[0-1]?\d-[0-3]?\d",
        endDate = @"20\d\d-[0-1]?\d-[0-3]?\d"
    },
    defaults: new
    {
        param2 = RouteParameter.Optional,
        startDate = RouteParameter.Optional,
        endDate = RouteParameter.Optional
    }
);

config.Routes.MapHttpRoute(
    name: "Multiparam2",
    routeTemplate: "api/{controller}/{action}/{param1}/{param2}/{startDate}/{endDate}",
    constraints: new
    {
        startDate = @"(20\d\d-[0-1]?\d-[0-3]?\d)?",
        endDate = @"(20\d\d-[0-1]?\d-[0-3]?\d)?"
    },
    defaults: new
    {
        startDate = RouteParameter.Optional,
        endDate = RouteParameter.Optional
    }
);

注意,在这种情况下:

  1. 路由可能不匹配,因此它们必须按正确的顺序注册,如图所示。如果您首先注册了Multiparam2 路由,它将错误地处理这样的URL:http://localhost:1179/api/test/testaction/1/2014-12-12/2015-1-1,param1=1; param2="2014-12-12"; startDate="2015-1-1"。 (您可以通过对仅接受数字的 param2 附加限制来避免这种情况,例如 param2=@"\d+")
  2. 操作必须具有startDate 和endDate 的默认值。

结论

您可以谨慎处理不同位置的默认参数:

  • 以正确的顺序注册路由
  • 在路由中定义默认参数,在控制器动作中定义默认值
  • 使用约束

如果您仔细计划路线的外观,您可以通过一些路线和可选参数获得所需的内容。

【讨论】:

    【解决方案2】:

    JotaBe 的回答很好而且很完整。只是你必须考虑参数是否是可选的,你必须按照从最低参数到最高参数的顺序编写 routeTemplate。

    就像:

    // for the 1st
    routeTemplate: "api/{controller}/{action}/{param1}/{startDate}/{endDate}"
    
    // for the 2nd
    routeTemplate: "api/{controller}/{action}/{param1}/{param2}/{startDate}/{endDate}"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 2020-04-20
      • 2017-06-03
      • 1970-01-01
      • 2017-10-31
      • 2017-06-26
      相关资源
      最近更新 更多