何时何地设置自定义IOperationInvoker?

10 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我决定通过扩展我已经需要的行为(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.");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在问题在于:

  1. 只有在IOperationInvoker中调用构造函数,其他方法都没有.
  2. decoratee IOperationInvoker(在装饰器的构造函数中传递的那个)为null.

我猜测,其他一些行为的其他代码可能是之后在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>
Run Code Online (Sandbox Code Playgroud)

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

Ale*_*y F 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)
  {
  }
 }
Run Code Online (Sandbox Code Playgroud)

然后在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());
   }
  }
Run Code Online (Sandbox Code Playgroud)


小智 2

我正在构建类似的东西(我认为 - 没有时间查看所有代码),但以不同的方式进行了处理。

为了实现这一目标,我使用以下方法:

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