Receiving 405 when expecting 404's when route matching occurs. Only defined pattern is a single route with a complex segment.
Take a basic ASP.NET Core Web Api for this spike example (I used visual studio's out of the box "Weather Forecast") and tweak the controller so that the single endpoint is a POST with a template of {id}:bar
on a controller whose route is foo
.
Ergo, only one endpoint is defined in the whole project: POST foo/{id}:bar
Now, when I run the above api locally and use Postman to hit it with different requests varying only the verb and route I get the returned status';
/foo/123:bar | /foo/literal | |
---|---|---|
GET | 405 (expected) | 405 (why?) |
POST | 200 (expected) | 404 (expected) |
DELETE | 405 (expected) | 405 (why?) |
I expected all verbs for /foo/literal to return a 404 because the only route that can be matched is one that has two segments: foo
and {id}:bar
and the latter segment is a complex segment with a parameter part followed by a literal part. Said literal part is not suffixed to this route, ergo, it should not be a match and surely 404's should be returned?
So is this a) a bug in route matching b) a mistake in my understanding, and are those 405's for `/foo/literal' in fact correct?
Below is the only file I changed from the example project, it is the file "WeatherForecastController"
using Microsoft.AspNetCore.Mvc;
namespace SpikeExample.Controllers
{
[ApiController]
[Route("foo")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpPost("{id}:bar")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
CodePudding user response:
I created an issue in the dotnet/aspnetcore repo and I received the below reply:
I believe this happens because we consider the HttpMethod before we evaluate the constraint and as a result, we produce a 405, since for us complex segments are just parameters with constraints.
I believe this is very much by design as we try to delay evaluating constraints as much as possible as they are expensive.
In this case, our HttpMethodMatcherPolicy sees the endpoint candidate and determines that it can't satisfy the methods but that there are other endpoints for other methods and that's why you see the 405