Когда и где установить пользовательский IOperationInvoker? - PullRequest
10 голосов
/ 20 июля 2009

Я пытаюсь расширить WCF, чтобы иметь веб-службу RESTful, в которой для каждой операции я выполняю проверку заголовка HTTP-авторизации, значение которого я использую для вызова метода Login ().

После завершения входа в систему я хочу вызвать соответствующий метод операции, проверяющий, выдано ли исключение безопасности, и в этом случае я отвечу специальным сообщением «Отказано в доступе», используя соответствующий код состояния HTTP.

Учитывая это, я подумал, что было бы неплохо реализовать IEndpointBehavior, который применяет реализацию IOperationInvoker к каждой операции (устанавливая свойство DispatchOperation.Invoker).

Я решил реализовать IOperationInvoker, используя шаблон проектирования Decorator. Моей реализации понадобится другой 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;
        }
    }
}

Я решил реализовать IEndpointBehavior, расширив поведение, в котором я уже нуждался (WebHttpBehavior), так что я использую только один 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. IOperationInvoker (тот, который передается в конструктор декоратора) - null .

Я предполагаю, что, возможно, какой-то другой код из какого-то другого поведения устанавливает другой IOperationInvoker в настройке OperationDispatcher.Invoker впоследствии. Таким образом, переопределяя мою. Это ясно объяснило бы мою ситуацию.

Что происходит и что мне делать?

Мой сервис работает самостоятельно.

На тот случай, если вам нужно это увидеть, вот моя конфигурация в файле app.config в 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>

Я читал это далеко. Я глубоко благодарен вам. Действительно, спасибо!

Ответы [ 2 ]

12 голосов
/ 09 декабря 2010

Вместо установки вызывающих в методе ApplyDispatchBehavior (), вы должны сделать IOperationBehavior реализатор:

 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());
   }
  }
2 голосов
/ 22 июля 2009

Я создаю что-то похожее (я думаю - у меня нет времени, чтобы просмотреть весь ваш код), но пошло по-другому.

Для этого я использую следующее:

  • IMessageInspector для чтения заголовков входящих HTTP-запросов (в этом случае извлечение идентификатора сеанса из файла cookie и извлечение объекта сеанса из кэша).
  • Комбинация IPrincipal и IAuthorizationPolicy для реализации моего собственного пользовательского кода авторизации (WCF автоматически вызывает мой код для запросов к методам веб-службы, которые имеют атрибут '[PrincipalPermission (SecurityAction.Demand, Role = "somerole")] 'set).
  • IErrorHandler, который перехватывает любые неперехваченные исключения из методов веб-службы (в том числе исключение, в котором отказано в разрешении, выбрасывается при неудачной авторизации - т.е. метод IsRole, который вы реализуете в IPrincipal, возвращает false). Если вы поймете исключение безопасности отказано, вы можете использовать WebOperationContext.Current для установки пользовательских кодов ошибок HTTP для ответного сообщения.
  • Пользовательское поведение (IContractBehavior - но вы также можете использовать поведение EndPoint или Service или что угодно), которое создает все вышеперечисленное во время выполнения и присоединяет их к соответствующим конечным точкам.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...