14

I have few controllers that inherit from the same base class. Among the different actions that they don't share with each other, they do have a few that are completely identical. I would like to have these on my base class because they all work completely the same it's just that they're accessed through different routes.

How should I define these actions with several different routes?

My inherited classes also have a RoutePrefixAttribute set on them so each of them is pointing to a different route.

Example

I have base abstract class called Vehicle and then inherited Car, Bike, Bus etc. All of them would have common action Move()

/bus/move
/car/move
/bike/move

How can I define action Move() on my base class Vehicle so that it will be executed on each subclass route?

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • 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
  • And have it what? `vehicle/move` for all of them? – Robert Koritnik May 28 '14 at 19:27
  • Are you planning to override the `Move()` in the child classes? – Nkosi Jan 11 '16 at 17:05
  • @Nkosi: No they would be defined on the base class and there only, but should be executed under different routes of the child classes... It may be that in some special circumstances I would be overriding those but that will be seldom done. Overriding would define new route anyway so that shouldn't be a problem. – Robert Koritnik Jan 12 '16 at 10:31
  • Then in that case check the answer I posted below – Nkosi Jan 12 '16 at 11:47

2 Answers2

19

Check the answer I gave here WebApi2 attribute routing inherited controllers, which references the answer from this post .NET WebAPI Attribute Routing and inheritance.

What you need to do is overwrite the DefaultDirectRouteProvider:

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

With that done you then need to configure it in your web API configuration:

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        .....
        // Attribute routing (with inheritance).
        config.MapHttpAttributeRoutes(new WebApiCustomDirectRouteProvider());
        ....
    }
}

You will then be able to do what you described like this:

public abstract class VehicleControllerBase : ApiController {
    [Route("move")] // All inheriting classes will now have a `{controller}/move` route 
    public virtual HttpResponseMessage Move() {
        ...
    }
}

[RoutePrefix("car")] // will have a `car/move` route
public class CarController : VehicleControllerBase { 
    public virtual HttpResponseMessage CarSpecificAction() {
        ...
    }
}

[RoutePrefix("bike")] // will have a `bike/move` route
public class BikeController : VehicleControllerBase { 
    public virtual HttpResponseMessage BikeSpecificAction() {
        ...
    }
}

[RoutePrefix("bus")] // will have a `bus/move` route
public class BusController : VehicleControllerBase { 
    public virtual HttpResponseMessage BusSpecificAction() {
        ...
    }
}
Matheus Lacerda
  • 5,983
  • 11
  • 29
  • 45
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 2
    I think some of these method signatures have changed in MVC 5.2.3 and this answer no longer seems to work for me. – Jon Gunter Apr 04 '18 at 18:37
  • I am still using this in production code on that same version. Ask a new question showing what is not working for you and let us see if we can solve the problem – Nkosi Apr 04 '18 at 20:32
  • Wouldn't it also work if I only define the Move method in the base controller without the WebApiCustomDirectRouteProvider? I tried this approach in my code and it worked. – Junlong Wang May 18 '20 at 20:34
0

This is what I did and it worked the way you mentioned in your question.

I created base ApiController class and inherited all my API controllers from it. I defined Delete operation in my Base class (which returns string "Not Supported") and didn't define delete on any of my child controller. Now when I do a delete on any of my controller I get the message "Not Supported" i.e. Base class's delete is called. ( I am doing Delete request on Child, and not on base i.e. /Bike/move)

But if I define a Delete on any of the controller it gives me warning of Hiding base implementation, but on doing Delete request for api I get - "An error has occurred."

I haven't tried doing RoutePrefix way.

Guanxi
  • 3,103
  • 21
  • 38
  • I suppose you've defined your routing centrally in the classic way (the `controller/action/id` we used to do in MVC) and not using declarative routing using attributes? Because I'm not having that much of a problem with common controller functionality as I have problems with declarative routing of my common functions that should be accessible through child routing definitions... – Robert Koritnik Jan 12 '16 at 10:34