Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
422 views
in Technique[技术] by (71.8m points)

wcf - When and where to set a custom IOperationInvoker?

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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...