web-dev-qa-db-fra.com

Comment utiliser IDispatchMessageInspector dans un service WCF?

J'essaie d'utiliser IDispatchMessageInspector dans une implémentation de service WCF pour accéder aux valeurs d'en-tête personnalisées.

Quelque chose comme:

public class MyService : IMyService
{
    public List<string> GetNames()
    {
        var headerInspector = new CustomHeaderInspector();

        // Where do request & client channel come from?
        var values = headerInspector.AfterReceiveRequest(ref request, clientChannel, OperationContext.Current.InstanceContext);            
    }
}

J'ai implémenté ma propre classe IDispatchMessageInspector.

public class CustomHeaderInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        var userName = prop.Headers["Username"];

        return userName;
    }
}

Comment je passe

  • System.ServiceModel.Channels. Message et

  • System.ServiceModel. IClientChannel

to AfterReceiveRequest appelé à partir de l'implémentation du service?

EDIT:

De nombreux articles tels que celui-ci ou celui-ci donnent des exemples sur la façon de mettre en œuvre votre propre variable ServiceBehavior. Donc, votre implémentation de service ressemble à ceci:

[MyCustomBehavior]
public class MyService : IMyService
{
    public List<string> GetNames()
    {
        // Can you use 'MyCustomBehavior' here to access the header properties?
    }
}

Donc, avec cela, puis-je accéder à MyCustomBehavior en quelque sorte dans la méthode d'opération de service pour accéder à des valeurs d'en-tête personnalisées?

12
Didaxis

Vous devez configurer le 

<extensions>
  <behaviorExtensions>
    <add 
      name="serviceInterceptors" 
      type="CustomHeaderInspector , MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
    />
  </behaviorExtensions>
</extensions>

Ensuite, l'extension sera traitée dans votre pile WCF. Le service lui-même n'a aucune notion de la serviceInterceptors et vous n'avez pas à faire quelque chose comme dans votre premier bloc de code. La pile WCF vous injectera Inspector.

MSDN: system.servicemodel.dispatcher.idispatchmessageinspector

6
Peter

J'utilise IClientMessageInspector pour le même objectif. Voici comment vous pouvez les appliquer à partir de code:

 var serviceClient = new ServiceClientClass(binding, endpointAddress);
serviceClient.Endpoint.Behaviors.Add(
            new MessageInspectorEndpointBehavior<YourMessageInspectorType>());


/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class MessageInspectorEndpointBehavior<T> : IEndpointBehavior
    where T: IClientMessageInspector, new()
{
    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new T());
    }

    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    public void Validate(ServiceEndpoint endpoint)
    {
        // Nothing special here
    }
}

Et voici un exemple d'implémentation de MessageInspector que j'utilise pour transmettre la version du client au serveur et récupérer la version du serveur dans des en-têtes personnalisés:

/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class VersionCheckMessageInspector : IClientMessageInspector
{
    /// <summary>
    /// Enables inspection or modification of a message before a request message is sent to a service.
    /// </summary>
    /// <param name="request">The message to be sent to the service.</param>
    /// <param name="channel">The WCF client object channel.</param>
    /// <returns>
    /// The object that is returned as the <paramref name="correlationState " /> argument of
    /// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message@,System.Object)" /> method.
    /// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
    /// <paramref name="correlationState" /> objects are the same.
    /// </returns>
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        request.Headers.Add(new VersionMessageHeader());
        return null;
    }

    /// <summary>
    /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
    /// </summary>
    /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
    /// <param name="correlationState">Correlation state data.</param>
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        var serverVersion = string.Empty;
        var idx = reply.Headers.FindHeader(VersionMessageHeader.HeaderName, VersionMessageHeader.HeaderNamespace);
        if (idx >= 0)
        {
            var versionReader = reply.Headers.GetReaderAtHeader(idx);
            while (versionReader.Name != "ServerVersion"
                   && versionReader.Read())
            {
                serverVersion = versionReader.ReadInnerXml();
                break;
            }
        }

        ValidateServerVersion(serverVersion);
    }

    private static void ValidateServerVersion(string serverVersion)
    {
        // TODO...
    }
}

public class VersionMessageHeader : MessageHeader
{
    public const string HeaderName = "VersionSoapHeader";
    public const string HeaderNamespace = "<your namespace>";
    private const string VersionElementName = "ClientVersion";

    public override string Name
    {
        get { return HeaderName; }
    }

    public override string Namespace
    {
        get { return HeaderNamespace; }
    }

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteElementString(
            VersionElementName,
            Assembly.GetExecutingAssembly().GetName().Version.ToString());
    }
}
5
Woodman

Ce que j'ai fait pour accéder aux détails, j'ai défini les éléments suivants dans IDispatchMessageInspector.AfterReceiveRequest

Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username, "Membership Provider"), roles);

J'ai omis le code d'authentification de cela.

Pour accéder à la valeur à partir de la méthode de service, vous pouvez appeler

Thread.CurrentPrincipal.Identity.Name

1
3dd

Je crois que vous n'avez pas besoin d'implémenter IDispatchMessageInspector personnalisé pour récupérer des en-têtes personnalisés, vous pouvez le faire à partir d'une méthode d'opération de service comme celle-ci:

var mp = OperationContext.Current.IncomingMessageProperties;
var property = (HttpRequestMessageProperty)mp[HttpRequestMessageProperty.Name];
var userName = property.Headers["Username"];

Il est logique d'implémenter l'inspecteur de messages de répartition personnalisé si vous souhaitez interrompre le traitement du message, par exemple si les informations d'identification sont manquantes - vous pouvez simplement lancer FaultException dans ce cas.

Mais si vous souhaitez toujours transmettre la valeur de l'inspecteur de messages de répartition à la méthode d'opération de service, elle peut probablement être transmise via un singleton avec l'identificateur d'appel (ID de session), pour être extraite ultérieurement par la méthode ou à l'aide de wcf extensions

1
Alexander

Sur la page MSDN à laquelle vous avez accédé, vous trouverez également une description de l'insertion d'un inspecteur, ainsi qu'un exemple. Citer:

En règle générale, les inspecteurs de message sont insérés par un comportement de service, un comportement de noeud final ou un comportement de contrat. Le comportement ajoute ensuite l'inspecteur de message à la collection DispatchRuntime.MessageInspectors.

Plus tard, vous avez les exemples suivants:

  • Implémentation d'IDispatchMessageInspector personnalisé
  • Implémentation d'IServiceBehavior personnalisé qui ajoute l'inspecteur à l'exécution.
  • Configuration du comportement via un fichier .config.

Cela devrait être suffisant pour vous aider à démarrer. Sinon n'hésitez pas à demander :)

Si vous souhaitez simplement accéder aux en-têtes à partir de votre service, vous pouvez essayer OperationContext.Current.IncomingMessageHeaders.

0
Patko