C#WebApiConfig MapHttpRoutes with Action AND可选的DateTime(-string)和可选的id

时间:2014-08-08 09:18:02

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

我的API控制器中有一些带有一些HttpGET和HttpPOST方法的C#Web API。下面列出了所有要知道我想要实现的目标:

的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,因为我已经将Date添加到了一些(不是全部)API控制器中?

我试过这个:

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

但是现在我的HttpGets都没有工作..('''错误'''应用程序。无法找到资源。')两者都有日期({host}/api/products/all/{device_id}/07-08-2014)且没有日期({{1} })..

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


编辑1:

我已将日期限制更改为{host}/api/users/all/{device_id},如@Yevgeniy.Chernobrivets suggested。我还尝试了从顶部MapHttpRoute中删除date = @"^\d{2}-\d{2}-\d{4}$" /* dd-MM-yyyy */的其他建议。当我执行此操作时,constraints: new { id = @"^[0-9]+$" }之类的请求再次有效,但是{host}/api/users/all/{device_id}之类的请求会给我以下错误:

{host}/api/orders/all/{device_id}/07-08-2014

编辑2:

我也尝试了一个MapHttpRoute:

{"$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."}

使用与之前相同的调用,但参数名称已更改(使用日期[产品,类别,订单]的文件已将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]+$" } ); 更改为dateall_id更改为{ {1}}。未使用日期的用户[标签]将id更改为one_id。)

但是现在我的HttpGET都没有工作(再一次)(id)。 (PS:我知道我在正则表达式和MapHttpRoutes上非常糟糕,所以有人知道一个完整的解决方案,所以我所有的HttpGets和HttpPost都在使用所有的API控制器吗?)


编辑3:

约束似乎有问题。看起来好像在MapHttpRoute中添加约束时,忽略了all_id ..(我可能做错了但是无法弄清楚什么。)我尝试在我的原始MapHttpRoute之前添加MapHttpRoute,这次没有像这样的约束:

Server Error in '/' Application. The resource cannot be found.

现在几乎一切正常:

RouteParamter.Optional

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

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

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

所以我对MapHttpRoutes中约束的影响感到非常困惑。:S在我做了所有这些试验和错误之后,我仍然没有接近工作解决方案..


编辑4:

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

{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.") 添加到我的所有GetOne和GetAll方法中。

得到以下结果:

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](还有标签而不是用户)。看起来{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 在第二个(和第一个)MapHttpRoute中不起作用。

如果我找不到任何解决方案,我只需坚持使用此解决方案,只需提供{host}/api/users/one/{device_id}/{id}即可获得单个用户/标签。&gt;。&gt;我有一个截止日期,它已经让我走了,比预期更长的时间只是简单地将日期添加到URL-Route ..

编辑4b:

如果我从我的方法中移除defaults: new { date = RouteParameter.Optional },/00-00-0000,似乎[ActionName("one")]也无效([ActionName("all")]),这意味着我保留了ActionNames它使用第三个MapHttpRoute而不是GetAllUsers方法的第一个。当我为它提供约束时,{host}/api/users/all/{device_id}根本不起作用..

2 个答案:

答案 0 :(得分:2)

您的日期约束不正确 - 您在“任何数字”说明符\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 :(得分:0)

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

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