何时何地设置自定义 IOperationInvoker? [英] When and where to set a custom IOperationInvoker?

查看:26
本文介绍了何时何地设置自定义 IOperationInvoker?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试扩展 WCF,以便我可以拥有一个 RESTful Web 服务,其中,对于每个操作,我都会对 HTTP 授权标头执行验证,我使用其值来调用 Login() 方法.

登录完成后,我希望调用操作的相应方法检查是否抛出安全异常,在这种情况下,我将使用适当的 HTTP 状态代码回复自定义访问被拒绝"消息.

考虑到这一点,我认为实现一个 IEndpointBehavior 将 IOperationInvoker 的实现应用于每个操作(设置 DispatchOperation.Invoker 属性)将是一个好主意.

我决定使用装饰器设计模式实现 IOperationInvoker.我的实现需要另一个 IOperationInvoker 在它的构造函数中,方法调用将委托给它.

这是我的 IOperationInvokerImplementation:

 公共类 BookSmarTkOperationInvoker : IOperationInvoker{私有只读 IOperationInvoker 调用程序;public BookSmarTkOperationInvoker(IOOperationInvokerdecoratee){this.invoker = 装饰者;}公共对象[]分配输入(){返回 this.invoker.AllocateInputs();}公共对象调用(对象实例,对象 [] 输入,输出对象 [] 输出){操作前();//哪里有使用 WebOperationContext.Current 执行登录的代码对象 o = 空;尝试{o = this.invoker.Invoke(实例,输入,输出);}捕获(异常异常){输出 = 空;返回 AfterFailedOperation(异常);//返回自定义访问被拒绝响应}返回o;}公共 IAsyncResult InvokeBegin(对象实例,对象 [] 输入,AsyncCallback 回调,对象状态){throw new Exception("操作调用者不是异步的.");}公共对象 InvokeEnd(对象实例,输出对象 [] 输出,IAsyncResult 结果){throw new Exception("操作调用者不是异步的.");}public bool IsSynchronous{得到{返回假;}}}

我决定通过扩展我已经需要的行为 (WebHttpBehavior) 来实现 IEndpointBehavior,这样我只使用一个 beavior.这是我写的代码:

公共类 BookSmarTkEndpointBehavior : WebHttpBehavior{公共覆盖无效验证(ServiceEndpoint 端点){base.Validate(端点);}公共覆盖无效 AddBindingParameters(ServiceEndpoint 端点,BindingParameterCollection bindingParameters){base.AddBindingParameters(endpoint, bindingParameters);}public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){base.ApplyDispatchBehavior(endpoint, endpointDispatcher);foreach(endpointDispatcher.DispatchRuntime.Operations 中的 DispatchOperation 操作){IOperationInvoker defaultInvoker = operation.Invoker;IOperationInvoker decoratorInvoker = new BookSmarTkOperationInvoker(defaultInvoker);operation.Invoker = decoratorInvoker;Console.Write("之前:" + ((object)defaultInvoker ?? "null"));Console.WriteLine(" After: " + operation.Invoker);}}public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime){base.ApplyClientBehavior(endpoint, clientRuntime);throw new Exception("无法在客户端端点中使用 BookSmarTkEndointBehavior.");}}

问题来了:

  1. 在 IOperationInvoker 中仅调用构造函数,其他方法均未调用.
  2. 被装饰者 IOperationInvoker(在装饰者的构造函数中传递的那个)是 null.

我猜可能来自其他行为的其他一些代码在之后的 OperationDispatcher.Invoker 设置中设置了另一个 IOperationInvoker.因此,覆盖我的.这将清楚地解释我的情况.

发生了什么,我该怎么办?

我的服务是自托管的.

如果您需要查看,这里是我在 system.serviceModel 下的 app.config 文件中的配置.

<服务名称="BookSmarTk.Web.Service.BookSmarTkService"><主机><基地址><add baseAddress="http://localhost:8080/service"/></baseAddresses></host><端点地址=""行为配置=BookSmaTkEndpointBehavior"绑定=webHttpBinding"bindingConfiguration="BookSmarTkBinding"合同="BookSmarTk.Web.Service.BookSmarTkService"></端点></服务></服务><行为><服务行为><行为名称="BookSmartkServiceBehavior"><serviceDebug httpHelpPageEnabled="true" httpHelpPageUrl="/help.htm" includeExceptionDetailInFaults="true"/></行为></serviceBehaviors><端点行为><行为名称="BookSmaTkEndpointBehavior"><!--<webHttp/>--><bookSmarTkEndpointBehavior/></行为></endpointBehaviors></行为><绑定><webHttpBinding><绑定名称="BookSmarTkBinding"></binding></webHttpBinding></绑定><扩展><行为扩展><add name="bookSmarTkEndpointBehavior" type="BookSmarTk.Web.Service.BookSmarTkEndpointBehaviorElement, BookSmarTk.Web.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/></behaviorExtensions></扩展名>

你读了这么多,我对你深表感谢.真的,谢谢!

解决方案

不是在 ApplyDispatchBehavior() 方法中设置调用者,你必须创建一个 IOperationBehavior 实现者:

 公共类 MyOperationBehavior: IOperationBehavior{public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters){}public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation){}public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation){dispatchOperation.Invoker = new BookSmarTkOperationInvoker(dispatchOperation.Invoker);}公共无效验证(OperationDescription operationDescription){}}

