title: C# WebApiConfig MapHttpRoutes with Action AND optional DateTime(-string) AND optional id
body: <p>I have a C# Web API with a few HttpGET and HttpPOST-methods in my API-Controllers. Here below a list of all of them to know what I want to achieve:</p>
  
  <p>ProductsController:</p>
  
  <pre><code>// GET: {host}/api/products/all/{device_id}/{date}
  [ActionName("all")]
  public IEnumerable&lt;ProductsAPI&gt; 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);
  </code></pre>
  
  <p>CategoriesController:</p>
  
  <pre><code>// GetAllCategories &amp; GetCategory similar to ProductsController
  // (included date string)
  </code></pre>
  
  <p>OrdersController:</p>
  
  <pre><code>// GetAllOrders &amp; GetOrder &amp; SaveOrderChanges similar to ProductsController
  // (included date string)
  </code></pre>
  
  <p>TagsController:</p>
  
  <pre><code>// {host}/api/tags/all/{device_id}
  [ActionName("all")]
  public IEnumerable&lt;TagsAPI&gt; GetAllTags(string device_id);
  
  // {host}/api/tags/one/{device_id}/{id}
  [ActionName("one")]
  public TagsAPI GetTag(string device_id, int id);
  </code></pre>
  
  <p>UsersController:</p>
  
  <pre><code>// GetAllUsers &amp; GetUser similar to TagsController
  // (excluding the need for a date string)
  </code></pre>
  
  <p>TestController:</p>
  
  <pre><code>// 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);
  </code></pre>
  
  <p>Before this change I didn't included the Date-string in the Products, Categories and Orders and everything worked perfect when I used the following WebApiConfig's MapHttpRoute:</p>
  
  <pre><code>config.Routes.MapHttpRoute(
      name: "DefaultApiRoute",
      routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
      defaults: new { id = RouteParameter.Optional }
  );
  </code></pre>
  
  <p>Now my question is: How should I define my MapHttpRoute now that I've added the Date to some (not all) of my API-Controllers?</p>
  
  <p>I did try this:</p>
  
  <pre><code>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]+$" }
  );
  </code></pre>
  
  <p>But now none of my HttpGets are working anymore.. ('Server Error in '/' Application. The resource cannot be found.') Both with date (<code>{host}/api/products/all/{device_id}/07-08-2014</code>) and without date (<code>{host}/api/users/all/{device_id}</code>)..</p>
  
  <p>Does anyone know what is wrong with my MapHttpRoutes and how I should change it?</p>
  
  <hr>
  
  <p>EDIT 1:</p>
  
  <p>I've changed the date constraints to <code>date = @"^\d{2}-\d{2}-\d{4}$" /* dd-MM-yyyy */</code> like <a href="https://stackoverflow.com/questions/25199949/c-sharp-webapiconfig-maphttproutes-with-action-and-optional-datetime-string-an/25200536#25200536">@Yevgeniy.Chernobrivets suggested</a>. I've also tried his other suggestion of removing the <code>constraints: new { id = @"^[0-9]+$" }</code> from the top MapHttpRoute. When I do this my requests like <code>{host}/api/users/all/{device_id}</code> are working again, but the ones with the date like <code>{host}/api/orders/all/{device_id}/07-08-2014</code> are giving me the following error:</p>
  
  <pre><code>{"$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."}
  </code></pre>
  
  <hr>
  
  <p>EDIT 2:</p>
  
  <p>I've also tried a single MapHttpRoute like so:</p>
  
  <pre><code>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]+$" }
  );
  </code></pre>
  
  <p>With the same calls as before, but with the parameter names changed (The ones using date [Products, Categories, Orders] have <code>date</code> changed to <code>all_id</code> and <code>id</code> changed to <code>one_id</code>. The ones that aren't using date [Users, Tags] have <code>id</code> changed to <code>all_id</code>.)</p>
  
  <p>But now none of my HttpGETs are working (once again) anymore (<code>Server Error in '/' Application. The resource cannot be found.</code>). (PS: I know I'm pretty bad at regex and MapHttpRoutes, so does anyone know a complete solution so all my HttpGets and HttpPosts are working, of all API-Controllers?)</p>
  
  <hr>
  
  <p>EDIT 3:</p>
  
  <p>There seems to be something wrong with the constraints.. It almost looks like when constraints are added in the MapHttpRoute, the <code>RouteParamter.Optional</code> is ignored.. (I probably did something wrong though, but can't figure out what.) I tried adding a MapHttpRoute before my original MapHttpRoute, this time without constraints like so:</p>
  
  <pre><code>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 }
  );
  </code></pre>
  
  <p>Now almost everything is working:</p>
  
  <pre><code>{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
  </code></pre>
  
  <p>Still, would prefer it if I could add constraints..</p>
  
  <p>But even with my old code (before date was ever applied), constraints doesn't work when I add them:</p>
  
  <pre><code>config.Routes.MapHttpRoute(
      name: "DefaultApiRoute",
      routeTemplate: "api/{controller}/{action}/{device_id}/{id}",
      defaults: new { id = RouteParameter.Optional },
      constraints: new { id = @"^[0-9]+$" }
  );
  </code></pre>
  
  <p>Without the constraints this works:</p>
  
  <pre><code>{host}/api/users/all/{device_id}  // works
  {host}/api/users/all/{device_id}/5  // works
  </code></pre>
  
  <p>With the constraints this doesn't work..:</p>
  
  <pre><code>{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.")
  </code></pre>
  
  <p>So I'm pretty confused about constraints' impact in the MapHttpRoutes.. :S And I'm still no where near a working solution after all these trial and errors I did..</p>
  
  <hr>
  
  <p>Edit 4:</p>
  
  <p>After another <a href="https://stackoverflow.com/questions/25199949/c-sharp-webapiconfig-maphttproutes-with-action-and-optional-datetime-string-an?noredirect=1#comment39315804_25199949">suggestion of @Yevgeniy.Chernobrivets</a> I tried to separate All and One:</p>
  
  <pre><code>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) }
  );
  </code></pre>
  
  <p>And added <code>[HttpGet]</code> to all my GetOne and GetAll methods.</p>
  
  <p>With the following results:</p>
  
  <pre><code>{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
  </code></pre>
  
  <p>So, now almost everything works, except for <code>{host}/api/users/one/{device_id}/{id}</code> (and also with tags instead of users). It looks like the <code>defaults: new { date = RouteParameter.Optional },</code> isn't working in the second (and first) MapHttpRoute.</p>
  
  <p>If I can't find any solution I just stick with this one and just provide <code>/00-00-0000</code> when I want a single User/Tag.. >.> I have a deadline and it already took me way, way longer than expected to simply add the date to the URL-Route..</p>
  
  <p>EDIT 4b:</p>
  
  <p>If I remove the <code>[ActionName("one")]</code> and <code>[ActionName("all")]</code> from my methods, it seems that <code>{host}/api/users/all/{device_id}</code> isn't working either (<code>"No action "all" was found on the controller 'Users' that matches the request."</code>), so that means when I keep the ActionNames it uses the third MapHttpRoute instead of the first for the GetAllUsers-method.. The <code>date = RouteParameter.Optional</code> isn't working at all when I provide a constraint for it.. </p>