How do you inherit route prefixes at the controller class level in WebApi?

httpget in web api
defaultdirectrouteprovider
route(api/(controller))
web api route attribute multiple parameters
web api controller inheritance
apicontroller attributes
web api route parameter validation
mvc routing vs web api routing

Note, I've read about the new routing features as part of WebApi 2.2 to allow for inheritance of routes. This does not seem to solve my particular issue, however. It seems to solve the issue of inheriting action level route attributes, but not route prefixes defined at the class level. http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#ARI

I would like to do something like this:

[RoutePrefix("account")]
public abstract class AccountControllerBase : ControllerBase { }

[RoutePrefix("facebook")]
public class FacebookController : AccountControllerBase
{
    [Route("foo")]
    public async Task<string> GetAsync() { ... }
}

[RoutePrefix("google")]
public class GoogleController : AccountControllerBase
{
    [Route("bar")]
    public async Task<string> GetAsync() { ... }
}

I would like the account route prefix to be inherited, so when defining the Facebook and Google controllers, I get routes:

~/account/facebook/foo
~/account/google/bar

Currently, routes are getting defined without the account portion from the base class.

I had a similar requirement. What i did was:

public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
    {
        var routePrefix =  base.GetRoutePrefix(controllerDescriptor);
        var controllerBaseType = controllerDescriptor.ControllerType.BaseType;

        if (controllerBaseType == typeof(BaseController))
        {
            //TODO: Check for extra slashes
            routePrefix = "api/{tenantid}/" + routePrefix;
        }

        return routePrefix;
    }
}

Where BaseController is the one defining what is the prefix. Now normal prefixes work and you can add your own. When configuring routes, call

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

Inheriting route attributes in ASP.NET Web API, NET Web API, you will see that it's marked as an “inheritable” attribute. for example when implementing a centralized route prefix for Web API. public abstract class GenericController<TEntity> : ApiController where TEntity  Have you tried creating a base class inherited from APIController, and then your controllers inherited from the base class. I don't think having different routes make a difference in this case. – Guanxi May 28 '14 at 18:48

As @HazardouS identifies, @Grbinho's answer is hard-coded. Borrowing from this answer to inheritance of direct routing and from @HazardouS, I wrote this object

public class InheritableDirectRouteProvider : DefaultDirectRouteProvider {}

Then overrode the following methods, hoping RoutePrefixAttribute would get inherited:

protected override IReadOnlyList<IDirectRouteFactory> GetControllerRouteFactories(HttpControllerDescriptor controllerDescriptor)
{
  // Inherit route attributes decorated on base class controller
  // GOTCHA: RoutePrefixAttribute doesn't show up here, even though we were expecting it to.
  //  Am keeping this here anyways, but am implementing an ugly fix by overriding GetRoutePrefix
  return controllerDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
}

protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
  // Inherit route attributes decorated on base class controller's actions
  return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
}

Sadly, per the gotcha comment, RoutePrefixAttribute doesn't show up in the factory list. I didn't dig into why, in case anyone wants to research a little deeper into this. So I kept those methods for future compatibility, and overrode the GetRoutePrefix method as follows:

protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
{
  // Get the calling controller's route prefix
  var routePrefix = base.GetRoutePrefix(controllerDescriptor);

  // Iterate through each of the calling controller's base classes that inherit from HttpController
  var baseControllerType = controllerDescriptor.ControllerType.BaseType;
  while(typeof(IHttpController).IsAssignableFrom(baseControllerType))
  {
    // Get the base controller's route prefix, if it exists
    // GOTCHA: There are two RoutePrefixAttributes... System.Web.Http.RoutePrefixAttribute and System.Web.Mvc.RoutePrefixAttribute!
    //  Depending on your controller implementation, either one or the other might be used... checking against typeof(RoutePrefixAttribute) 
    //  without identifying which one will sometimes succeed, sometimes fail.
    //  Since this implementation is generic, I'm handling both cases.  Preference would be to extend System.Web.Mvc and System.Web.Http
    var baseRoutePrefix = Attribute.GetCustomAttribute(baseControllerType, typeof(System.Web.Http.RoutePrefixAttribute)) 
      ?? Attribute.GetCustomAttribute(baseControllerType, typeof(System.Web.Mvc.RoutePrefixAttribute));
    if (baseRoutePrefix != null)
    {
      // A trailing slash is added by the system. Only add it if we're prefixing an existing string
      var trailingSlash = string.IsNullOrEmpty(routePrefix) ? "" : "/";
      // Prepend the base controller's prefix
      routePrefix = ((RoutePrefixAttribute)baseRoutePrefix).Prefix + trailingSlash + routePrefix;
    }

    // Traverse up the base hierarchy to check for all inherited prefixes
    baseControllerType = baseControllerType.BaseType;
  }

  return routePrefix;
}

