Skip to content

Getting 400 "ApiVersionUnspecified " instead of 405 when using wrong HTTP method despite AssumeDefaultVersionWhenUnspecified = true #254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
krzkraw opened this issue Feb 13, 2018 · 5 comments

Comments

@krzkraw
Copy link

krzkraw commented Feb 13, 2018

I'm using Microsoft.AspNet.WebApi.Versioning v2.2.0 in my solution.

Hello2Controller's method Get() has [Route("hello")] and [Route("{version:apiVersion}/hello")] routes. Controller itself is [ApiVersion("2")].
HelloControler's method Get() is the same, however controller is [ApiVersion("1", Deprecated = true)]

So both api/hello endpoints support only HTTP GET in both v1 and v2.
Calling GET ...api/hello or api/1/hello or api/2/hello returns 200 OK.
Calling POST (or any unsupported verb for that matter) on api/1/hello or api/2/hello returns 405 Method Not Allowed - "UnsupportedApiVersion The requested resource with API version '1' does not support HTTP method 'POST'." , which is expected correct response.
However, when you do NOT specify API version and make a call like this POST ...api/hello response is 400 Bad Request - ApiVersionUnspecified An API version is required, but was not specified. - which is not expected correct response as it should be 405 too because of AssumeDefaultVersionWhenUnspecified being set to true.

So Web Api is not assuming the default API version which is set to 1.0 when request has wrong HTTP methods and returns Bad Request instead of Method Not Allowed. How this can be resolved and 405 returned when API version is not set explicitly? I've searched trough numerous open and closed issues, wikis and stackoverflow posts but I have not came across issue like this yet.

Here is versioning code from WebApiConfig:

config.AddApiVersioning(options =>
            {
                options.ReportApiVersions = true;
                options.AssumeDefaultVersionWhenUnspecified = true;
                options.ApiVersionSelector = new CurrentImplementationApiVersionSelector(options);
            });

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

            config.MapHttpAttributeRoutes(constraintResolver, new RoutePrefixProvider());

And here are the example controllers

[ApiVersion("2")]
    public class Hello2Controller : ApiController
    {
        [NoAuthorization]
        [HttpGet]
        [Route("hello")]
        [Route("{version:apiVersion}/hello")]
        public IHttpActionResult Get()
        {
            return Ok("Response from Hello2Controller");
        }
    }
[ApiVersion("1")]
    public class HelloController : ApiController
    {
        [NoAuthorization]
        [HttpGet]
        [Route("{version:apiVersion}/hello")]
        public IHttpActionResult Get()
        {
            return Ok("Response from HelloController");
        }
    }
@commonsensesoftware
Copy link
Collaborator

Looks like there is an edge case with this specific combination that results in the wrong status code and error message being produced. It's a very simple fix. I should have it out soon.

@lxalln
Copy link

lxalln commented Mar 2, 2018

@commonsensesoftware We are experiencing this, any time we hit attempt to 'GET' against a route that is setup for 'POST' only.

I was having a poke around in the code to see if I can spot this, but no luck yet. Any pointers?

@jonsagara
Copy link

jonsagara commented Mar 3, 2018

I had this issue, and it had to do with calling app.UseStatusCodePagesWithReExecute in Startup.cs. Erroneous API requests were being rewritten to the MVC error handler to show status code pages, which the API versioning doesn't know how to handle.

I solved this by disabling status code pages for API requests. In my API authentication handler, at the very beginning of the request in HandleAuthenticateAsync:

// Disable status code page features. This is an API, and we don't need friendly status code pages.
var statusCodePagesFeature = Context.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
    statusCodePagesFeature.Enabled = false;
}

How you handle this in your projects will likely vary, but it's worth a shot to see if this works for you, too.

See: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling

@commonsensesoftware
Copy link
Collaborator

Apologies for the delay. The change in Web API was quick and easy, but quite a few internal changes were required for ASP.NET Core. I usually keep the package releases in-sync to keep things straight in mind. As luck would have it, I was also out of the country on vacation for 10 days. I'm just getting things caught up with the 'ol j-o-b and everything happening on GitHub. I hope to have this out over the weekend.

@jonsagara suggestion might work. I can confirm that there is a legimate bug that cause cause the wrong status code to be returned, but this only happens when the an implicit version is allowed, the controller is matched, but there is no corresponding action. It was a short-circuit mistake in the logic. You should get 405 in these cases.

Other alternatives include customizing the error response provider to map things to 405 on your own until the fix is out. The only difference between the two response paths is whether you get 400 or 405. The error code and message will be the same. I've tried to keep things a REST compliant as possible, even ASP.NET Core doesn't currently support 405.

Thanks for you patience.

@lxalln
Copy link

lxalln commented Mar 3, 2018

@jonsagara we are already disabling status code pages for api requests. We are seeing this on non-api requests. I'm not entirely sure how users are producing the error, but I can reproduce it by browsing to a URL that only has a POST action.

This sounds more like what @commonsensesoftware is talking about. So I will wait for the next release. We've been suffering from this for a while now, it's only minor, so don't rush to get it out on my behalf 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants