Route Disambiguation in ASP.NET Core (MVC 6)

2019-05-17 06:53发布

问题:

When ASP.NET Core encounters ambiguously named routes, it becomes inert. That is, the application will run without exceptions thrown but, it will fail to process any requests, on any controllers. The calling client receives 500 responses.

I'll show how I got into this mess, and I'd like suggestions of how to fix it.

I have a controller that looks like this:

[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]
public class AddressesController : Controller
{

    [HttpGet("{aid}", Name = "PostalLink")]
    public async Task<IActionResult> GetAddress(Guid id, Guid aid)
    {
        //...implementation is irrelevant for this question.
    }

    [HttpPost]
    [SwaggerResponseRemoveDefaults]
    [SwaggerResponse(HttpStatusCode.Created, Type = typeof(PostalRecord))]
    public async Task<IActionResult> CreateAddress(Guid id, [FromBody] PostalAddress address)
    {
        address.ID = Guid.NewGuid();
        await createAddress.Handle(address);

        return CreatedAtRoute("PostalLink", new { id = id, aid = address.ID });
    }

Why the two route prefixes on the controller? Because it fits my microservices (and Swagger documentation) strategy. Nevertheless, in this example ASP.NET Core does not know how to resolve the route name "PostalLink" because it is implicitly bound to the two prefixes:

[Route("api/Subscribers/{id}/[controller]")]
[Route("api/Organizations/{id}/[controller]")]

I can fix the problem simply by changing the HttpGet so that instead of this:

    [HttpGet("{aid}", Name = "PostalLink")]

I have this:

    [HttpGet("{aid}")] //the route is no longer "named"

Unfortunately, removing the route name is not a real option for me.

What is the prescribed way to fix this?

Below are some of the options I'm considering.

Possibility #1

Theoretically, ASP.NET could simply "figure it out" by itself. For example, if the current request resolved to the route containing the word "Subscribers", then the "PostalLink" name should reference that route. Seen this way, perhaps my code is exposing a bug, defect, or oversight in ASP.NET Core.

Possibility #2

I could collapse my two prefix routes into a single route like this:

[Route("api/{parent}/{id}/[controller]")]

This works, but it undermines my REST documentation strategy. I'm using Swashbuckle to publish endpoint metadata. I want a user of my API to expressly see that my "Addresses" API is serving either "Subscribers" or "Organizations". When I have two explicit route prefixes, the Swagger documentation works correctly (and I properly validate the URI used by the client).

Possibility #3

I could simply override the two prefixes like this:

    [HttpGet("~/api/Subscribers/{id}/Addresses/{aid}", Name = "SubscriberLink")]
    [HttpGet("~/api/Organizations/{id}/Addresses/{aid}", Name = "OrganizationLink")]
    public async Task<IActionResult> GetAddress(Guid id, Guid aid)
    {
        //...implementation is irrelevant for this question.
    }

Now my documentation and route validation works, but my implementation is forced to check which route was used to reach the endpoint. That is very doable, but very annoying.

Possibility #4

Perhaps there is a more expressive way to handle this problem without attribute-based-routing? If yes, please share!

Details

My project.json is configured as follows:

"frameworks": {
  "dnx46": { }
},

I am using DNX SDK version 1.0.0-rc1-update1. Also, I posted a related SO question for those who would like more context of what I am trying to do.

回答1:

If your route names are the same for all your actions, why not specify them directly on the controller ?

[Route("api/Subscribers/{id}/[controller]", Name = "SubscriberLink")]
[Route("api/Organizations/{id}/[controller]", Name = "OrganizationLink")]
public class AddressesController : Controller
{

    [HttpGet("{aid}")]
    public async Task<IActionResult> GetAddress(Guid id, Guid aid)
    {
        //...implementation is irrelevant for this question.
    }
}