1
votes

When using only attribute routing in an ASP.NET Core 5.0 app, for example, with the following in Startup:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

A controller class with a single public method that has no route attribute will still be matched to a GET request that otherwise matches that controller.

For example GET api/test will match the Index method below (it doesn't matter what the method is called) even though the method doesn't have a route attribute:

[ApiController]
[Route("api/[controller]")]
public class TestController
{
    public IActionResult Index()
    {
        return Ok();
    }
}

If, however, another public method is added to that controller class, also without an attribute, an AmbiguousMatchException is thrown for GET api/test because routing can't otherwise figure out which method to use.

If a [HttpGet] attribute is added to one of those methods, the issue is resolved.

I understand that one solution is to just ensure that all methods have appropriate route attributes, or that only one method on the controller is public.

Is there a way to prevent this default/fallback behaviour where methods that don't have explicit attributes are still matched?

Can attribute routing be configured to only match methods that have explicit routing attributes on them?

1
I can't check it right now, but I guess you can add some custom midleware right after your app.UseRouting(); call in a Startup.Configure method. Something like thisvasily.sib
Perhaps you could do something with an IActionDescriptorProviderLlama

1 Answers

2
votes

Setting the route to:

[Route("api/{controller}/{action}")] 

Will indicate that action has to be specified, it will not default to anything unless you specify default action like this:

[Route("api/{controller}/{action=Index}")] 

Edit:

If you want to be forced to be explicit about the routes, then you can remove the [Route()] attribute from controller level and add it above every action like this

[ApiController]
public class TestController
{
    [Route("api")]
    [Route("api/[controller]")]
    [Route("api/[controller]/index")]
    public IActionResult Index()
    {
        return Ok();
    }

    [Route("api/[controller]/asdf")]
    [Route("api/[controller]/asdf/{id?}")]
    public IActionResult Index2(int? id)
    {
        return Ok();
    }
}

The code will then not compile unless every action has a Route() attribute or other similar attributes like HttpGet(), HTTpPost() etc.