Search code examples
c#asp.net-web-apiasp.net-web-api2asp.net-routingaspnet-api-versioning

ASP.NET WebAPI 2 Versioning POST with id


I'm trying to implement the 'Microsoft.AspNet.WebApi.Versioning' nuget in an existing productive WebAPI 2 application to support new designed 'v2' controllers.

So far everything works fine with the most controllers but now I ran into trouble with some POST controllers.

Sample code:

using Microsoft.Web.Http;
using System.Web.Http;

namespace ApplicationApi.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/version")]
    public class VersionController : ApiController
    {
        //Works
        [HttpGet]
        public IHttpActionResult Get()
        {
            return Ok(true);
        }

        //Throws  HTTP 405 - The requested resource with API version '1.0' does not support HTTP method 'POST'.
        [HttpPost]
        public IHttpActionResult Post(int id, [FromBody]dynamic value)
        {
            return Ok(true);
        }

        //Works
        [HttpPost]
        public IHttpActionResult Post([FromBody]dynamic value)
        {
            return Ok(true);
        }
    }
}

All POST methods without the additional {id} work. But since we have a huge amount old clients for a long time I've to keep the old version up and running and it is not an option to remove the {id} in the old controllers. In the new controllers there is no need for the {id}.

WebAPI config

var constraintResolver = new DefaultInlineConstraintResolver()
{
    ConstraintMap =
    {
        ["apiVersion"] = typeof( ApiVersionRouteConstraint )
    }
};
config.MapHttpAttributeRoutes(constraintResolver);

config.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
});

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

Any idea what I'm doing wrong or how to workaround this?


Solution

  • The problem has nothing to do with versioning.

    The reason for methods with id parameter not working is you have conflict routes:

    [Route("api/version")]
    

    and

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

    For controller called Version with Route("api/version") attribute, you can only have either attribute routing, OR conventional routing (MVC / WebAPI routing), but you can't have both.

    In your case, the attribute routing overrides the MVC one, so api/version/{id} does not exist any more. But api/version?id still works, because it is addressed by attribute routing.

    To fix that problem, you can change your attribute routing on your controller to RoutePrefix instead of Route, and use Route with relative path on each action if necessary.

    necessary means you have routes that cannot be addressed by conventional routing.

    [ApiVersion("1.0")]
    [RoutePrefix("api/version")]
    public class VersionController : ApiController
    {
        [HttpPost]
        public IHttpActionResult Post([FromBody] dynamic value)
        {
            return Ok();
        }
    
        [HttpPost]
        public IHttpActionResult Post(int id, [FromBody] dynamic value)
        {
            return Ok(1);
        }
    }