我正在尝试使用装饰器来包装Web API控制器(IHttpController实现),但是当我这样做时,Web API会抛出异常,因为不知何故它会期待实际的实现.
将装饰器应用于控制器是我成功应用于MVC控制器的一个技巧,我显然希望在Web API中也这样做.
我创建了一个自定义的IHttpControllerActivator,它允许解析装饰的IHttpController实现.这是一个剥离的实现:
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); } }
我的装饰师看起来像这样:
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); } }
但是,当我运行我的应用程序并请求ValuesController时,Web API会抛出以下InvalidCastException:
Unable to cast object of type ‘WebApiTest.MyHttpControllerDecorator’
to type ‘WebApiTest.Controllers.ValuesController’.
堆栈跟踪:
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)
这就好像Web API为我们提供了IHttpController抽象,但跳过了它仍然依赖于实现本身.这当然会严重违反依赖性倒置原则,并使抽象完全无用.所以我可能会做错事.
我做错了什么?我怎样才能愉快地装饰我的API控制器?
解决方法
我会说,如何在ASP.NET Web API中实现此行为的自然设计方式是使用
Custom Message Handlers / Delegation Handlers
例如,我确实有这个DelegationHandler
public class AuthenticationDelegationHandler : DelegatingHandler { protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,CancellationToken cancellationToken) { // I. do some stuff to create Custom Principal // e.g. var principal = CreatePrincipal(); ... // II. return execution to the framework return base.SendAsync(request,cancellationToken).ContinueWith(t => { HttpResponseMessage resp = t.Result; // III. do some stuff once finished // e.g.: // SetHeaders(resp,principal); return resp; }); }
这就是如何将其注入结构:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MessageHandlers.Add(new AuthenticationDelegationHandler());