Skip to content

Commit 0f7ef8a

Browse files
Limit Allow for 405 to actions in the same API version. Fixes #795
1 parent 555bf12 commit 0f7ef8a

File tree

1 file changed

+32
-11
lines changed

1 file changed

+32
-11
lines changed

src/Microsoft.AspNet.WebApi.Versioning/Controllers/ActionSelectorCacheItem.cs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Web.Http.Controllers;
1414
using System.Web.Http.Routing;
1515
using System.Web.Http.Services;
16+
using static Microsoft.Web.Http.Versioning.ApiVersionMapping;
1617
using static System.Net.HttpStatusCode;
1718
using static System.StringComparer;
1819

@@ -34,30 +35,27 @@ sealed class ActionSelectorCacheItem
3435
readonly CandidateAction[] combinedCandidateActions;
3536
readonly IDictionary<HttpActionDescriptor, string[]> actionParameterNames = new Dictionary<HttpActionDescriptor, string[]>();
3637
readonly ILookup<string, HttpActionDescriptor> combinedActionNameMapping;
37-
readonly HashSet<HttpMethod> allowedMethods = new HashSet<HttpMethod>();
3838
StandardActionSelectionCache? standardActions;
3939

4040
internal ActionSelectorCacheItem( HttpControllerDescriptor controllerDescriptor )
4141
{
4242
this.controllerDescriptor = controllerDescriptor;
4343

44-
var allMethods = this.controllerDescriptor.ControllerType.GetMethods( BindingFlags.Instance | BindingFlags.Public );
45-
var validMethods = Array.FindAll( allMethods, IsValidActionMethod );
44+
var validMethods = this.controllerDescriptor.ControllerType
45+
.GetMethods( BindingFlags.Instance | BindingFlags.Public )
46+
.Where( IsValidActionMethod )
47+
.ToArray();
4648

4749
combinedCandidateActions = new CandidateAction[validMethods.Length];
4850

4951
for ( var i = 0; i < validMethods.Length; i++ )
5052
{
51-
var method = validMethods[i];
52-
var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor, method );
53-
var actionBinding = actionDescriptor.ActionBinding;
53+
var actionDescriptor = new ReflectedHttpActionDescriptor( controllerDescriptor, validMethods[i] );
5454

55-
allowedMethods.AddRange( actionDescriptor.SupportedHttpMethods );
5655
combinedCandidateActions[i] = new CandidateAction( actionDescriptor );
57-
5856
actionParameterNames.Add(
5957
actionDescriptor,
60-
actionBinding.ParameterBindings
58+
actionDescriptor.ActionBinding.ParameterBindings
6159
.Where( binding => !binding.Descriptor.IsOptional && binding.Descriptor.ParameterType.CanConvertFromString() && binding.WillReadUri() )
6260
.Select( binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName ).ToArray() );
6361
}
@@ -193,6 +191,27 @@ IReadOnlyList<CandidateHttpActionDescriptor> FindMatchingActions( HttpController
193191
return selectedCandidates.Select( c => new CandidateHttpActionDescriptor( c ) ).ToArray();
194192
}
195193

194+
IEnumerable<HttpMethod> GetAllowedMethods( HttpControllerContext controllerContext )
195+
{
196+
var request = controllerContext.Request;
197+
var apiModel = controllerContext.ControllerDescriptor.GetApiVersionModel();
198+
var version = apiModel.IsApiVersionNeutral ? ApiVersion.Neutral : request.ApiVersionProperties().RequestedApiVersion!;
199+
var httpMethods = new HashSet<HttpMethod>();
200+
201+
for ( var i = 0; i < combinedCandidateActions.Length; i++ )
202+
{
203+
var actionDescriptor = combinedCandidateActions[i].ActionDescriptor;
204+
var endpointModel = actionDescriptor.GetApiVersionModel( Explicit );
205+
206+
if ( endpointModel.IsApiVersionNeutral || endpointModel.ImplementedApiVersions.Contains( version ) )
207+
{
208+
httpMethods.AddRange( actionDescriptor.SupportedHttpMethods );
209+
}
210+
}
211+
212+
return httpMethods;
213+
}
214+
196215
HttpResponseMessage CreateSelectionError( HttpControllerContext controllerContext )
197216
{
198217
var actionsFoundByParams = FindMatchingActions( controllerContext, ignoreVerbs: true );
@@ -204,9 +223,10 @@ HttpResponseMessage CreateSelectionError( HttpControllerContext controllerContex
204223

205224
var request = controllerContext.Request;
206225
var model = controllerContext.ControllerDescriptor.GetApiVersionModel();
226+
var httpMethods = GetAllowedMethods( controllerContext );
207227
var exceptionFactory = new HttpResponseExceptionFactory( request, new Lazy<ApiVersionModel>( () => model ) );
208228

209-
return exceptionFactory.CreateMethodNotAllowedResponse( model.IsApiVersionNeutral, allowedMethods );
229+
return exceptionFactory.CreateMethodNotAllowedResponse( model.IsApiVersionNeutral, httpMethods );
210230
}
211231

212232
HttpResponseMessage CreateActionNotFoundResponse( HttpControllerContext controllerContext )
@@ -276,9 +296,10 @@ CandidateAction[] GetInitialCandidateList( HttpControllerContext controllerConte
276296
{
277297
var request = controllerContext.Request;
278298
var model = controllerContext.ControllerDescriptor.GetApiVersionModel();
299+
var httpMethods = GetAllowedMethods( controllerContext );
279300
var exceptionFactory = new HttpResponseExceptionFactory( request, new Lazy<ApiVersionModel>( () => model ) );
280301

281-
throw exceptionFactory.NewMethodNotAllowedException( model.IsApiVersionNeutral, allowedMethods );
302+
throw exceptionFactory.NewMethodNotAllowedException( model.IsApiVersionNeutral, httpMethods );
282303
}
283304

284305
var candidatesFoundByName = new CandidateAction[actionsFoundByName.Length];

0 commit comments

Comments
 (0)