Partager via


Dispatch par élément de corps

L’exemple AdvancedDispatchByBody montre comment implémenter un autre algorithme pour affecter des messages entrants aux opérations.

Par défaut, le répartiteur de modèle de service sélectionne la méthode de gestion appropriée pour un message entrant en fonction de l’en-tête WS-Addressing « Action » du message ou des informations équivalentes dans la requête SOAP HTTP.

Certaines piles de services web SOAP 1.1 qui ne suivent pas les instructions WS-I Basic Profile 1.1 ne distribuent pas de messages en fonction de l’URI d’action, mais plutôt en fonction du nom qualifié XML du premier élément à l’intérieur du corps SOAP. De même, le côté client de ces piles peut envoyer des messages avec un en-tête HTTP SoapAction vide ou arbitraire, qui a été autorisé par la spécification SOAP 1.1.

Pour modifier la façon dont les messages sont distribués à des méthodes, l’exemple implémente l’interface IDispatchOperationSelector d’extensibilité sur le DispatchByBodyElementOperationSelector. Cette classe sélectionne les opérations en fonction du premier élément du corps du message.

Le constructeur de classe attend un dictionnaire rempli avec les paires de XmlQualifiedName et les chaînes, dans lequel les noms qualifiés indiquent le nom du premier enfant du corps SOAP et les chaînes indiquent le nom d'opération correspondant. Il defaultOperationName s’agit du nom de l’opération qui reçoit tous les messages qui ne peuvent pas être mis en correspondance avec ce dictionnaire :

class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
    Dictionary<XmlQualifiedName, string> dispatchDictionary;
    string defaultOperationName;

    public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
    {
        this.dispatchDictionary = dispatchDictionary;
        this.defaultOperationName = defaultOperationName;
    }
}

IDispatchOperationSelector Les implémentations sont très simples à générer, car il n’existe qu’une seule méthode sur l’interface : SelectOperation. Le travail de cette méthode consiste à inspecter un message entrant et à retourner une chaîne qui correspond au nom d’une méthode sur le contrat de service pour le point de terminaison actuel.

Dans cet exemple, le sélecteur d’opération acquiert un XmlDictionaryReader pour le corps du message entrant en utilisant GetReaderAtBodyContents. Cette méthode place déjà le lecteur sur le premier enfant du corps du message afin qu’il soit suffisant d’obtenir le nom et l’URI d’espace de noms de l’élément actuel et de les combiner en un XmlQualifiedName qui est ensuite utilisé pour rechercher l’opération correspondante à partir du dictionnaire détenu par le sélecteur d’opération.

public string SelectOperation(ref System.ServiceModel.Channels.Message message)
{
    XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
    XmlQualifiedName lookupQName = new
       XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);
    message = CreateMessageCopy(message,bodyReader);
    if (dispatchDictionary.ContainsKey(lookupQName))
    {
         return dispatchDictionary[lookupQName];
    }
    else
    {
        return defaultOperationName;
    }
}

L’accès au corps du message avec GetReaderAtBodyContents ou à l’une des autres méthodes qui fournissent l’accès au contenu du corps du message entraîne la marque « lecture » du message, ce qui signifie que le message n’est pas valide pour un traitement ultérieur. Par conséquent, le sélecteur d’opération crée une copie du message entrant avec la méthode indiquée dans le code suivant. Étant donné que la position du lecteur n’a pas été modifiée pendant l’inspection, elle peut être référencée par le message nouvellement créé auquel les propriétés du message et les en-têtes de message sont également copiés, ce qui entraîne un clone exact du message d’origine :

private Message CreateMessageCopy(Message message,
                                     XmlDictionaryReader body)
{
    Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);
    copy.Headers.CopyHeaderFrom(message,0);
    copy.Properties.CopyProperties(message.Properties);
    return copy;
}

Ajout d’un sélecteur d’opérations à un service

Les sélecteurs d’opérations de répartition de service sont des extensions du répartiteur Windows Communication Foundation (WCF). Pour la sélection des méthodes sur le canal de rappel de contrats duplex, il existe également des sélecteurs d'opération clients qui fonctionnent de manière semblable aux sélecteurs d'opération de distribution décrits ici, mais ne sont pas traités de manière explicite dans cet exemple.

Comme la plupart des extensions de modèle de service, les sélecteurs d’opération de distribution sont ajoutés au répartiteur à l’aide de comportements. Un comportement est un objet de configuration, qui ajoute une ou plusieurs extensions au runtime de distribution (ou au runtime client) ou modifie ses paramètres.

Étant donné que les sélecteurs d’opérations ont une portée contractuelle, le comportement adéquat à mettre en œuvre ici est le IContractBehavior. Étant donné que l’interface est implémentée sur une Attribute classe dérivée, comme indiqué dans le code suivant, le comportement peut être ajouté de manière déclarative à n’importe quel contrat de service. Chaque fois qu'un ServiceHost est ouvert et que le runtime de dispatch est construit, tous les comportements trouvés soit comme attributs sur les contrats, les opérations et les implémentations de service, soit comme éléments dans la configuration du service, sont automatiquement ajoutés, puis sollicités pour contribuer à des extensions ou modifier la configuration par défaut.

