【问题标题】:C# WebApiConfig MapHttpRoutes with Action AND optional DateTime(-string) AND optional idC# WebApiConfig MapHttpRoutes 与 Action AND optional DateTime(-string) AND optional id
【发布时间】:2014-10-01 17:22:28
【问题描述】:

我的 API 控制器中有一个 C# Web API,其中包含一些 HttpGET 和 HttpPOST 方法。下面列出了所有这些以了解我想要实现的目标:

产品控制器:

// GET: {host}/api/products/all/{device_id}/{date}
[ActionName("all")]
public IEnumerable<ProductsAPI> GetAllProducts(string device_id, string date);

// GET: {host}/api/products/one/{device_id}/{date}/{id}
[ActionName("one")]
public ProductsAPI GetProduct(string device_id, string date, int id);

// POST: {host}/api/products/save/{device_id}
[HttpPost]
[AllowAnonymous]
[ActionName("save")]
public bool SaveProductChanges([FromBody] Data data, [FromUri] string device_id);

类别控制器:

// GetAllCategories & GetCategory similar to ProductsController
// (included date string)

订单控制器:

// GetAllOrders & GetOrder & SaveOrderChanges similar to ProductsController
// (included date string)

标签控制器:

// {host}/api/tags/all/{device_id}
[ActionName("all")]
public IEnumerable<TagsAPI> GetAllTags(string device_id);

// {host}/api/tags/one/{device_id}/{id}
[ActionName("one")]
public TagsAPI GetTag(string device_id, int id);

用户控制器:

// GetAllUsers & GetUser similar to TagsController
// (excluding the need for a date string)

测试控制器:

// GET {host}/api/test/enable/{device_id}
[HttpGet]
[ActionName("enable")]
public String enableTests(string device_id);

// GET {host}/api/test/disable/{device_id}
[HttpGet]
[ActionName("disable")]
public String disableTests(string device_id);

在此更改之前,我没有在产品、类别和订单中包含日期字符串,并且当我使用以下 WebApiConfig 的 MapHttpRoute 时一切正常:

config.Routes.MapHttpRoute(
    name: "DefaultApiRoute",
    routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

现在我的问题是:既然我已经将日期添加到我的一些(不是全部)API 控制器,我应该如何定义我的 MapHttpRoute?

我确实试过这个:

config.Routes.MapHttpRoute(
    name: "ApiById",
    routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { id = @"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
    name: "ApiByDate",
    routeTemplate: "api/{controller}/{action}/{device_id}/{date}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { date = @"d{2}-d{2}-d{4}" /* dd-MM-yyyy */, id = @"^[0-9]+$" }
);

但是现在我的 HttpGet 都不再工作了..('/'应用程序中的服务器错误。找不到资源。')有日期({host}/api/products/all/{device_id}/07-08-2014)和没有日期({host}/api/users/all/{device_id})..

有谁知道我的 MapHttpRoutes 出了什么问题以及我应该如何更改它?


编辑 1:

我已将日期限制更改为 date = @"^\d{2}-\d{2}-\d{4}$" /* dd-MM-yyyy */,例如 @Yevgeniy.Chernobrivets suggested。我还尝试了他的其他建议,即从顶部 MapHttpRoute 中删除 constraints: new { id = @"^[0-9]+$" }。当我这样做时,我的请求(如 {host}/api/users/all/{device_id})再次起作用,但日期为 {host}/api/orders/all/{device_id}/07-08-2014 的请求给了我以下错误:

{"$id":"1","Message":"No HTTP resource was found that matches the request URI
'http://localhost:*my_port*/api/orders/all/*given_device_id*/07-08-2014'.",
"MessageDetail":"No action was found on the controller 'Orders' that matches the request."}

编辑 2:

我也尝试过像这样的单个 MapHttpRoute:

config.Routes.MapHttpRoute(
    name: "WebApiRoute",
    routeTemplate: "api/{controller}/{action}/{device_id}/{all_id}/{one_id}",
    defaults: new { all_id = RouteParameter.Optional, one_id = RouteParameter.Optional },
    constraints: new { all_id = @"^((\d{2}-\d{2}-\d{4})|([0-9]+))$", one_id = @"^[0-9]+$" }
);

使用与以前相同的调用,但更改了参数名称(使用日期 [Products, Categories, Orders] 的调用将 date 更改为 all_idid 更改为 one_id。未使用日期 [Users, Tags] 已将 id 更改为 all_id。)

但是现在我的 HttpGET 都不再(再次)工作了(Server Error in '/' Application. The resource cannot be found.)。 (PS:我知道我在 regex 和 MapHttpRoutes 方面很糟糕,所以有人知道一个完整的解决方案,所以我所有的 HttpGets 和 HttpPosts 都可以工作,在所有 API-Controllers 中?)


编辑 3:

约束似乎有问题..几乎看起来当在 MapHttpRoute 中添加约束时,RouteParamter.Optional 被忽略了..(虽然我可能做错了什么,但不知道是什么。 ) 我尝试在我原来的 MapHttpRoute 之前添加一个 MapHttpRoute,这次没有像这样的限制:

config.Routes.MapHttpRoute(
    name: "WebApiRoute",
    routeTemplate: "api/{controller}/{action}/{device_id}/{all_id}/{one_id}",
    defaults: new { all_id = RouteParameter.Optional, one_id = RouteParameter.Optional }//,
    //constraints: new { all_id = @"^((\d{2}-\d{2}-\d{4})|([0-9]+))$", one_id = @"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
    name: "DefaultApiRoute",
    routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

现在几乎一切正常:

{host}/api/products/all/{device_id}/07-08-2014  // works
{host}/api/products/one/{device_id}/07-08-2014/5  // works
{host}/api/users/all/{device_id}  // works
{host}/api/users/one/{device_id}/5  // doesn't work (gives same result as "users/all/{device_id}")
{host}/api/users/one/{device_id}/bla/5  // works (but shouldn't work..)
// Haven't tested the HttpPOSTs yet

不过,如果我可以添加约束,我会更喜欢它..

但即使使用我的旧代码(在应用日期之前),当我添加约束时它们也不起作用:

config.Routes.MapHttpRoute(
    name: "DefaultApiRoute",
    routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { id = @"^[0-9]+$" }
);

没有限制,这可行:

{host}/api/users/all/{device_id}  // works
{host}/api/users/all/{device_id}/5  // works

有了约束,这不起作用..:

{host}/api/users/all/{device_id}  // doesn't work (error: "Server Error in '/' Application. The resource cannot be found. ")
{host}/api/users/all/{device_id}/5  // partly works (gives all users, but shouldn't need "/5" for this..)
{host}/api/users/one/{device_id}/5  // doesn't work (error: "No action was found on the controller 'Users' that matches the request.")

所以我对 MapHttpRoutes 中的约束影响感到非常困惑。.. :S 经过所有这些试验和错误之后,我仍然离可行的解决方案很远..


编辑 4:

又一个suggestion of @Yevgeniy.Chernobrivets我试图将All和One分开:

config.Routes.MapHttpRoute(
    name: "ApiAll",
    routeTemplate: "api/{controller}/all/{device_id}/{date}",
    defaults: new { date = RouteParameter.Optional },
    constraints: new { date = @"^\d{2}-\d{2}-\d{4}$", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
config.Routes.MapHttpRoute(
    name: "ApiOne",
    routeTemplate: "api/{controller}/one/{device_id}/{id}/{date}",
    defaults: new { date = RouteParameter.Optional },
    constraints: new { date = @"^\d{2}-\d{2}-\d{4}$", id = @"^\d+$", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
config.Routes.MapHttpRoute(
    name: "ApiGet",
    routeTemplate: "api/{controller}/{action}/{device_id}",
    defaults: null,
    constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
config.Routes.MapHttpRoute(
    name: "ApiPost",
    routeTemplate: "api/{controller}/{action}/{device_id}",
    defaults: null,
    constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
);

并将[HttpGet] 添加到我所有的 GetOne 和 GetAll 方法中。

结果如下:

{host}/api/products/all/{device_id}/07-08-2014  // works
{host}/api/products/one/{device_id}/5/07-08-2014  // works (NOTE: date and id are switched now, but I don't mind this)
{host}/api/users/all/{device_id}  // works
{host}/api/users/one/{device_id}/5  // doesn't work ("Server Error in '/' Application. The resource cannot be found.")
{host}/api/users/one/{device_id}/5/00-00-0000  // works (but I want it to work without the date as well..)
{host}/api/test/enable/{device_id} // works
// Haven't tested the HttpPOSTs yet

所以,除了{host}/api/users/one/{device_id}/{id}(以及使用标签而不是用户)之外,现在几乎所有东西都可以正常工作。看起来defaults: new { date = RouteParameter.Optional }, 在第二个(也是第一个)MapHttpRoute 中不起作用。

如果我找不到任何解决方案,我会坚持使用这个解决方案,并在需要单个用户/标签时提供 /00-00-0000.. >.> 我有一个截止日期,它已经花了我很多时间,比预计只需将日期添加到 URL-Route..

编辑 4b:

如果我从我的方法中删除[ActionName("one")][ActionName("all")]{host}/api/users/all/{device_id} 似乎也不起作用("No action "all" was found on the controller 'Users' that matches the request."),这意味着当我保留 ActionNames 时,它使用第三个 MapHttpRoute 而不是GetAllUsers 方法的第一个。当我为其提供约束时,date = RouteParameter.Optional 根本不起作用。

【问题讨论】:

  • 在第三次编辑中,有两个约束,这行 '{host}/api/users/one/{device_id}/5' 作为 '{host}/api/users/all/{device_id} '。这实际上是预期的,因为您的第一个路由具有可选的 id 参数,这就是它匹配它的原因。
  • @Yevgeniy.Chernobrivets 嗯好的。但是,我仍然有[ActionName("all")][ActionName("one")] 在这些方法之上,所以我假设当我使用{host}/api/users/one/{device_id}/5 时,它应该使用带有ActionName“one”的GetUser 方法,而不是带有ActionName“all”的GetAllUsers ,即使这两个 MapHttpRoute 都与 URL 路径匹配。无论如何,我对这一切是如何运作的有点困惑,也有点沮丧。如果有人知道一个解决方案来涵盖我在 API-Controller 方法上方的 cmets 中提供的所有 HttpGET,我将不胜感激。
  • 我认为你需要将你的“全部”和“一个”路线分成不同的路线。
  • @Yevgeniy.Chernobrivets 好的,我尝试拆分它们并再次进行编辑(编辑 4)。只有一个还没有工作(我还没有测试 HttpPOST,当所有 HttpGET 完全工作时会这样做)。 PS:我已经将 MapHttpRoute 中的日期和 ID 切换为 "one"
  • 请检查我的答案。

标签: c# asp.net-mvc routing asp.net-mvc-routing constraints


【解决方案1】:

您的日期限制不正确 - 您在“任何数字”说明符 \d 之前错过了反斜杠。 试试这个约束:

date = @"^\d{2}-\d{2}-\d{4}$"

编辑: 约束破坏可选参数似乎是对的。检查this post 是否有相同的问题。

我最终做的是向正则表达式添加空字符串的可能性。您可以通过使用|^$ 与正则表达式的其余部分分隔开来做到这一点。

我还添加了action="actionname" 到默认值并使date 参数可以为空。

路线配置:

 config.Routes.MapHttpRoute(
    name: "ApiAll",
    routeTemplate: "api/{controller}/all/{device_id}/{date}",
    defaults: new { date = RouteParameter.Optional, action="all"},
    constraints: new { date = @"^$|^\d{2}-\d{2}-\d{4}$", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
 );
 config.Routes.MapHttpRoute(
    name: "ApiOne",
    routeTemplate: "api/{controller}/one/{device_id}/{id}/{date}",
    defaults: new { date = RouteParameter.Optional, action = "one" },
    constraints: new { date = @"^$|^\d{2}-\d{2}-\d{4}$", id = @"^$|^\d+$", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
 );
 config.Routes.MapHttpRoute(
    name: "ApiGet",
    routeTemplate: "api/{controller}/{action}/{device_id}",
    defaults: null,
    constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
 );
 config.Routes.MapHttpRoute(
    name: "ApiPost",
    routeTemplate: "api/{controller}/{action}/{device_id}",
    defaults: null,
    constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
 );

控制器动作方法(代码基于 ootb 值控制器):

 [ActionName("all")]
 [HttpGet]
 public IEnumerable<string> Get(string device_id, DateTime? date = null)
 {
      return new string[] { "value1", "value2" };
 }

 // GET api/values/5
 [ActionName("one")]
 [HttpGet]
 public string Get(string device_id, int id, DateTime? date = null)
 {
      return "value";
 }

【讨论】:

  • 感谢您的更正 (+1),但仍然无法正常工作。没有日期约束的那个(例如{host}/api/users/{device_id})也不再起作用了,因为我有两个有约束的 MapHttpRoutes 而不是一个没有约束的......
  • 如果您从第一条路由中删除约束(不删除第二条),那么“{host}/api/users/{device_id}”是否有效?
  • 等等,但是您提供的 '{host}/api/users/{device_id}' 的 url 与您的路线不匹配,因为您缺少 '{action}' 参数。
  • 啊,我确实提到了{host}/api/users/all/{device_id}。无论如何,我已经尝试过您从顶部 MapHttpRoute 中删除约束的其他建议。然后那些没有日期的人又开始工作了,但是有日期的人给了我一个No Action found error(我已经对我的原始帖子进行了编辑)。
  • 非常感谢编辑,这成功了!太糟糕了,我不能再次投票给你。非常感谢您的帮助。
【解决方案2】:

对于原始问题,由于 id 有限制并且是可选的,您还必须将 REGEX 条目设置为“可选”。

config.Routes.MapHttpRoute(
    name: "ApiById",
    routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: new { id = @"(\d+)?" }
);

【讨论】:

  • 谢谢@Derek Wade,我遇到了同样的问题。你的回答解决了。
猜你喜欢
  • 1970-01-01
  • 2022-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
相关资源
最近更新 更多