Notes:

  1. Attribute.GetCustomAttributes(Assembly,Type,bool) method includes an "inherit" boolean... but it's ignored for this method signature. ARG! Because if it worked, we could have dropped the reflection loop... which takes us to the next point:
  2. This traverses up the inheritance hierarchy with reflection. Not ideal because of the O(n) calls through reflection, but required for my needs. You can get rid of the loop if you only have 1 or 2 levels of inheritance.
  3. Per the GOTCHA in the code, RoutePrefixAttribute is declared in System.Web.Http and in System.Web.Mvc. They both inherit directly from Attribute, and they both implement their own IRoutePrefix interface (i.e. System.Web.Http.RoutePrefixAttribute<--System.Web.Http.IRoutePrefix and System.Web.Mvc.RoutePrefixAttribute<--System.Web.Mvc.IRoutePrefix). The end result is that the library used to declare your controller (web.mvc or web.http) is the library whose RoutePrefixAttribute is assigned. This makes sense, of course, but I lost 2 hours refactoring code that was actually legit because my test case implicitly checked for System.Web.Http.RoutePrefixAttribute but the controller was declared with System.Web.Mvc... Hence the explicit namespace in the code.

The same web api controller name in 2 different areas, name spaces , I happened to chose a name for a web api controller eg. The same web api controller name in 2 different areas, name spaces, and route prefixes cancel each CustomerApiController (inheriting from ApiController) in one area (​Shipping). Having the same controller class name in different namespaces  As you can see from the above example, we are using the route attributes at the action level to define the routes, and furthermore, all the routes in the StudentsController start with the same prefix – students that mean students is the common prefix for all the routes available in the Student Controller. Here, you can set the common prefix “students” for the entire Student Controller by using the [RoutePrefix] attribute as shown below at the controller level.

Tried this in ASP.NET Web Api 2.2 (should/might also work in MVC):

public class InheritedRoutePrefixDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
    {
        var sb = new StringBuilder(base.GetRoutePrefix(controllerDescriptor));
        var baseType = controllerDescriptor.ControllerType.BaseType;

        for (var t = baseType; typeof(ApiController).IsAssignableFrom(t); t = t.BaseType)
        {
            var a = (t as MemberInfo).GetCustomAttribute<RoutePrefixAttribute>(false);
            if (a != null)
            {
                sb.Insert(0, $"{a.Prefix}{(sb.Length > 0 ? "/": "")}");
            }
        }

        return sb.ToString();
    }
}

It links the route prefixes together in the controller inheritance chain.

WebApi controller inheritance, Is it possible to inherit one controller from another and access their methods by same route? Something like this: [RoutePrefix("api/resource")] public class /​multiple-controller-types-with-same-route-prefix-asp-net-web-api. If you look at the definition of the RouteAttribute in ASP.NET Web API, you will see that it’s marked as an “inheritable” attribute. As such, it’s reasonable to assume that if you use that attribute on a base controller, it will be respected in a child controller you create off the base one.

Maybe it is late, but I think this base controller attribute will make it work:

[Route("account/[Controller]")]

Routing in ASP.NET Core - Quick Code, NET Core maps the incoming request based on the routes that you… with an api prefix (that is, URLs in the form of /api/[controller]/[action] ), then this is To test the route you just configured, add a new empty class in the Controllers and are hierarchical, which means that they support route inheritance. The answer you quoted has since been updated. As of WebApi 2.2 they created an extensibility point to allow the feature you wanted. Attribute rout can be inherited, but you need to configure it. I had the same requirement for a base API controller and after searching came across the same answer you quoted..NET WebAPI Attribute Routing and

