Custom routing doesn't work like it did in 7.4.1
I created a small sample app based upon
https://docs.microsoft.com/en-us/odata/webapi/custom-routing-convention
Am I missing something. Worked fine in 7.4.1 anyone have a working sample?
7.5.0+
Was fine in 7.4.1
var conventions = new List<IODataRoutingConvention>();
conventions.Insert(0, new CustomPropertyRoutingConvention());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Filter().OrderBy().Count().Expand().MaxTop(100);
endpoints.MapODataRoute("odata", "api", GetEdmModel(), new DefaultODataPathHandler(), conventions);
});
public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention
{
public override string SelectAction(
RouteContext routeContext,
SelectControllerResult controllerResult,
IEnumerable<ControllerActionDescriptor> actionDescriptors)
{
return "GetName";
}
}
```
IEdmModel GetEdmModel()
{
var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Product>("Products");
return odataBuilder.GetEdmModel();
}
```
public class ProductsController :ODataController
{
public string GetOrders([FromODataUri] int key)
{
return "test1";
}
public string GetAddress([FromODataUri] int key)
{
return "test1";
}
public string GetName([FromODataUri] int key)
{
return "test3";
}
}
Trigger the GetName Action
Produces an error.
AmbiguousMatchException: The request matched multiple endpoints. Matches:
OdataTest1.Controllers.ProductsController.GetOrders (OdataTest1)
OdataTest1.Controllers.ProductsController.GetAddress (OdataTest1)
OdataTest1.Controllers.ProductsController.GetName (OdataTest1)
Hi,
Can you please provide us the repro of the sample you created and also request url(s) you were trying
Thanks
It's exactly the steps discussed in
https://docs.microsoft.com/en-us/odata/webapi/custom-routing-convention
With the latest nuget packages
I will find the sample
@p6345uk could you share the endpoint that your request is hitting? Or does the error occur during startup before you even issue a request?
Its when your making a request
@p6345uk could you share the exact request you're making?
Is it GET /odata/Products(1)/Name? for example
It could be anything that triggers the
CustomPropertyRoutingConvention
It appears that
return "GetName";
Then triggers the error
In my example
api/products
Should trigger the SelectAction inside of the custom Routing - I believe
Which it does
but me specifying
return "GetName"; doesnt appear to work
Where it did in version 7.4.1
Added a sample
https://github.com/p6345uk/OdataTestRouting
if you move the versions between 7.4.1 and the latest you can see the differences
@p6345uk out of curiosity, why did you not use the built-in routing convention since routing to a property is supported by default. If you have a method called GetName(int key) in your ProductsController and make a request to /Products(1)/Name, the built-in routing will select the GetName(int key) method.
@p6345uk I tried your sample project and was able to reproduce your error.
The error seems to stem from the fact that you have a [Route("api/[controller]")] attribute on your controller. This probably causes conflicts with the OData WebAPI routing mechanics.
If you remove [Route] attribute, then the error does not occur.
However, when you remove the [Route] attribute, your sample will return a 404 error when you visit the endpoint /api/Products. This is due to details of how the ODataActionSelector works:
If your action method has parameters, e.g. GetName(int key), then those parameters need to be added as keys in the route context's RouteData. (some parameters are exempt from this rule, such ODataQueryOptions, cancellation tokens, parameters that are bound to the body of POST/PUT/PATCH requests, etc.)
So if you want your custom routing convention to correctly route to an action that has parameters, then add those parameters to the route data, e.g. to properly route to the GetName(int key) method, then you should have an entry for the key parameter in the route data values.
c#
public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention
{
public override string SelectAction(
RouteContext routeContext,
SelectControllerResult controllerResult,
IEnumerable<ControllerActionDescriptor> actionDescriptors)
{
routeContext.RouteData.Values.Add("key", 1); // you can retrieve the key from query options, route segments or other sources
return "GetName";
}
}
@p6345uk out of curiosity, why did you not use the built-in routing convention since routing to a property is supported by default. If you have a method called
GetName(int key)in yourProductsControllerand make a request to/Products(1)/Name, the built-in routing will select theGetName(int key)method.
In my specific case we have a myriad of types you can specify in the route
/{Type}
Which I am setting up OData Actions to achieve however i am using the Routing conventions to direct them to different actions on the controller
@p6345uk I tried your sample project and was able to reproduce your error.
The error seems to stem from the fact that you have a
[Route("api/[controller]")]attribute on your controller. This probably causes conflicts with the OData WebAPI routing mechanics.If you remove
[Route]attribute, then the error does not occur.However, when you remove the
[Route]attribute, your sample will return a 404 error when you visit the endpoint/api/Products. This is due to details of how the ODataActionSelector works:If your action method has parameters, e.g.
GetName(int key), then those parameters need to be added as keys in the route context'sRouteData. (some parameters are exempt from this rule, suchODataQueryOptions, cancellation tokens, parameters that are bound to the body of POST/PUT/PATCH requests, etc.)So if you want your custom routing convention to correctly route to an action that has parameters, then add those parameters to the route data, e.g.
public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention { public override string SelectAction( RouteContext routeContext, SelectControllerResult controllerResult, IEnumerable<ControllerActionDescriptor> actionDescriptors) { routeContext.RouteData.Values.Add("key", 1); // you can retrieve the key from query options, route segments or other sources return "GetName"; } }
Will give that a try :)