2009-07-20 14 views
9

Sto cercando di estendere WCF in modo che possa avere un servizio web RESTful, nel quale, per ogni operazione, eseguo una verifica dell'intestazione dell'autorizzazione HTTP, il cui valore utilizzo per chiamare un metodo Login().Quando e dove impostare un IOperationInvoker personalizzato?

Dopo il login è fatto, desidero richiamare il metodo corrispondente dell'operazione controllare se un'eccezione di protezione viene lanciata, nel qual caso vi risponderò con un costume "Accesso negato" messaggio" utilizzando il codice di stato HTTP appropriata.

Con questo in mente, ho pensato che l'attuazione di un IEndpointBehavior che applica un implementaion di IOperationInvoker per ogni operazione (impostando la proprietà DispatchOperation.Invoker) sarebbe una buona idea.

ho deciso di implementare un IOperationInvoker con il design Decorator La mia implementazione necessiterebbe di un altro IOperationInvoker nel suo costruttore a cui le chiamate di metodo sarebbero delegate .

Questo è il mio 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; 
     } 
    } 
} 

ho deciso di implementare un IEndpointBehavior estendendo il comportamento che già serviva (WebHttpBehavior) in questo modo io uso un solo beavior. Ecco il codice che ho scritto:

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

Ora qui è il problema:

  1. Solo il costruttore viene richiamato nel IOperationInvoker, nessuno degli altri metodi sono.
  2. Il decoratore IOperationInvoker (quello che è passato nel costruttore del decoratore) è null.

Sto indovinando che forse qualche altro codice da qualche altro comportamento sta creando un'altra IOperationInvoker nel OperationDispatcher.Invoker impostazione in seguito. Quindi, scavalcando il mio. Questo spiegherebbe chiaramente la mia situazione.

Cosa sta succedendo e cosa dovrei fare?

Il mio servizio è auto-ospitato.

Nel caso in cui sia necessario vederlo, ecco la configurazione che ho nel file app.config in 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> 

Ho letto fino a qui e sono profondamente grato a voi. Grazie davvero!

risposta

2

Sto costruendo qualcosa di simile (credo - non ho il tempo di esaminare tutto il codice), ma l'ho fatto in un modo diverso.

Per raggiungere questo sto usando la seguente:

  • Un IMessageInspector per leggere le intestazioni in arrivo richiesta HTTP messaggio (in questo caso l'estrazione di un ID di sessione da un cookie e recupero di un oggetto di sessione da una cache).
  • Una combinazione di un IPrincipal e una IAuthorizationPolicy per implementare il mio codice di autorizzazione personalizzato (WCF invocherà automaticamente il mio codice per le richieste ai metodi del servizio Web che hanno l'attributo '[PrincipalPermission (SecurityAction.Demand, Role = "somerole")] ' impostato).
  • Un IErrorHandler che rileva eventuali eccezioni non rilevate dai metodi del servizio Web (inclusa un'eccezione di autorizzazione negata generata se l'autorizzazione non riesce - ovvero il metodo IsRole implementato nell'IPrincipal restituisce false). Se si rileva l'eccezione di protezione negata, è possibile utilizzare WebOperationContext.Current per impostare i codici di errore HTTP personalizzati per il messaggio di risposta.
  • Un comportamento personalizzato (un IContractBehavior - ma è anche possibile utilizzare un comportamento di EndPoint o Servizio o qualsiasi altra cosa si desideri) che crea tutto quanto sopra in fase di runtime e li collega agli endpoint appropriati.
12

Invece di impostare invokers a ApplyDispatchBehavior() metodo, è necessario fare un implementor 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) 
    { 
    } 
} 

e poi al ApplyDispatchBehavior() è necessario impostare che il comportamento:

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

Questo la soluzione ha funzionato con me +1 –

Problemi correlati