带有Action和可选DateTime(-string)和可选id的maphttprouts

本文关键字:id maphttprouts DateTime Action 带有 -string | 更新日期: 2023-09-27 18:17:09

我有一个c# Web API,在我的API控制器中有一些HttpGET和httppost方法。下面列出了我想要达到的目标:

ProductsController:

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

CategoriesController:

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

OrdersController:

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

TagsController:

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

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

I did try this:

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建议。我还尝试了他从顶部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_id, id更改为one_id)。那些没有使用日期[用户,标签]的id更改为all_id。)

但现在没有我的httpget工作(再一次)了(Server Error in '/' Application. The resource cannot be found.)。(PS:我知道我在regex和MapHttpRoutes很糟糕,所以有人知道一个完整的解决方案,所以我所有的HttpGets和HttpPosts都在工作,所有的api控制器?)


编辑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

但是,如果我可以添加约束的话,我会更喜欢它。

但是即使在我的旧代码中(在应用date之前),当我添加约束时,约束也不起作用:

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的影响。在我做了这么多尝试和错误之后,我仍然没有找到一个可行的解决方案。


编辑4:

在@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.

编辑4 b:

如果我从我的方法中删除[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根本不起作用。

带有Action和可选DateTime(-string)和可选id的maphttprouts

您的日期约束不正确-您在"任意数字"说明符'd之前错过了反斜杠。试试这个约束:

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

编辑:看来你是对的,约束打破了可选参数。查看这篇文章是否有同样的问题。

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

我还将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";
 }

对于原始问题,由于对id有约束并且是可选的,因此您必须将REGEX条目也设置为"可选"。

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