何时何地设置自定义IOperationInvoker?

时间:2009-07-20 18:44:47

标签: wcf

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

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

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

我决定使用Decorator设计模式实现IOperationInvoker。我的实现需要在其构造函数中使用另一个IOperationInvoker,方法调用将委派给它。

这是我的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;
        }
    }
}

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

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.");
    }
}

现在问题在于:

  1. 在IOperationInvoker中只调用构造函数,其他方法都没有。
  2. decoratee IOperationInvoker(在装饰器的构造函数中传递的那个)是 null
  3. 我猜测其他一些行为中的其他代码可能是之后在OperationDispatcher.Invoker设置中设置了另一个IOperationInvoker。因此,压倒我的。这可以清楚地解释我的情况。

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

    我的服务是自托管的。

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

    <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>
    

    我读到这里,我非常感谢你。真的,谢谢!

3 个答案:

答案 0 :(得分:12)

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

 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)
  {
  }
 }

然后在ApplyDispatchBehavior()中,您应该设置该行为:

  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());
   }
  }

答案 1 :(得分:2)

我正在构建类似的东西(我认为 - 没有时间查看所有代码),但是以不同的方式解决了这个问题。

为实现这一点,我使用以下内容:

  • IMessageInspector,用于读取传入的HTTP请求消息标头(在本例中,从cookie中提取会话ID并从缓存中检索会话对象)。
  • IPrincipal和IAuthorizationPolicy的组合,用于实现我自己的自定义授权代码(WCF将自动调用我的代码,以获取对具有属性'[PrincipalPermission(SecurityAction.Demand,Role =“somerole”)]的Web服务方法的请求'set)。
  • IErrorHandler,它从Web服务方法中捕获任何未捕获的异常(包括在授权失败时抛出的权限被拒绝的异常 - 即您在IPrincipal中实现的IsRole方法返回false)。如果捕获到安全性被拒绝的异常,则可以使用WebOperationContext.Current为响应消息设置自定义HTTP错误代码。
  • 自定义行为(IContractBehavior - 但您也可以使用EndPoint或Service行为或任何您想要的行为),它在运行时创建上述所有内容并将它们附加到适当的端点。

答案 2 :(得分:1)

我知道这已经很老了,但对我来说Alexey's answer 工作了。但是,仅当ApplyDispatchBehaviour方法调用基本方法时。像这样:

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

        foreach (var operation in endpoint.Contract.Operations)
        {
            if (operation.Behaviors.Contains(typeof(AccessControlOperationBehaviour)))
                continue;
    
            operation.Behaviors.Add(new AccessControlOperationBehaviour());
        }
    }