Good-bye WCF: Using WebApi for Exposing Controller Contracts

Motivation

WebApi 2 introduced a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes and gives you more control over mapping of URIs in WebApi.
This pattern is more convenient than the earlier style of routing, called convention-based routing, in that endpoint declarations are more explicit and visible to developer.

But when comparing it to WCF Service declaration, WebApi's attribute routing mechanism still lacks key features, such as the ability to customize routing implementation or specify Route attributes on base controllers and interfaces.

In addition, in WCF, all services expose contracts. A contract is a platform-neutral and standard way of describing what the service does, and enables other services to consume service operations via service-proxy client objects.
This comes in handy when resources are frequently shared between services in your SOA architecture.

So, as we migrated our code base and services from WCF stack to the more lightweight, REST orientied WebApi stack, we felt we had lost some abilities along the way - mainly in terms of easy integrations between our services.
To tackle that, we first implemented ad-hoc RPC-like clients that generated REST requests to the target service.
Then went on building Dynamic Proxy client, that using interception of method invocation and custom attributes (specified URI templates) constructed an http request and invoked it.
Downside was duplicate declaration of service contract - first with WebApi Route Attributes on Controller and then on our exposed service interfaces.

Customizing Attribute Routing

WebApi 2.2 added improvements to attribute routing and now provides extensibility, which allows full control over how routes are discovered and configured.

The following code enables retrieving the Route attribute from base controllers:

public class CustomDirectRouteProvider : DefaultDirectRouteProvider  
{
    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
    }
} 

actionDescriptor provides information about the controller action method. Using relflection, inherited custom attibutes can be extracted from the base controller and returned as a concrete method attribute.

Addition of Interface Contracts

So we have the ability to forward routing from base to derived methods, but does it work for interface methods?

As opposed to base classes, which are inherited, any methods, properties or members present at base class are also present on derived class.
Interfaces are implemented, meaning that the custom attribute specified on the interface declaration does not belong to the class and doesn't get inherited.

For that purpose, some reflection "heavy-lifting" will be required here.
Additional route discovery, which relies on controller interfaces, is added to the CustomDirectRouteProvider implementation specified above.

public class CustomDirectRouteProvider : DefaultDirectRouteProvider  
{
    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(
        HttpActionDescriptor actionDescriptor)
    {
        var actionRoutes = new List<IDirectRouteFactory>();

        // get attrtibute routings from base controller
        var reflectedActionDescriptor = 
            actionDescriptor as ReflectedHttpActionDescriptor;

        var routes = 
            reflectedActionDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
        if (routes != null)
            actionRoutes.AddRange(routes);

        // get attribute routings from contrtoller interfaces
        var interfaces = 
           actionDescriptor.ControllerDescriptor.ControllerType
             .GetInterfaces();
        foreach (var contInterface in interfaces)
        {
            var interfaceDirectRoutes =  
                GetInterfaceDirectRoutes(
                    reflectedActionDescriptor.MethodInfo,
                    actionDescriptor.ControllerDescriptor.ControllerType, 
                    contInterface);
            if (interfaceDirectRoutes != null)
                actionRoutes.AddRange(interfaceDirectRoutes);
        }
        return actionRoutes;
    }

    /// Retrieves Direct routes from interface.
    private static IEnumerable<IDirectRouteFactory> GetInterfaceDirectRoutes(
        MethodInfo actionMethodInfo, Type classType, Type interfaceType)
    {
        InterfaceMapping map = classType.GetInterfaceMap(interfaceType);

        // locate matching interface method info and extract 
        // its DirectRoute custom attribute
        return map.InterfaceMethods
            .Where((t, i) => map.TargetMethods[i] == actionMethodInfo)
            .Select(t => 
               t.GetCustomAttributes(typeof(IDirectRouteFactory), true)
               .OfType<IDirectRouteFactory>())
            .FirstOrDefault();
    }
}

ActionDescriptor exposes its container Controller, and via controller type, all implementing interfaces are retrieved. Each interface is scavenged for the IDirectRouteFactory attribute related to the specified action method.
And eventually, all collected routes are returned to the WebApi routing mechanism. Those will be used to match URI request to contoller action.

Get Routing Customization Running

Once CustomDirectRouteProvider is ready, the WebApi needs to be configured to use it instead of the default direct routing. At WebApiConfig.cs, make the following change:

public static void Register(HttpConfiguration config)  
{
    // Web API routes

    // instead of:
    //config.MapHttpAttributeRoutes();

    // use:
    config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

    // other Web API configurationn
}

And now, ApiController can rely on routing attributes specified on the actual controller class,
its base controller or contract interface, as depicted here:

[RoutePrefix("api/values")]
public class ValuesController : BaseController, IMyController  
{
    // GET api/values/from_derieved/ids
    [Route("from_derieved/ids")]
    public IEnumerable<string> Get()
    {
        return new string[] {"Route got from derieved"};
    }

    // GET api/values/from_base/4/5
    public override string Get(int id1, int id2)
    {
        return "Route got from base controller";
    }

    // GET api/values/from_interface/5
    public string Get(int id)
    {
        return "Route got from interface";
    }
 }

public interface IMyController  
{
    [Route("from_interface/{id:int}")]
    string Get(int id);
}

public abstract class BaseController : ApiController  
{
    [Route("from_base/{id1:int}/{id2:int}")]
    public abstract string Get(int id1, int id2);
}

Exposing WebApi Service Contract

What's next? Strip service controllers from attribute routings and forward them to controller interfaces.
These interfaces can be now used as WebApi contracts and shared across your SOA architecture.

In order to consume Service contract, you can utilize AOP Interceptors such as Castle Windsor DynamicProxy to construct an HTTP request from the service address and to resolve the URI template from the Route attribute.
E.g., http://mywebsite/api/values/from_interface/5
More on this will be covered in future posts, so stay tuned to Como Engineering Blog!