In the current version of Web API, when the action selector does not find a matching action for a HTTP method it returns a 404 status code even when the resource URI supports other methods (e.g. it supports GET and POST but not DELETE, PUT, etc.)
Based on the HTTP spec, this is not the correct behaviour as a 404 means that "The server has not found anything matching the Request-URI". In this case, there is a resource at the Request-URI as an operation with a supported verb succeeds so a Not Found status code is misleading and incorrect. Instead, it should return a 405 status code which means "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI". This is correct as only the particular method is not supported.
__NOTE__:
This bug has been fixed in the default action selector and so this scenario works for a non-ODataController. But this doesn’t work for OData controller as it creates a custom selector wrapping the default action selector and always returns 404...
OData action selector:
```
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Response disposed later")]
public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
if (controllerContext == null)
{
throw Error.ArgumentNull("controllerContext");
}
HttpRequestMessage request = controllerContext.Request;
ODataPath odataPath = request.GetODataPath();
IEnumerable<IODataRoutingConvention> routingConventions = request.GetODataRoutingConventions();
IHttpRouteData routeData = controllerContext.RouteData;
if (odataPath == null || routingConventions == null || routeData.Values.ContainsKey(ODataRouteConstants.Action))
{
return _innerSelector.SelectAction(controllerContext);
}
ILookup<string, HttpActionDescriptor> actionMap = _innerSelector.GetActionMapping(controllerContext.ControllerDescriptor);
foreach (IODataRoutingConvention routingConvention in routingConventions)
{
string actionName = routingConvention.SelectAction(odataPath, controllerContext, actionMap);
if (actionName != null)
{
routeData.Values[ODataRouteConstants.Action] = actionName;
return _innerSelector.SelectAction(controllerContext);
}
}
throw new HttpResponseException(request.CreateErrorResponse(
HttpStatusCode.NotFound,
Error.Format(SRResources.NoMatchingResource, controllerContext.Request.RequestUri),
Error.Format(SRResources.NoRoutingHandlerToSelectAction, odataPath.PathTemplate)));
}
```
Based on the HTTP spec, this is not the correct behaviour as a 404 means that "The server has not found anything matching the Request-URI". In this case, there is a resource at the Request-URI as an operation with a supported verb succeeds so a Not Found status code is misleading and incorrect. Instead, it should return a 405 status code which means "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI". This is correct as only the particular method is not supported.
__NOTE__:
This bug has been fixed in the default action selector and so this scenario works for a non-ODataController. But this doesn’t work for OData controller as it creates a custom selector wrapping the default action selector and always returns 404...
OData action selector:
```
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Response disposed later")]
public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
if (controllerContext == null)
{
throw Error.ArgumentNull("controllerContext");
}
HttpRequestMessage request = controllerContext.Request;
ODataPath odataPath = request.GetODataPath();
IEnumerable<IODataRoutingConvention> routingConventions = request.GetODataRoutingConventions();
IHttpRouteData routeData = controllerContext.RouteData;
if (odataPath == null || routingConventions == null || routeData.Values.ContainsKey(ODataRouteConstants.Action))
{
return _innerSelector.SelectAction(controllerContext);
}
ILookup<string, HttpActionDescriptor> actionMap = _innerSelector.GetActionMapping(controllerContext.ControllerDescriptor);
foreach (IODataRoutingConvention routingConvention in routingConventions)
{
string actionName = routingConvention.SelectAction(odataPath, controllerContext, actionMap);
if (actionName != null)
{
routeData.Values[ODataRouteConstants.Action] = actionName;
return _innerSelector.SelectAction(controllerContext);
}
}
throw new HttpResponseException(request.CreateErrorResponse(
HttpStatusCode.NotFound,
Error.Format(SRResources.NoMatchingResource, controllerContext.Request.RequestUri),
Error.Format(SRResources.NoRoutingHandlerToSelectAction, odataPath.PathTemplate)));
}
```