Pour la concision, l’extrait de code suivant montre uniquement l’implémentation de la méthode ApplyDispatchBehavior, qui affecte les modifications de configuration pour le répartiteur dans cet exemple. Les autres méthodes ne sont pas indiquées car elles sont retournées à l'appelant sans effectuer aucun travail.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior
{
    // public void AddBindingParameters(...)
    // public void ApplyClientBehavior(...)
    // public void Validate(...)

En premier lieu, l'implémentation ApplyDispatchBehavior installe le dictionnaire de recherche pour le sélecteur d'opération en itérant sur les éléments OperationDescription dans la classe ContractDescription du point de terminaison de service. Ensuite, chaque description de l’opération est inspectée pour détecter la présence du DispatchBodyElementAttribute comportement, une implémentation de IOperationBehavior celle-ci est également définie dans cet exemple. Bien que cette classe soit également un comportement, elle est passive et ne contribue activement à aucune modification de configuration au runtime de distribution. Toutes ses méthodes sont retournées à l'appelant sans aucune action. Le comportement d'opération existe seulement afin que les métadonnées requises pour le nouveau mécanisme de distribution, à savoir le nom qualifié de l'élément de corps sur lequel une opération est sélectionnée, puissent être associées aux opérations respectives.

Si un tel comportement est trouvé, une paire valeur créée à partir du nom qualifié XML (QName propriété) et le nom de l’opération (Name propriété) est ajoutée au dictionnaire.

Une fois le dictionnaire rempli, un nouveau DispatchByBodyElementOperationSelector est construit avec ces informations et défini comme sélecteur d’opération du runtime de distribution :

public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
    Dictionary<XmlQualifiedName,string> dispatchDictionary =
                     new Dictionary<XmlQualifiedName,string>();
    foreach( OperationDescription operationDescription in
                              contractDescription.Operations )
    {
        DispatchBodyElementAttribute dispatchBodyElement =
   operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();
        if ( dispatchBodyElement != null )
        {
             dispatchDictionary.Add(dispatchBodyElement.QName,
                              operationDescription.Name);
        }
    }
    dispatchRuntime.OperationSelector =
            new DispatchByBodyElementOperationSelector(
               dispatchDictionary,
               dispatchRuntime.UnhandledDispatchOperation.Name);
    }
}

Implémentation du service

Le comportement implémenté dans cet exemple affecte directement la façon dont les messages du câble sont interprétés et distribués, qui est une fonction du contrat de service. Par conséquent, le comportement doit être déclaré au niveau du contrat de service dans toute implémentation de service qui choisit de l’utiliser.

Le service d'exemple de projet applique le comportement de contrat DispatchByBodyElementBehaviorAttribute au contrat de service IDispatchedByBody et marque chacune des deux opérations OperationForBodyA() et OperationForBodyB() avec un comportement d’opération DispatchBodyElementAttribute. Lorsqu’un hôte de service pour un service qui implémente ce contrat est ouvert, ces métadonnées sont récupérées par le générateur de répartiteur, comme décrit précédemment.

Étant donné que le sélecteur d’opération répartit uniquement en fonction de l’élément corps du message et ignore l'« Action », il est nécessaire d’indiquer au runtime de ne pas vérifier l’en-tête « Action » sur les réponses retournées en affectant le caractère générique « * » à la ReplyAction propriété de OperationContractAttribute. En outre, il est nécessaire d’avoir une opération par défaut dont la propriété « Action » est définie sur le caractère générique « * ». L’opération par défaut reçoit tous les messages qui ne peuvent pas être distribués et n’ont pas de DispatchBodyElementAttribute:

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),
                            DispatchByBodyElementBehavior]
public interface IDispatchedByBody
{
    [OperationContract(ReplyAction="*"),
     DispatchBodyElement("bodyA","http://tempuri.org")]
    Message OperationForBodyA(Message msg);
    [OperationContract(ReplyAction = "*"),
     DispatchBodyElement("bodyB", "http://tempuri.org")]
    Message OperationForBodyB(Message msg);
    [OperationContract(Action="*", ReplyAction="*")]
    Message DefaultOperation(Message msg);
}

L’exemple d’implémentation de service est simple. Chaque méthode encapsule le message reçu dans un message de réponse et l’renvoie au client.

Génération et exécution de l'exemple

Lorsque vous exécutez l'exemple, le contenu des réponses de l'opération s'affiche dans la fenêtre de la console cliente, similaire à la sortie (mise en forme) suivante.

Le client envoie trois messages au service dont l’élément de contenu du corps est nommé bodyA, bodyBet bodyX, respectivement. Comme on peut le déduire de la description précédente et du contrat de service indiqué, le message entrant avec l’élément bodyA est dirigé vers la méthode OperationForBodyA(). Étant donné qu’il n’existe aucune cible de répartition explicite pour le message avec l’élément bodyX body, le message est envoyé à l’objet DefaultOperation(). Chacune des opérations de service encapsule le corps du message reçu dans un élément spécifique à la méthode et la retourne, qui est effectuée pour mettre en corrélation clairement les messages d’entrée et de sortie pour cet exemple :

<?xml version="1.0" encoding="IBM437"?>
<replyBodyA xmlns="http://tempuri.org">
   <q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>
</replyBodyA>
<?xml version="1.0" encoding="IBM437"?>
<replyBodyB xmlns="http://tempuri.org">
  <q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>
</replyBodyB>
<?xml version="1.0" encoding="IBM437"?>
<replyDefault xmlns="http://tempuri.org">
   <q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>
</replyDefault>

Pour configurer, générer et exécuter l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer la solution, suivez les instructions indiquées dans la rubrique Génération des exemples Windows Communication Foundation.

  3. Pour exécuter l’exemple dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions figurant dans la rubrique Exécution des exemples Windows Communication Foundation.