Search code examples
c#asp.netasp.net-web-apiswaggerasp.net-web-api-routing

API Routes: Multiple operations with path


I am trying to configure my API routes, and I can't seem to get around this error from Swagger:

500 : {"Message":"An error has occurred.","ExceptionMessage":"Not supported by Swagger 2.0: Multiple operations with path 'api/Doors/{OrganizationSys}' and method 'GET'.

I understand why I am getting the error, but I'm not sure how to fix it. Here are the API end points:

public IHttpActionResult Get(int organizationSys)
{
    ....
}

public IHttpActionResult Get(int organizationSys, int id)
{
    ....
}


public IHttpActionResult Post([FromBody]Doors door)
{
    ....
}

public IHttpActionResult Put([FromBody]Doors door)
{
    ....
}

public IHttpActionResult Delete(int organizationSys, int id)
{
    ....
}

And here are my routes, which are clearly not correct:

config.Routes.MapHttpRoute(
    name: "route1",
    routeTemplate: "api/{controller}/{organizationSys}"
);

config.Routes.MapHttpRoute(
    name: "route2",
    routeTemplate: "api/{controller}/{organizationSys}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}"
);

UPDATE:

I now have this, but get the same error:

config.Routes.MapHttpRoute(
    name: "route1",
    routeTemplate: "api/{controller}/{organizationSys}/{id}"
);

config.Routes.MapHttpRoute(
    name: "route2",
    routeTemplate: "api/{controller}/{organizationSys}"
);

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}"
);

UPDATE 2: This is what I have now....

Getting closer, I think.

[Route("api/Doors/{organizaitonSys}")]
public IHttpActionResult Get(int organizationSys)
{
    ....
}

[Route("api/Doors/{organizaitonSys}/{id}")]
public IHttpActionResult Get(int organizationSys, int id)
{
    ....
}

[Route("api/Doors")]
public IHttpActionResult Post([FromBody]Doors door)
{
    ....
}

[Route("api/Doors")]
public IHttpActionResult Post([FromBody]Doors door)
{
    ....
}

[Route("api/Doors/{organizaitonSys}/{id}")]
public IHttpActionResult Delete(int organizationSys, int id)
{
    ....
}

And then the routes:

config.Routes.MapHttpRoute(
name: "route1",
routeTemplate: "api/{controller}/{organizationSys}/{id}"
);

config.Routes.MapHttpRoute(
name: "route2",
routeTemplate: "api/{controller}/{organizationSys}"
);

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}"
);

However, when I try to call

GET: http://localhost:26307/api/Doors/1012

or

GET: http://localhost:26307/api/Doors/1012/23

or

DELETE: http://localhost:26307/api/Doors/1012/23

I get 404 errors. And now in Swagger, the organizationSys parameter shows up twice in the Try It section:

enter image description here


Solution

  • route1 and route2 have the same mapping because of the optional {id}. remove the optional parameter and put route2 before route1 as route1 is more general.

    You can also provide constraints, which restrict how a URI segment can match a placeholder:

    constraints: new { id = @"\d+" }   // Only matches if "id" is one or more digits.
    

    Example

    config.Routes.MapHttpRoute(
        name: "route2",
        routeTemplate: "api/{controller}/{organizationSys}/{id}",
        constraints: new { id = @"\d+" }   // Only matches if "id" is one or more 
    );
    
    config.Routes.MapHttpRoute(
        name: "route1",
        routeTemplate: "api/{controller}/{organizationSys}"
    );
    
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}"
    );
    

    There will also be an issue mapping to

    public IHttpActionResult Get(int OrganizationSys, int DoorID) {
        ....
    }
    

    because of the DoorID parameter, which does not match that of the route template. rename it to id or update the template to match.

    public IHttpActionResult Get(int organizationSys, int id) {
        ....
    }
    

    If using attribute routing then make sure it is enabled before convention-based routes

    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {
            // Attribute routing.
            config.MapHttpAttributeRoutes();
    
            // Convention-based routing.
    
           //...code removed for brevity
        }
    }
    

    Then apply the routes to the controller via attributes.

    [RoutePrefix("api/Doors")]
    public class DoorsController : ApiController {
    
        //matches GET api/doors/5
        [HttpGet]
        [Route("{organizationSys:int}")]
        public IHttpActionResult Get(int organizationSys) {
            //....
        }
    
        //matches GET api/doors/5/1
        [HttpGet]
        [Route("{organizationSys:int}/{id:int}")]
        public IHttpActionResult Get(int organizationSys, int id) {
            ....
        }    
    
        //matches POST api/doors
        [HttpPost]
        [Route("")]
        public IHttpActionResult Post([FromBody]Doors door) {
            //....
        }
    
        //matches PUT api/doors
        [HttpPut]
        [Route("")]    
        public IHttpActionResult Put([FromBody]Doors door) {
            //....
        }
    
        //matches DELETE api/doors/5/1
        [HttpDelete]
        [Route("{organizationSys:int}/{id:int}")]    
        public IHttpActionResult Delete(int organizationSys, int id) {
            //....
        }    
    }
    

    Note the use of RoutePrefix attribute to reduce repeated template parts and route constraints.

    Reference Attribute Routing in ASP.NET Web API 2