175 votes

Comment ajouter un en-tête HTTP personnalisé à chaque appel WCF ?

J'ai un service WCF qui est hébergé dans un service Windows. Les clients qui utilisent ce service doivent passer un identifiant à chaque fois qu'ils appellent les méthodes du service (parce que cet identifiant est important pour ce que la méthode appelée doit faire). J'ai pensé que c'est une bonne idée de mettre d'une manière ou d'une autre cet identifiant dans les informations de l'en-tête WCF.

Si c'est une bonne idée, comment puis-je ajouter automatiquement l'identifiant aux informations de l'en-tête. En d'autres termes, chaque fois que l'utilisateur appelle la méthode WCF, l'identifiant doit être automatiquement ajouté à l'en-tête.

UPDATE : Les clients qui utilisent le service WCF sont à la fois des applications Windows et des applications Windows Mobile (utilisant le Compact Framework).

0 votes

Avez-vous réussi à le faire fonctionner sur le Compact Framework ?

1 votes

200voto

Mark Good Points 1956

L'avantage de cette méthode est qu'elle est appliquée à chaque appel.

Créez une classe qui implémente IClientMessageInspector . Dans la méthode BeforeSendRequest, ajoutez votre en-tête personnalisé au message sortant. Cela pourrait ressembler à quelque chose comme ceci :

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
    HttpRequestMessageProperty httpRequestMessage;
    object httpRequestMessageObject;
    if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
    {
        httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
        if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
        {
            httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
        }
    }
    else
    {
        httpRequestMessage = new HttpRequestMessageProperty();
        httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
        request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
    }
    return null;
}

Créez ensuite un comportement de point de terminaison qui applique l'inspecteur de messages au runtime du client. Vous pouvez appliquer le comportement via un attribut ou via la configuration en utilisant un élément d'extension de comportement.

Voici un grand exemple de comment ajouter un en-tête HTTP user-agent à tous les messages de requête. Je l'utilise dans quelques-uns de mes clients. Vous pouvez également faire la même chose du côté du service en implémentant la fonction IDispatchMessageInspector .

C'est ce que vous aviez à l'esprit ?

Mise à jour : J'ai trouvé ceci liste des fonctionnalités WCF qui sont supportées par le cadre compact. Je crois que les inspecteurs de messages sont classés comme 'Channel Extensibility' qui, selon ce post, sont soutenu par le cadre compact.

0 votes

Merci. C'est exactement ce que je voulais, mais est-ce que cela fonctionne dans un cadre compact ?

0 votes

Mohammadreza, je ne suis pas sûr. La liste que j'ai trouvée suggérait qu'ils pourraient l'être, mais je n'ai pas pu le confirmer.

2 votes

@Mark, C'est une très bonne réponse. Merci. J'ai essayé avec net.tcp mais j'utilise directement la collection Headers (les Headers Http n'ont pas fonctionné). J'obtiens un Header avec mon jeton (Name) dans l'événement ServiceHost AfterReceiveRequest, mais pas la valeur (il ne semble même pas y avoir de propriété pour une valeur). Y a-t-il quelque chose qui m'échappe ? Je me serais attendu à une paire nom/valeur car lorsque je crée l'en-tête, il me demande : request.Headers.Add(MessageHeader.CreateHeader(name, ns, value)) ;

81voto

AgileJon Points 20497

Vous l'ajoutez à l'appel en utilisant :

using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
    MessageHeader<string> header = new MessageHeader<string>("secret message");
    var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
    OperationContext.Current.OutgoingMessageHeaders.Add(untyped);

    // now make the WCF call within this using block
}

Et ensuite, côté serveur, vous l'attrapez en utilisant :

MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
string identity = headers.GetHeader<string>("Identity", "http://www.my-website.com");

5 votes

Merci pour votre exemple de code. Mais avec cela, je dois ajouter l'en-tête chaque fois que je veux appeler une méthode. Je voulais que ce processus soit transparent. Je veux dire qu'avec une seule implémentation, chaque fois que l'utilisateur crée un client de service et utilise une méthode, l'en-tête du client est automatiquement ajouté au message.

0 votes

Voici un bon lien MSDN avec un exemple pour développer la suggestion fournie dans cette réponse : msdn.microsoft.com/fr/us/library/

1 votes

Merci, c'est un excellent morceau de code si vous utilisez une bibliothèque client personnalisée. De cette façon, vous n'avez pas besoin d'implémenter le messageinspector. Il suffit de créer une méthode wrapper commune qui englobe chaque appel client dans l'OperationContextScope.

31voto

Nimesh Madhavan Points 2273

Si vous souhaitez simplement ajouter le même en-tête à toutes les demandes adressées au service, vous pouvez le faire sans aucun codage !
Ajoutez simplement le nœud headers avec les en-têtes requis sous le nœud endpoint dans votre fichier de configuration client.

<client>  
  <endpoint address="http://localhost/..." >  
    <headers>  
      <HeaderName>Value</HeaderName>  
    </headers>   
 </endpoint>

22 votes

Il s'agit d'en-têtes SOAP ( ala MessageHeader ) - pas les en-têtes HTTP.

25voto

SliverNinja Points 15924

Voici une autre solution utile pour ajouter manuellement des en-têtes HTTP personnalisés à la demande WCF de votre client en utilisant la méthode de l'en-tête. ChannelFactory en tant que mandataire. Il faudrait le faire pour chaque demande, mais cela suffit comme démo simple si vous avez juste besoin de tester l'unité de votre proxy en préparation des plateformes non-.NET.

// create channel factory / proxy ...
using (OperationContextScope scope = new OperationContextScope(proxy))
{
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
    {
        Headers = 
        { 
            { "MyCustomHeader", Environment.UserName },
            { HttpRequestHeader.UserAgent, "My Custom Agent"}
        }
    };    
    // perform proxy operations... 
}

3 votes

J'ai essayé 4 autres suggestions similaires et c'est la seule qui a fonctionné pour moi.

0 votes

Cela ajoute effectivement les en-têtes HTTP, merci ! :) Mais bon sang, c'est un code très laid.

2voto

Paulo Santos Points 8148

Si je comprends bien votre demande, la réponse est simple : vous ne pouvez pas.

C'est parce que le client du service WCF peut être généré par n'importe quel tiers qui utilise votre service.

SI vous avez le contrôle des clients de votre service, vous pouvez créer une classe client de base qui ajoute l'en-tête désiré et hérite du comportement des classes de travail.

1 votes

Convenu, si vous construisez vraiment une SOA, vous ne pouvez pas supposer que tous les clients sont basés sur .NET. Attendez que votre entreprise soit rachetée.

2 votes

Est-ce vraiment vrai ? Les clients de services web Java n'ont pas la possibilité d'ajouter des noms/valeurs aux en-têtes SOAP ? Je trouve cela difficile à croire. Bien sûr, ce serait une implémentation différente, mais c'est une solution interopérable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X