I am using .NET Core 3.0 Web API, I have a situation where I have set the prefix in the baseapicontroller and also set the route in the derived controller, theoretically I thought that it would work like "baseapicontroller route/derived controller route" but it is not working like that, .net core picks only the derived class route and ignore the baseapicontroller route.
I have tried to use the attribute "Route" and "RoutePrefix", it is still the same.
[ApiController]
[RoutePrefix("api/v1")]
//[Route("api/v1")]
public class BaseApiController : ControllerBase
{
}
derived controller
[Route("testing-route")]
public class TestRouteController : BaseApiController
{
[HttpGet]
public string test()
{
return "";
}
}
I want the URL to be "api/v1/testing-route/test", I don't want to repeat api/v1 in every controller. and also I know people might say that I can use "api/v1/[controller]/[action]" but that is not my requirement. I want to use the custom route for the controller.
Looking forward to get any good answers.
Thanks
The built-in RouteAttribute will not work with controller inheritance by default. However, to achieve your goal, you could create a custom IControllerModelConvention to apply a rule that takes consideration of controller inheritance at startup-time.
To avoid mixing up with the default [RouteAttribute], I create a custom MyRoutePrefixAttribute instead of using the default [RouteAttribute] (Feel free to use the [RouteAttribute] if you think this behavior should be overrided ):
[AttributeUsage(AttributeTargets.Class)]
public class MyRoutePrefixAttribute : Attribute
{
public MyRoutePrefixAttribute(string prefix) { Prefix = prefix; }
public string Prefix { get; }
}
And to lookup this [MyRoutePrefixAttribute] from the inheritance chain, I create a RoutePrefixConvention that will combine the prefixes recursively:
public class RoutePrefixConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
foreach (var selector in controller.Selectors)
{
var prefixes = GetPrefixes(controller.ControllerType); // [prefix, parentPrefix, grandpaPrefix,...]
if(prefixes.Count == 0) continue;
// combine these prefixes one by one
var prefixRouteModels = prefixes.Select(p => new AttributeRouteModel(new RouteAttribute(p.Prefix)))
.Aggregate( (acc , prefix)=> AttributeRouteModel.CombineAttributeRouteModel(prefix, acc));
selector.AttributeRouteModel = selector.AttributeRouteModel != null ?
AttributeRouteModel.CombineAttributeRouteModel(prefixRouteModels, selector.AttributeRouteModel):
selector.AttributeRouteModel = prefixRouteModels;
}
}
private IList<MyRoutePrefixAttribute> GetPrefixes(Type controlerType)
{
var list = new List<MyRoutePrefixAttribute>();
FindPrefixesRec(controlerType, ref list);
list = list.Where(r => r!=null).ToList();
return list;
// find [MyRoutePrefixAttribute('...')] recursively
void FindPrefixesRec(Type type, ref List<MyRoutePrefixAttribute> results)
{
var prefix = type.GetCustomAttributes(false).OfType<MyRoutePrefixAttribute>().FirstOrDefault();
results.Add(prefix); // null is valid because it will seek prefix from parent recursively
var parentType = type.BaseType;
if(parentType == null) return;
FindPrefixesRec(parentType, ref results);
}
}
}
this convention will NOT influence the performance: it only searches all the [MyRoutePrefixAttribute] attributes through the inheritance chain at startup time.
Finally, don't forget to add this convention within your startup:
services.AddControllersWithViews(opts =>{
opts.Conventions.Add(new RoutePrefixConvention());
});
let's create three controllers to test the above RoutePrefixConvention:
RootApiController -> BaseApiController -> TestRouteController
[ApiController]
[MyRoutePrefix("root/controllers")]
public class RootApiController : ControllerBase { }
[MyRoutePrefix("api/v1")]
public class BaseApiController : RootApiController { }
[Route("testing-route")]
public class TestRouteController : BaseApiController
{
[HttpGet]
public string test()
{
return "abc";
}
}
Now when accessing /root/controllers/api/v1/testing-route, We'll get a string of abc:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With