zameb zameb - 3 years ago 133
reST (reStructuredText) Question

Routing to multiple GETS fails when the Id is a string

I have tried answers to other similar questions, but I'm still having this problem.
We are implementing a ASP.NET REST API with a setup like the following:

[Authorize]
[Route("api/cars/{id:int}")]
public HttpResponseMessage Get(int id)
{
//Some stuff
}

[Authorize]
[Route("api/cars/{brand?}/{color?}")]
public HttpResponseMessage GetBySource(string brand = null, string color = null)
{
//Some stuff
}


The routing works fine thanks to the int constraint on the Get(int id) method and it supports calls as:

{host}/api/cars/1
{host}/api/cars/toyota
{host}/api/cars/toyota/blue
{host}/api/cars?brand=toyota&color=blue


Now there's a new requirement to support string Ids
The following "logical" change (to remove the int constraint on the Id) have broken the setup:

[Authorize]
[Route("api/cars/{id}")]
public HttpResponseMessage Get(string id)
{
//Some stuff
}


Now most of the previous calls are routed to Get(string id):

{host}/api/cars/1 //---> Works OK
{host}/api/cars/toyota //---> "toyota" is the car Id instead of brand (No OK)
{host}/api/cars?brand=toyota //---> "brand=toyota" is the car Id instead of parsing the brand (No OK)
{host}/api/cars/toyota/blue //---> (404 Error)
{host}/api/cars?brand=toyota&color=blue //---> (404 Error)


It has sense after all. The [Route("api/cars/{id}")] is considering any string after cars as an Id, and is expecting a route like [Route("api/cars/{id}/xxx/{yyy}")] to fit the other requests. But it would not have sense to put a unique Id in front of other filters.

We are evaluating to change our previous design only if it is really needed. So my question is:
Can we make the following design work?:

{host}/api/cars/A100T50
{host}/api/cars/toyota
{host}/api/cars/toyota/blue
{host}/api/cars?brand=toyota&color=blue


If not, which design would you recommend me to use?

My route config is simple as:

public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}


Thanks in advance for any guidance

Answer Source

Are you able to combine the two methods together?

[Authorize]
//edit: add another route so that it recognizes id 
[Route("api/cars/{id}")]
[Route("api/cars")]
public HttpResponseMessage Get(string brand = "", string color = "", string id = "")
{
    //example to get from database based on whichever parameter provided 
    using(var ctx = new DbContext())
    {
        var cars = ctx.Cars
                      .Where(car => String.IsNullOrEmpty(id) || car.Id == id 
                      && String.IsNullOrEmpty(color) || car.Color == color
                      && String.IsNullOrEmpty(brand) || car.Brand == brand);
    //Some stuff
}

The you'd be able to call

{host}/api/cars/AT100
{host}/api/cars?id=AT100
{host}/api/cars?color=black
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download