I just ran into this same issue on a .NET Core 3.0 app (seems to be a new feature in MVC 6 so it won't work for MVC 5 and previous versions but may still be helpful for anyone else that stumbles across this problem). I don't have enough rep to make a comment on @EmilioRojo's answer but he is correct. Here is some more information from the Microsoft Docs to help people that come across the same issue.

Token replacement in route templates ([controller], [action], [area]) For convenience, attribute routes support token replacement by enclosing a token in square-braces ([, ]). The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined. In the following example, the actions match URL paths as described in the comments:

[Route("[controller]/[action]")]
public class ProductsController : Controller
{
    [HttpGet] // Matches '/Products/List'
    public IActionResult List() {
        // ...
    }

    [HttpGet("{id}")] // Matches '/Products/Edit/{id}'
    public IActionResult Edit(int id) {
        // ...
    }
}

Attribute routes can also be combined with inheritance. This is particularly powerful combined with token replacement.

[Route("api/[controller]")]
public abstract class MyBaseController : Controller { ... }

public class ProductsController : MyBaseController
{
   [HttpGet] // Matches '/api/Products'
   public IActionResult List() { ... }

   [HttpPut("{id}")] // Matches '/api/Products/{id}'
   public IActionResult Edit(int id) { ... }
}

What is New in ASP.Net Web API 2 Attribute Routing, Net Web API 2 Attribute Routing by providing walk through example. we used to define a route per controller inside “WebApiConfig.cs” class. There are different ways to define routing attributes, you can define route prefix on controller level, and you DbContext where “LearningContext” inherits from. To do this, we have the option to prefix the Route attribute value with a tilde (~) sign at the method level, for which we do not want to have the common prefix. So we add the tilde sign to the second method and make a direct call to the method. See the code below: Now let's send the request with the changed url's and see the results.

Routing and Action Selection in ASP.NET Web API, For a high-level overview of routing, see Routing in ASP. overloads, and so forth), and methods inherited from the ApiController class. Web API 2 supports a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily create URIs that describe hierarchies of resources.

Routing in ASP.NET Core, For controllers, see Routing to controller actions in ASP.NET Core. Endpoints are a low-level primitive like middleware, and aren't coupled to the routing Can be used as a prefix to a route parameter to bind to the rest of the URI. NET Core framework APIs that use RegularExpressions pass a timeout. Web API controller is a class which can be created under the Controllers folder or any other folder under your project's root folder. The name of a controller class must end with "Controller" and it must be derived from System.Web.Http.ApiController class. All the public methods of the controller are called action methods. The following is a

Web API Route and Route Prefix: Part 2, In this article you will learn Web API Route and Route Prefix in Web Services. level, in other words on the methods of the Web API controllers. at the class level, called RoutePrefix and set its value to the required prefix,  For a high-level overview of routing, see Routing in ASP.NET Web API. This article looks at the details of the routing process. If you create a Web API project and find that some requests don't get routed the way you expect, hopefully this article will help.

Comments
  • possible duplicate of .NET WebAPI Attribute Routing and inheritance
  • Not a duplicate. That question is asking about the RoutePrefix on the controller implementing the base controller. I'm asking about putting the RoutePrefix on the base controller. Please read again. Thanks.
  • @kurifodo worked for me. please accept answer if it worked for you too :)
  • This solution hard codes the desired prefix within the provider. This is not scalable. The OP wants inheritance based prefixes.
  • if (controllerBaseType == typeof(BaseApiController)) { var baseRoutePrefixObject = controllerBaseType.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(RoutePrefixAttribute))?.ConstructorArguments.FirstOrDefault().Value; String baseRoutePrefix = baseRoutePrefixObject as String; routePrefix = $"{baseRoutePrefix ?? String.Empty}/{routePrefix}"; } for excluding hardcode
  • This is a great answer, the only caveat is, what if system.web.mvc is not referenced at all? Is there a way to make this even more generic?
  • @JamesBlake If you're not including the System.Web.Mvc library in the project, the simplest approach would be to cull the line that references it. Other than that, you could use something like this to reflect on what assemblies are loaded... but that's overkill IMO
  • Worked for me. Just need to add config.MapHttpAttributeRoutes(new InheritableDirectRouteProvider()); to the WebApiConfig.
  • Could you show me an example ? I've copied you code to my project but it doesn't work. My controller structure is the same as the question's
  • You may want to add more information on why you believe that would work with potentially links to the documentation or your answer may be flagged as Low Quality. Here are some guidelines for How do I write a good answer?. This provided answer may be correct, but it could benefit from an explanation. Code only answers are not considered "good" answers. From review.
  • Here is the Documentation mentioning this pattern: docs.microsoft.com/en-us/aspnet/core/mvc/controllers/…