AspNetCoreOData icon indicating copy to clipboard operation
AspNetCoreOData copied to clipboard

Routing by convention for functions creates duplicate GET route

Open davidyee opened this issue 3 years ago • 0 comments

Assemblies affected ASP.NET Core OData 8.0.10

Describe the bug While using routing by convention for OData Functions it is required to use [HttpGet] before the FunctionRoutingConvention handles the route generation. However, when the [HttpGet] is applied, it also appears to be generating a route for the blank path too.

According to https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/Routing/Conventions/FunctionRoutingConvention.cs#L39 [HttpGet] must be specified on the route as well which may be the reason it is being picked up and handled "twice".

The same logic appears to be in the Action routing convention as well so I would expect the same issue to occur for OData Actions as well.

My ODataController:

[Route("api/v1/Component")]
[ApiController]
public class ComponentController : ODataController {

	[EnableQuery]
	[HttpGet] // must be specified
	public ActionResult<IQueryable<Component>> GetComponents(string jobName) { ... }

}

Output in $odata debug: image

Data Model

var entityType = builder.EntitySet<Component>(nameof(Component)).EntityType;
entityType.Select().Expand().OrderBy().Filter().Count().Page(ApiDefaultLimits.MaxTop, ApiDefaultLimits.PageSize);

var et2 = entityType
	.Collection
	.Function("GetComponents")
	.ReturnsCollectionFromEntitySet<Component>(nameof(Component));
et2.Parameter<string>("jobName");

Expected behavior While using routing convention, it is expected that only a single route is generated for functions. However, a blank route (at /api/v1/Component) is also generated as shown in the screenshot above.

According to #428 we're not supposed to mix the usage of HTTP attributes (e.g. [HttpGet]) for methods handled by routing convention; however, the function will not be handled by the FunctionRoutingConvention without one being declared. This requirement appears to be inconsistent in behaviour compared to the other routing conventions.

Workaround Rename the GetComponents method to something different like GetComponentsFunction and manually specify the route in the HTTP Get attribute like so. For instance:

[HttpGet("GetComponents(jobName={jobName})")]
public ActionResult<IQueryable<Component>> GetComponentsFunction(string jobName) { ... }

This workaround would be ignoring the utilization of routing by convention though.

davidyee avatar Jul 06 '22 09:07 davidyee