然后在 ApplyDispatchBehavior() 处设置该行为:

 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher){foreach(端点.Contract.Operations中的var操作){if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))继续;operation.Behaviors.Add(new MyOperationBehavior());}}

I'm trying to extend WCF so that I can have a RESTful web service, in which, for each operation, I perform a verification of the HTTP Authorization header, whose value I use to call a Login() method.

After the login is done, I wish to invoke the operation's corresponding method checking if a security exception is thrown, in which case I'll reply with a custom "access denied" message" using the appropriate HTTP Status Code.

With this in mind, I thought implementing a IEndpointBehavior that applies an implementaion of IOperationInvoker to each operation (setting the DispatchOperation.Invoker property) would be a good idea.

I decided to implement an IOperationInvoker using the Decorator design pattern. My implementation would need another IOperationInvoker in it's constructor to which the method invocations would be delegated.

This is my IOperationInvokerImplementation:

    public class BookSmarTkOperationInvoker : IOperationInvoker{

    private readonly IOperationInvoker invoker;

    public BookSmarTkOperationInvoker(IOperationInvoker decoratee)
    {
        this.invoker = decoratee;
    }

    public object[] AllocateInputs()
    {
        return this.invoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        BeforeOperation(); // Where there's code to perform the login using WebOperationContext.Current
        object o = null;
        try
        {
            o = this.invoker.Invoke(instance, inputs, out outputs);
        }
        catch (Exception exception)
        {
            outputs = null;
            return AfterFailedOperation(exception); // Return a custom access denied response
        }

        return o;
    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        throw new Exception("The operation invoker is not asynchronous.");
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        throw new Exception("The operation invoker is not asynchronous.");
    }

    public bool IsSynchronous
    {
        get
        {
            return false;
        }
    }
}

I decided to implement an IEndpointBehavior by extending the behavior I already needed (WebHttpBehavior) this way I only use one beavior. Here's the code I wrote:

public class BookSmarTkEndpointBehavior : WebHttpBehavior
{
    public override void Validate(ServiceEndpoint endpoint)
    {
        base.Validate(endpoint);
    }

    public override void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        base.AddBindingParameters(endpoint, bindingParameters);
    }

    public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        base.ApplyDispatchBehavior(endpoint, endpointDispatcher);

        foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
        {
            IOperationInvoker defaultInvoker = operation.Invoker;
            IOperationInvoker decoratorInvoker = new BookSmarTkOperationInvoker(defaultInvoker);
            operation.Invoker = decoratorInvoker;

            Console.Write("Before: " + ((object)defaultInvoker ?? "null"));
            Console.WriteLine(" After: " + operation.Invoker);
        }
    }

    public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        base.ApplyClientBehavior(endpoint, clientRuntime);
        throw new Exception("The BookSmarTkEndointBehavior cannot be used in client endpoints.");
    }
}

Now here's the problem:

  1. Only the constructor is being invoked in the IOperationInvoker, none of the other methods are.
  2. The decoratee IOperationInvoker (the one that's passed in the decorator's constructor) is null.

I'm guessing that maybe some other code from some other behavior is setting another IOperationInvoker in the OperationDispatcher.Invoker setting afterwards. Thus, overriding mine. This would clearly explain my situation.

What is happening and what should I do?

My service is self-hosted.

In case you need to see it, here is the configuration I have in the app.config file under system.serviceModel.

<services>
  <service name="BookSmarTk.Web.Service.BookSmarTkService">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8080/service"/>
      </baseAddresses>
    </host>
    <endpoint  
      address=""
      behaviorConfiguration="BookSmaTkEndpointBehavior"
      binding="webHttpBinding" 
      bindingConfiguration="BookSmarTkBinding"
      contract="BookSmarTk.Web.Service.BookSmarTkService">
    </endpoint>
  </service>
</services>

<behaviors>
  <serviceBehaviors>
    <behavior name ="BookSmartkServiceBehavior">
      <serviceDebug httpHelpPageEnabled="true" httpHelpPageUrl="/help.htm" includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="BookSmaTkEndpointBehavior">
      <!--<webHttp/>-->
      <bookSmarTkEndpointBehavior />
    </behavior>
  </endpointBehaviors>
</behaviors>

<bindings>
  <webHttpBinding>
    <binding name="BookSmarTkBinding">
    </binding>
  </webHttpBinding>
</bindings>

<extensions>
  <behaviorExtensions>
    <add name="bookSmarTkEndpointBehavior" type="BookSmarTk.Web.Service.BookSmarTkEndpointBehaviorElement, BookSmarTk.Web.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </behaviorExtensions>
</extensions>

I you read this far I am deeply grateful towards you. Really, Thank You!

解决方案

Instead of setting invokers at ApplyDispatchBehavior() method, you have to make an IOperationBehavior implementor:

 public class MyOperationBehavior: IOperationBehavior
 {
  public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
  {
  }

  public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
  {
  }

  public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
  {
   dispatchOperation.Invoker = new BookSmarTkOperationInvoker(dispatchOperation.Invoker);
  }

  public void Validate(OperationDescription operationDescription)
  {
  }
 }

and then at ApplyDispatchBehavior() you should set that behavior:

  public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
  {
    foreach (var operation in endpoint.Contract.Operations) {
      if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))
       continue;

      operation.Behaviors.Add(new MyOperationBehavior());
   }
  }

这篇关于何时何地设置自定义 IOperationInvoker?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