Aspnet-api-versioning: OPTIONS request being handled and returns 405

Created on 29 Jan 2019  路  11Comments  路  Source: microsoft/aspnet-api-versioning

Whenever I issue an OPTIONS request to my controller route, 3 candidates are pre-selected but then none match, because they're PUT, DELETE, PATCH and not options, obviously.
Why is the versioning getting in the way of an OPTIONS request?

I get this in the output:

Microsoft.AspNetCore.Mvc.Routing.DefaultApiVersionRoutePolicy:Information: Multiple candidate actions were found, but none matched the requested service API version '1'

PS: I've debugged into the source and basically got into ApiVersionActionSelector.EvaluateActionConstraints and verified what I mentioned above.

Thanks in advance!

answered asp.net core question

Most helpful comment

I've looked into this more. It's difficult to simulate your conditions without a repro. Thus far, I've been unable to recreate your scenario. I'm able to get CORS working just fine. Your registration of middleware seems correct, but you don't need to muck with the API versioning middleware. Currently, all it does is register the API versioning feature into the pipeline, which simply needs to occur any time before UseMvc(). This is automatically wired-up for you. For manually calling UseApiVersioning to have the intended effect, you must register the API versioning services with options.RegisterMiddleware = false; the default is true. You're not expected to ever have to mess with this except for the most advanced scenarios.

Using the Basic example for ASP.NET Core, I was able to get things working as expected with:

```c#
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc( options => options.EnableEndpointRouting = true )
.SetCompatibilityVersion( Latest );
services.AddCors();
services.AddApiVersioning( options => options.ReportApiVersions = true );
}

public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyOrigin().WithMethods("GET", "POST"));
app.UseMvc();
}

You didn't indicate which routing method you're using, but I confirmed that this works with both _Endpoint Routing_ and _Legacy Routing_.

I tested the following preflight:

```http
OPTIONS /api/values?api-version=2.0 HTTP/1.1
Host: localhost:5000
Origin: http://localhost
Access-Control-Request-Method: GET
Accept: application/json
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Origin: *

Clearly you are seeing a different behavior, but without more information, I'm unable to setup the same conditions.

All 11 comments

API versioning _gets in the way_ of every request that makes it a controller action. It versions all APIs, not just specific HTTP methods such as GET, PUT, POST, DELETE, or PATCH. It's perfectly reasonable to have an API that maps to OPTIONS. I have used this myself as way to query the server as to which API versions and other features are available. This can be useful for client tools.

I presume the real question is that you are trying to configure CORS and things are not working as you expect. This is likely due to configuration. The order of registration middleware can also be a factor.

Do you have a repro you can share? That will go miles to helping you resolve your issue. Sharing your configuration and the library version you are using will help as well.

@commonsensesoftware I understand and agree with your point, but in the scenario that I don't have a custom response to OPTIONS shouldn't it let the CORS middleware do its thing?
I don't have the full code to share, unfortunately, but I'm setting up in the following order:

  1. UseCors
  2. UseApiVersioning
  3. UseMvc

in terms of config I have, for versioning:

                services.AddVersionedApiExplorer(o =>
                {
                    o.DefaultApiVersion = new ApiVersion(1, 0);
                    o.AssumeDefaultVersionWhenUnspecified = true;
                    o.SubstituteApiVersionInUrl = true;
                    o.GroupNameFormat = "'v'VVV";
                });
                services.AddApiVersioning(
                    o =>
                    {
                        o.ReportApiVersions = true;
                        o.AssumeDefaultVersionWhenUnspecified = true;
                        o.DefaultApiVersion = new ApiVersion(1, 0);
                    });

I've looked into this more. It's difficult to simulate your conditions without a repro. Thus far, I've been unable to recreate your scenario. I'm able to get CORS working just fine. Your registration of middleware seems correct, but you don't need to muck with the API versioning middleware. Currently, all it does is register the API versioning feature into the pipeline, which simply needs to occur any time before UseMvc(). This is automatically wired-up for you. For manually calling UseApiVersioning to have the intended effect, you must register the API versioning services with options.RegisterMiddleware = false; the default is true. You're not expected to ever have to mess with this except for the most advanced scenarios.

Using the Basic example for ASP.NET Core, I was able to get things working as expected with:

```c#
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc( options => options.EnableEndpointRouting = true )
.SetCompatibilityVersion( Latest );
services.AddCors();
services.AddApiVersioning( options => options.ReportApiVersions = true );
}

public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
app.UseCors(builder => builder.AllowAnyHeader().AllowAnyOrigin().WithMethods("GET", "POST"));
app.UseMvc();
}

You didn't indicate which routing method you're using, but I confirmed that this works with both _Endpoint Routing_ and _Legacy Routing_.

I tested the following preflight:

```http
OPTIONS /api/values?api-version=2.0 HTTP/1.1
Host: localhost:5000
Origin: http://localhost
Access-Control-Request-Method: GET
Accept: application/json
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Origin: *

Clearly you are seeing a different behavior, but without more information, I'm unable to setup the same conditions.

I'm using .NET Core 2.1 so I don't think that option exists? Either way not specifying, so using the default routing mechanism.
Removed the UseApiVersioning and moved the AddCors right after AddMvc and then AddApiVersioning comes after that, issue is gone now.
But moving back to what I had the issue isn't happening either.
I've done a refactor to move code to a base startup class so that might've changed something, not sure now.. odd!

I don't have a complete picture of your environment and setup. A repro, even stripped down, is always best so I don't guess at your setup. The order in which you register services shouldn't matter, but I personally like to register them in logical order similar to middleware. In much older versions of API Versioning, it might have mattered.

I presume you meant ASP.NET Core 2.1. ASP.NET Core doesn't a specific affinity to .NET Core, but I can see how many people have been confused by the naming (thanks _marketing team_ >_<). You'll want to make sure you at least using API Versioning 3.0. You might also consider moving to ASP.NET Core 2.2, but set the compatibility to 2.1 if that's what you really need. This will allow you to use the latest version of API Versioning (3.1.2). I don't really plan on patching 3.0.x unless there is a really strong demand for it. It's quite a bit of work to maintain multiple branches; especially, when the current branch is forward compatible.

You set the compatibility level like this:

c# services.AddMvc().SetCompatibilityVersion( CompatibilityVersion.Version_2_1 );

I tried making this change using the modifications to the _Basic_ example that I listed above and it still works. I even tried dropping the versions back and that works too (though I did notice that CORS isn't reporting the Allow header correctly; maybe it's a bug in the old version).

The only other difference I can think of is hosting, which _might_ make a difference. I'm attaching the 2.1 version that's working for me. Perhaps you can work it to repro your scenario or troubleshoot what's different in your setup.

Basic CORS Sample.zip

I did mean ASPNET Core 2.1 :) yes, I have that SetCompatVersion indeed

Were you able to figure this out or otherwise resolve the issue? Did the example help?

It would appear that you've resolved your issue. If the issue isn't resolved, feel free to come back. We can reopen the issue and investigate further. Thanks.

@commonsensesoftware
With compatbilityversion 2.1 it works fine. with 2.2 it fails.
so please reopen and solve this problem.
here is a sample code.

eatklik.zip

@thezeeshanasghar I can't say exactly why your CORS configuration isn't working, but it's not related to API versioning. From what I can tell, you are not using API Versioning at all. Your question can more appropriately be answered on the ASP.NET repo(I'm not part of the ASP.NET team) or on Stack Overflow. Apologies for not being able to help you troubleshoot your issue.

At some point something else changed and the issue went away, sorry about the delay in feedback.

Was this page helpful?
0 / 5 - 0 ratings