我的API控制器中有一个带有一些HttpGET和HttpPOST方法的C#Web API。以下是所有这些人的清单,以了解我想要实现的目标:

产品控制器:

// 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)


OrdersController:

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


UsersController:

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


TestController:

// 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]+$" }
);


与以前相同的调用,但参数名称已更改(使用日期[产品,类别,订单]的参数已将date更改为all_id,将id更改为one_id。使用日期[用户,标签]已将id更改为all_id。)

但是现在我的HttpGET都不再起作用(再次)(Server Error in '/' Application. The resource cannot be found.)。 (PS:我知道我对regex和MapHttpRoutes相当不好,所以有人知道一个完整的解决方案,以便所有API控制器中的所有HttpGets和HttpPosts都能正常工作吗?)



编辑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路由。

编辑4b:

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

最佳答案

您的日期约束不正确-您在“任何数字”说明符\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";
 }

08-25 23:56