Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebApi attribute routing defined on interface

I'm trying to convert a WCF/REST service project to MVC/WebAPI. The service layer is implemented multiple times as a wrapper for different end systems, and all implementations observe a common contract defined in an interface (IContract). With WCF, we defined [WebInvoke] and [OperationContract] attributes on each of the methods which were exposed as web service methods. In WebAPI, this can be simplified with attribute routing defined on the controller. However, I would like to keep the route attributes defined on the interface, so all the implementations behave similarly.

Here's a sample of the old interface:

[ServiceContract]
public interface IContract
{
    [WebInvoke(UriTemplate = "Version", Method = "GET", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
    [OperationContract]
    string GetVersion();
}

Here's what I was hoping to get working:

public interface IContract
{
    [Route("Version")]
    [HttpGet]
    string GetVersion();
}

I would also consider creating an abstract base class, but this other StackOverflow question makes me think there isn't a suitable replacement for [WebInvoke(UriTemplate)] using WebAPI attribute routing. Is that the case, or can someone point me to a similar, supported technique?

Thanks

like image 923
tbetts42 Avatar asked Mar 24 '14 15:03

tbetts42


1 Answers

After some more research into what is coming out from the ASP.NET team, it looks like the System.Web.Http.Routing.IDirectRouteProvider extensibility will hopefully meet our needs. As of this writing, this is only available using the ASP.NET nightly builds, but will hopefully reach RTM or at least CTP by the time we're ready to roll it out.

The following code shows how to implement this for my example above (original source: https://aspnetwebstack.codeplex.com/workitem/1464)

In Application_Start or WebApiConfig.Register() pass a new IDirectRouteProvider to the MapHttpAttributeRoutes() method.

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

Define the Route Provider as follows. This basically just turns on attribute inheritance so it will read routes from the base class (but not the interface).

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

In my case, we created an abstract BaseApiController that provides [Route] attributes to the WebAPI methods.

public abstract class BaseApiController : ApiController, IContract
{
    [Route("Version")]
    [HttpGet]
    public abstract string GetVersion();

}

Our end systems were formerly WCF REST service endpoints which implemented an IContract interface. For WebAPI, the controllers now derive from the BaseApiController. (We also had to change the name from ContractService to ContractServiceController.) We kept IContract around for backwards compatibility, but it's now implemented on BaseApiController. The [RoutePrefix] attribute is defined on the controller, which is our root URL in this case.

[RoutePrefix("")]
public class TestController : BaseApiController
{
    public override string GetVersion();
    {
        return "Version 1.0.0.0";
    }
}
like image 133
tbetts42 Avatar answered Oct 22 '22 15:10

tbetts42