Kiedy i gdzie ustawić niestandardowy IOperationInvoker?

Usiłuję rozszerzyć WCF tak, że mogę mieć usługę WWW RESTful, w której dla każdej operacji wykonuję weryfikację nagłówka autoryzacji HTTP, którego wartość używam do wywołania metody Login ().

Po zakończeniu logowania chcę wywołać odpowiednią metodę sprawdzania operacji, jeśli zgłoszony zostanie wyjątek bezpieczeństwa, w którym to przypadku odpowiem niestandardowym komunikatem „odmowa dostępu” przy użyciu odpowiedniego kodu stanu HTTP.

Mając to na uwadze, pomyślałem, że wdrożenie IEndpointBehavior, który implementuje IOperationInvoker do każdej operacji (ustawienie właściwości DispatchOperation.Invoker), byłoby dobrym pomysłem.

Postanowiłem zaimplementować IOperationInvoker przy użyciu wzorca projektu Decorator. Moja implementacja wymagałaby innego IOperationInvoker w swoim konstruktorze, do którego byłyby delegowane wywołania metody.

To jest moja 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;
        }
    }
}

Postanowiłem zaimplementować IEndpointBehavior, rozszerzając to, czego już potrzebowałem (WebHttpBehavior) w ten sposób używam tylko jednego beavioru. Oto kod, który napisałem:

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

Teraz jest problem:

W IOperationInvoker wywoływany jest tylko konstruktor, żadna z pozostałych metod nie jest.Dekoracja IOperationInvoker (ta, która została przekazana w konstruktorze dekoratora) tozero.

Zgaduję, że być może jakiś inny kod z jakiegoś innego zachowania ustawia później inny IOperationInvoker w ustawieniu OperationDispatcher.Invoker. Tak więc, nadpisując moją. To by jasno wyjaśniało moją sytuację.

Co się dzieje i co powinienem zrobić?

Moja usługa jest hostowana samodzielnie.

Jeśli potrzebujesz go zobaczyć, oto konfiguracja, którą mam w pliku app.config w systemie.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>

Czytałem tak daleko, jestem ci bardzo wdzięczny. Naprawdę dziękuję!

questionAnswers(2)

yourAnswerToTheQuestion