I'm trying to wrap Web API controllers (IHttpController
implementations) with decorators, but when I do this, Web API throws an exception, because somehow it is expecting the actual implementation.
Applying decorators to controllers is a trick I successfully apply to MVC controllers and I obviously like to do the same in Web API.
I created a custom IHttpControllerActivator
that allows resolving decorated IHttpController
implementations. Here's a stripped implementation:
public class CrossCuttingConcernHttpControllerActivator : IHttpControllerActivator {
private readonly Container container;
public CrossCuttingConcernHttpControllerActivator(Container container) {
this.container = container;
}
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = (IHttpController)this.container.GetInstance(controllerType);
// Wrap the instance in one or multiple decorators. Note that in reality, the
// decorator is applied by the container, but that doesn't really matter here.
return new MyHttpControllerDecorator(controller);
}
}
My decorator looks like this:
public class MyHttpControllerDecorator : IHttpController {
private readonly IHttpController decoratee;
public MyHttpControllerDecorator(IHttpController decoratee) {
this.decoratee = decoratee;
}
public Task<HttpResponseMessage> ExecuteAsync(
HttpControllerContext controllerContext,
CancellationToken cancellationToken)
{
// this decorator does not add any logic. Just the minimal amount of code to
// reproduce the issue.
return this.decoratee.ExecuteAsync(controllerContext, cancellationToken);
}
}
However, when I run my application and request the ValuesController
, Web API throws me the following InvalidCastException
:
Unable to cast object of type 'WebApiTest.MyHttpControllerDecorator' to type 'WebApiTest.Controllers.ValuesController'.
Stacktrace:
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4()
at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
It's just as if Web API supplies us with the IHttpController
abstraction but skips it and still depends on the implementation itself. This would of course be a severe violation of the Dependency Inversion principle and make the abstraction utterly useless. So I'm probably doing something wrong instead.
What I'm I doing wrong? How can I happily decorate my API Controllers?
I would say, that the natural, designed way how to achieve this behaviour in ASP.NET Web API is with the Custom Message Handlers / Delegation Handlers
For example I do have this
DelegationHandler
in placeAnd this is how to inject that into the structure:
You can work around this by implementing
IHttpActionInvoker
and "converting" the decorator into the decorated instance at the point that theIHttpController
abstraction is no longer relevant.This is easily done by inheriting from
ApiControllerActionInvoker
.(I've hard coded the example and would expect any real world implementation to be more flexible.)
This was registered in
Global.asax.cs
Whether you'd actually want to do this is another matter - who knows the ramifications of altering
actionContext
?You can provide a custom implementation of
IHttpControllerSelector
to alter the type instantiated for a particular controller. (Please note I have not tested this to exhaustion)Update the decorator to be generic
Define the custom implementation of
IHttpControllerSelector
When registering the controllers, add in the registration of a decorated version of the controller type
Register the implementation of
IHttpControllerSelector
This is the method for creating the Decorated instance