Partager via


WSDL et contrats de service

L’utilitaire Wsutil.exe génère un stub en langage C en fonction des métadonnées WSDL fournies, ainsi que des définitions et descriptions des types de données décrits par les schémas XML créés par l’utilisateur.

Voici un exemple de document WSDL et de schéma XML qui sert de base à la discussion suivante :

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:types>
  <xs:schema xmlns:tns="http://Example.org" elementFormDefault="qualified" 
  targetNamespace="http://Example.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="SimpleMethod">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="a" type="xs:int" />
      <xs:element name="b" type="xs:int" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
   <xs:element name="SimpleMethodResponse">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="b" type="xs:int" />
      <xs:element name="c" type="xs:int" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
  </xs:schema>
 </wsdl:types>
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   <wsdl:input wsaw:Action="http://Example.org/ISimpleService/SimpleMethod" 
   message="tns:ISimpleService_SimpleMethod_InputMessage" />
   <wsdl:output wsaw:Action="http://Example.org/ISimpleService/SimpleMethodResponse" 
   message="tns:ISimpleService_SimpleMethod_OutputMessage" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="DefaultBinding_ISimpleService" type="tns:ISimpleService">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="SimpleMethod">
   <soap:operation soapAction="http://Example.org/ISimpleService/SimpleMethod" 
   style="document" />
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="SimpleService">
  <wsdl:port name="ISimpleService" binding="tns:DefaultBinding_ISimpleService">
   <soap:address location="http://Example.org/ISimpleService" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

Cet exemple fournit un contrat, ISimpleService, avec une seule méthode, SimpleMethod. « SimpleMethod » a deux paramètres d’entrée de type entier, a et b, qui sont envoyés du client au service. De même, SimpleMethod a deux paramètres de sortie de type entier, b et c, qui sont renvoyés au client après la réussite de l’exécution. Dans la syntaxe C annotée en SAL, la définition de méthode apparaît de la manière suivante :

void SimpleMethod(__in int a, __inout int *  b, __out int * c );

Dans cette définition, ISimpleService est un contrat de service avec une seule opération de service : SimpleMethod.

Le fichier d’en-tête de sortie contient des définitions et des descriptions pour référence externe. Cela inclut :

  • Définitions de structure C pour les types d’éléments globaux.
  • Prototype d’opération défini dans le fichier actuel.
  • Prototype de table de fonctions pour les contrats spécifiés dans le fichier WSDL.
  • Prototypes de proxy client et de stub de service pour toutes les fonctions spécifiées dans le fichier actuel.
  • Structure de données WS_ELEMENT_DESCRIPTION pour les éléments de schéma globaux définis dans le fichier actuel.
  • Structure de données WS_MESSAGE_DESCRIPTION pour tous les messages spécifiés dans le fichier actuel.
  • Structure de données WS_CONTRACT_DESCRIPTION pour tous les contrats spécifiés dans le fichier actuel.

Une structure globale est générée pour encapsuler toutes les descriptions globales des types de schéma et des types de modèle de service que l’application peut référencer. La structure est nommée en utilisant un nom de fichier normalisé. Dans cet exemple, Wsutil.exe génère une structure de définitions globale nommée « example_wsdl » qui contient toutes les descriptions de service web. La définition de structure est générée dans le fichier stub.

typedef struct _example_wsdl
{
  struct {
    WS_ELEMENT_DESCRIPTION SimpleMethod;
    WS_ELEMENT_DESCRIPTION SimpleMethodResponse;
  } elements;
  struct {
    WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_InputMessage;
    WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_OutputMessage;
  } messages;
  struct {
    WS_CONTRACT_DESCRIPTION DefaultBinding_ISimpleService;
  } contracts;
} _example_wsdl;

extern const _stockquote_wsdl stockquote_wsdl;

Pour les définitions d’élément globales dans le document de schéma XML (XSD), un prototype WS_ELEMENT_DESCRIPTION , ainsi que la définition de type C correspondante sont générés pour chacun des éléments. Les prototypes des descriptions d’élément pour SimpleMethod et SimpleMethodResponse sont générés comme des membres dans la structure ci-dessus. Les structures C sont générées de la façon suivante :

typedef struct SimpleMethod
{
  int   a;
  int   b;
} SimpleMethod;

typedef struct SimpleMethodResponse
{
  int   b;
  int   c;
} SimpleMethodResponse;

De même pour les types complexes globaux, Wsutil.exe génère des définitions de structure de type C comme ci-dessus, sans descriptions d’élément correspondantes.

Pour l’entrée WSDL, Wsutil.exe génère les prototypes et définitions suivants :

  • Un prototype WS_MESSAGE_DESCRIPTION est généré pour la description du message. Cette description peut être utilisée par le modèle de service ainsi que par la couche de message. Les structures de description de message sont des champs nommés « messagename » dans la structure globale. Dans cet exemple, la description du message est générée comme champ ISimpleService_SimpleMethod_InputMessage dans la structure ISimpleService_SimpleMethod_InputMessage, comme spécifié dans le fichier WSDL.
  • Le prototype WS_CONTRACT_DESCRIPTION est généré pour la description du contrat. Cette description est utilisée par le modèle de service. Les structures de description de contrat sont des champs nommés « contractname » dans la structure globale. Dans cet exemple, la description du contrat est générée comme champ DefaultBinding_ISimpleService dans la structure « _example_wsdl ».

Les spécifications d’opération et de type sont communes au proxy et au stub, et sont générées dans les deux fichiers. Wsutil.exe génère une copie uniquement si le proxy et le stub sont générés dans le même fichier.

Génération d’identificateur

Les structures C générées automatiquement listées ci-dessus sont créées en fonction du nom spécifié dans le fichier WSDL. Un NCname XML n’est généralement pas considéré comme un identificateur C valide et les noms sont normalisés selon les besoins. Les valeurs hexadécimales ne sont pas converties, et les lettres courantes comme « : », « / » et « . » sont converties en trait de soulignement « _ » pour améliorer la lisibilité.

En-tête du stub

Pour chaque opération du contrat de service, une routine de rappel nommée « <operationname>Callback » est générée. (Par exemple, l’opération « SimpleMethod » dans l’exemple de contrat de service a un rappel généré nommé « SimpleMethodCallback ».)

typedef HRESULT (CALLBACK *SimpleMethodCallback) (
  const WS_OPERATION_CONTEXT * context,
  int a, int *b, int *c,
  const WS_ASYNC_CONTEXT *asyncContext,
  WS_ERROR * error);

Pour chaque portType WSDL, Wsutil.exe génère une table de fonctions représentant portType. Chaque opération sur le portType a un pointeur de fonction correspondant vers le rappel présent dans la table de fonctions.

struct ISimpleServiceMethodTable
{
  ISimpleService_SimpleMethodCallback SimpleMethod;
};

Des prototypes de proxy sont générés pour toutes les opérations. Le nom du prototype est le nom de l’opération (dans ce cas, « SimpleMethod ») spécifié dans le fichier WSDL du contrat de service.

HRESULT WINAPI SimpleMethod(WS_CHANNEL *channel,
  WS_HEAP *heap,
  int a,
  int *b,
  int *c,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR * error );

Génération de prototype de description locale uniquement

Les fichiers andstub de proxy contiennent la définition de la structure de définitions globale, y compris les prototypes et les définitions des structures contenant des descriptions locales uniquement et des implémentations de stub de proxy/service client.

Tous les prototypes et définitions qui sont locaux dans le fichier stub sont générés dans le cadre d’une structure encapsulante. Cette structure de description locale englobante fournit une hiérarchie claire des descriptions dont ont besoin la couche de sérialisation et le modèle de service. La structure de description locale a des prototypes similaires à ceux présentés ci-dessous :

struct _filenameLocalDefinitions
{
  struct {
  // schema related output for all the embedded 
  // descriptions that needs to describe global complex types.
  } globalTypes;
  // global elements.
  struct {
  // schema related output, like field description
  // structure description, element description etc.
  ...
  } globalElements;
  struct {
  // messages and related descriptions
  } messages;
  struct {
  // contract and related descriptions.
  } contracts;
  struct {
  // XML dictionary entries.
  } dictionary;
} _filenameLocalDefinitions;

Référence des définitions à partir d’autres fichiers

Les définitions locales peuvent référencer des descriptions générées dans un autre fichier. Par exemple, le message peut être défini dans le fichier de code C généré à partir du fichier WSDL, mais l’élément de message peut être défini ailleurs dans le fichier de code C généré à partir du fichier XSD. Dans ce cas, Wsutil.exe génère une référence à l’élément global à partir du fichier qui contient la définition du message, comme illustré ci-dessous :

{  // WS_MESSAGE_DESCRIPTION
...
(WS_ELEMENT_DESRIPTION *)b_xsd.globalElement.<elementname>
  };

Descriptions des éléments d’entrée

Pour chaque élément global défini dans un fichier wsdl:type ou XSD, il existe un champ correspondant nommé elementName à l’intérieur du champ GlobalElement. Dans cet exemple, une structure nommée SimpleMethod est générée :

typedef struct _SimpleServiceLocal
{
  struct  // global elements
  {
    struct // SimpleMethod
    {
    ...
    WS_ELEMENT_DESCRIPTION SimpleMethod;
    } SimpleMethod;
    ...
  } globalElements;
}

D’autres descriptions dont a besoin la description de l’élément sont générées dans le cadre de la structure conteneur. Si l’élément est un élément de type simple, il y a seulement un champ WS_ELEMENT_DESCRIPTION. Si le type d’élément est une structure, tous les champs et descriptions de structure associés sont générés dans le cadre de la structure d’élément. Dans cet exemple, l’élément SimpleMethod est une structure contenant deux champs, a et b. Wsutil.exe génère la structure de la façon suivante :

...
struct // SimpleMethod
{
  struct // SimpleMethod structure
  {
    WS_FIELD_DESCRIPTION a;
    WS_FIELD_DESCRIPTION b;
    WS_FIELD_DESCRIPTION * SimpleMethodFields [2];
    WS_STRUCT_DESCRIPTION structDesc;
  } SimpleMethoddescs; // SimpleMethod
  WS_ELEMENT_DESCRIPTION elementDesc;
} SimpleMethod;
...

Les structures incorporées et les éléments incorporés sont générés comme des sous-structures, en fonction des besoins.

Wsutil.exe génère un champ sous la section WSDL pour chacune des valeurs portType définies sous le wsdl:service spécifié.

...
struct { // WSDL
    struct { // portTypeName
        struct { // operationName
        } operationName;
    ...
    WS_OPERATION_DESCRIPTION* operations[numOperations];
    WS_CONTRACT_DESCRIPTION contractDesc;
    } portTypeName;
}
...

Wsutil.exe génère un champ f qui contient toutes les descriptions nécessaires à l’opération, un seul tableau de pointeurs vers chacune des descriptions d’opération pour chaque méthode et un WS_CONTRACT_DESCRIPTION pour le portType spécifié.

Toutes les descriptions dont ont besoin les opérations sont générées dans le champ operationName sous le portType spécifié. Il s’agit notamment du champ WS_ELEMENT_DESCRIPTION ainsi que de la sous-structure des paramètres d’entrée et de sortie. De même, les champs WS_MESSAGE_DESCRIPTION pour le message d’entrée et le message de sortie facultatif sont inclus avec le champ de liste WS_PARAMETER_DESCRIPTION pour tous les paramètres d’opération et le champ WS_OPERATION_DESCRIPTION pour l’opération elle-même. Dans cet exemple, la structure de code de la description SimpleMethod est générée comme indiqué ci-dessous :

...
struct // messages
{
  WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_InputMessage;
  WS_MESSAGE_DESCRIPTION ISimpleService_SimpleMethod_OutputMessage;
} messages;
struct // contracts
{
  struct // DefaultBinding_ISimpleService
  {
    struct // SimpleMethod
    {
      WS_PARAMETER_DESCRIPTION params[3];
      WS_OPERATION_DESCRIPTION SimpleMethod;
    } SimpleMethod;
    WS_OPERATION_DESCRIPTION* operations[1];
    WS_CONTRACT_DESCRIPTION contractDesc;
  } DefaultBinding_ISimpleService;
} contracts;
...

Les noms et espaces de noms utilisés dans différentes descriptions sont générés comme des champs de type WS_XML_STRING. Toutes ces chaînes sont générées dans le cadre d’un dictionnaire constant par fichier. La liste des chaînes et le champ WS_XML_DICTIONARY (nommé dict dans l’exemple ci-dessous) sont générés dans le cadre du champ de dictionnaire de la structure fileNameLocal.

struct { // fileNameLocal
...
  struct { // dictionary
    struct { // XML string list
      WS_XML_STRING firstFieldName;
      WS_XML_STRING firstFieldNS;
      ...
    } xmlStrings;
  WS_XML_DICTIONARY dict;
  } dictionary;
}; // fileNameLocal;

Le tableau de WS_XML_STRING est généré sous la forme d’une série de champs de type WS_XML_STRING, nommés avec des noms conviviaux. Le stub généré utilise les noms conviviaux dans différentes descriptions pour améliorer la lisibilité.

Proxy client pour les opérations WSDL

Wsutil.exe génère un proxy client pour toutes les opérations. Les applications peuvent remplacer la signature de méthode en utilisant une option de ligne de commande comme préfixe.

HRESULT WINAPI bindingName_SimpleMethod(WS_SERVICE_PROXY *serviceProxy,
  WS_HEAP *heap,
  int a,
  int *b,
  int *c,
  const WS_CALL_PROPERTY* callProperties,
  ULONG callPropertyCount,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR * error )
{
  void* argList[] = {&a, &b, &c};
  return WsCall(_serviceProxy,
    (WS_OPERATION_DESCRIPTION*)&example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.SimpleMethod,
    (void **)&_argList,
    callProperties,
    callPropertyCount,
    heap,
    asyncContext,
    error
  );      
}

L’appelant d’opération doit passer un paramètre heap valide. Les paramètres de sortie sont alloués avec la valeur WS_HEAP spécifiée dans le paramètre heap. La fonction appelante peut réinitialiser ou libérer le tas afin de libérer de la mémoire pour tous les paramètres de sortie. Si l’opération échoue, d’autres informations détaillées sur l’erreur peuvent être récupérées à partir de l’objet d’erreur facultatif s’il est disponible.

Wsutil.exe génère un stub de service pour toutes les opérations décrites dans la liaison.

HRESULT CALLBACK ISimpleService_SimpleMethodStub(
  const WS_OPERATION_CONTEXT *context,
  void * stackStruct,
  void * callback,
  const WS_ASYNC_CONTEXT * asyncContext,
  WS_ERROR *error )
{
  SimpleMethodParamStruct *pstack = (SimpleMethodParamStruct *) stackstruct;
  SimpleMethodOperation operation = (SimpleMethodOperation)callback;
  return operation(context, pstack->a, &(pstack->b), &(pstack->c ), asyncContext, error );
}

La section ci-dessus décrit le prototype de la structure locale contenant toutes les définitions locales du fichier stub uniquement. Les sections suivantes décrivent les définitions des descriptions.

Génération de définition WSDL

Wsutil.exe génère une structure statique constante (const static) nommée *<file_name>*LocalDefinitions de type *<service_name>*Local qui contient toutes les définitions locales uniquement.

const static _SimpleServiceLocal example_wsdlLocalDefinitions =
{
  {  // global types
  ...
  }, // global types
  {  // global elements
  ...
  }, // global elements
  {  // messages
  ...
  }, //messages
  ...
  {  // dictionary
  ...
  }, // dictionary
},

Les descriptions WSDL suivantes sont prises en charge :

  • wsdl:service
  • wsdl:binding
  • wsdl:portType
  • wsdl:operation
  • wsdl:message

Traitement de wsdl:operation et wsdl:message

Chaque opération spécifiée dans le document WSDL est mappée à une opération de service par Wsutil.exe. L’outil génère des définitions séparées d’opérations de service pour le serveur et le client.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   <wsdl:input wsaw:Action="http://Example.org/ISimpleService/SimpleMethod" 
   message="tns:ISimpleService_SimpleMethod_InputMessage" />
   <wsdl:output wsaw:Action="http://Example.org/ISimpleService/SimpleMethodResponse" 
   message="tns:ISimpleService_SimpleMethod_OutputMessage" />
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
</wsdl:definitions>

La disposition des éléments de données des messages d’entrée et de sortie est évaluée par l’outil pour générer les métadonnées de sérialisation de l’infrastructure, ainsi que la signature réelle de l’opération de service résultante à laquelle les messages d’entrée et de sortie sont associés.

Les métadonnées de chaque opération dans un portType spécifique ont un message d’entrée et éventuellement un message de sortie, chacun de ces messages est mappé à un WS_MESSAGE_DESCRIPTION. Dans cet exemple, les messages d’entrée et de sortie sur l’opération dans le portType sont mappés à inputMessageDescription et éventuellement outputMessageDescription sur le WS_OPERATION_DESCRIPTION respectivement.

Pour chaque message WSDL, l’outil génère WS_MESSAGE_DESCRIPTION qui référence la définition WS_ELEMENT_DESCRIPTION, comme illustré ci-dessous :

... 
{    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.DefaultBinding_ISimpleServiceISimpleService_SimpleMethod_InputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&(WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethodReponse
},  // message description for ISimpleService_SimpleMethod_InputMessage
...

La description du message référence la description de l’élément d’entrée. Comme l’élément est défini globalement, la description du message référence la définition globale au lieu de l’élément statique local. De même, si l’élément est défini dans un autre fichier, Wsutil.exe génère une référence à la structure définie globalement dans ce fichier. Par exemple, si SimpleMethodResponse est défini dans un autre fichier example.xsd, Wsutil.exe génère à la place les éléments suivants :

...
{    // message description for ISimpleService_SimpleMethod_InputMessage
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.DefaultBinding_ISimpleServiceISimpleService_SimpleMethod_InputMessageactionName,
(WS_ELEMENT_DESCRIPTION*)&(WS_ELEMENT_DESCRIPTION*)&example_xsd.globalElements.SimpleMethodReponse
},  // message description for ISimpleService_SimpleMethod_InputMessage
...

Chaque description de message contient l’action et la description d’élément spécifique (champ de type WS_ELEMENT_DESCRIPTION) pour tous les éléments de données de message. Dans le cas d’un message de style RPC ou d’un message avec plusieurs parties, un élément wrapper est créé pour encapsuler les informations supplémentaires.

Prise en charge du style RPC

Wsutil.exe prend en charge le style document ainsi que les opérations de style RPC en fonction de la spécification de l’extension de liaison WSDL 1.1 pour SOAP 1.2. Les opérations de style RPC et littéral sont marquées comme WS_RPC_LITERAL_OPERATION. Le modèle de service ignore le nom de l’élément wrapper du corps de la réponse dans les opérations RPC/littérales.

Wsutil.exe ne prend pas en charge en mode natif les opérations de style encodage. Le paramètre WS_XML_BUFFER est généré pour l’encodage des messages, et les développeurs doivent remplir directement la mémoire tampon opaque.

Prise en charge de plusieurs parties de message

Wsutil.exe prend en charge plusieurs parties de message dans un message. Un message en plusieurs parties peut être spécifié de la façon suivante :

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:message name="ISimpleService_MutipleParts_InputMessage">
  <wsdl:part name="part1" element="tns:SimpleElement1" />
  <wsdl:part name="part2" element="tns:SimpleElement2" />
 </wsdl:message>
</wsdl:definitions>

Wsutil.exe génère un champ WS_STRUCT_TYPE pour l’élément de message si le message contient plusieurs parties. Si le message est représenté avec le style document, Wsutil.exe génère un élément wrapper avec le type struct. L’élément wrapper n’a pas de nom ni d’espace de noms spécifique, et la structure du wrapper contient tous les éléments de toutes les parties sous forme de champs. L’élément wrapper est destiné uniquement à une utilisation interne et n’est pas sérialisé dans le corps du message.

Si le message utilise une représentation de style RPC ou littéral, Wsutil.exe crée un élément wrapper avec le nom de l’opération comme nom d’élément et l’espace de noms spécifié comme espace de noms de service en fonction de la spécification de l’extension SOAP WSDL. La structure de l’élément contient un tableau de champs représentant les types spécifiés dans les parties de message. L’élément wrapper est mappé à l’élément supérieur réel dans le corps du message, comme indiqué dans la spécification SOAP.

Côté serveur, chaque opération entraîne un typedef de l’opération de service de serveur résultante. Ce typedef est utilisé pour référencer l’opération dans la table de fonctions, comme décrit précédemment. Chaque opération entraîne également la génération d’une fonction stub que l’infrastructure appelle au nom du délégué sur la méthode réelle.

typedef HRESULT (CALLBACK *SimpleMethodCallback) (
  const WS_OPERATION_CONTEXT* context,
  unsigned int  a,
  unsigned int * b,
  unsigned int * c,
  const WS_ASYNC_CONTEXT* asyncContext,
  WS_ERROR* error
  );

Pour l’opération SimpleMethod, le typedef SimpleMethodOperation est défini ci-dessus. Notez que la méthode générée a une liste d’arguments développée, et la partie de message des messages d’entrée et de sortie de l’opération SimpleMethod est un paramètre nommé.

Côté client, chaque opération est mappée à une opération de service proxy.

HRESULT WINAPI SimpleMethod (
  WS_SERVICE_PROXY* serviceProxy,
  ws_heap *heap,
  unsigned int  a,
  unsigned int * b,
  unsigned int * c,
  const WS_ASYNC_CONTEXT* asyncContext,
  WS_ERROR* error);

Traitement de wsdl:binding

Le modèle de service WWSAPI prend en charge l’extension de liaison SOAP. Pour chaque liaison, il existe un portType associé.

Le transport spécifié dans l’extension de liaison soap a un rôle de conseil uniquement. L’application doit fournir des informations de transport pendant la création d’un canal. Actuellement, nous prenons en charge les liaisons WS_HTTP_BINDING et WS_TCP_BINDING.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:binding name="DefaultBinding_ISimpleService" type="tns:ISimpleService">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="SimpleMethod">
   <soap:operation soapAction="http://Example.org/ISimpleService/SimpleMethod" 
   style="document" />
   <wsdl:input>
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>
</wsdl:definitions>

Dans notre exemple de document WSDL, nous avons un seul portType pour ISimpleService. La liaison SOAP fournie indique le transport HTTP, qui est spécifié comme WS_HTTP_BINDING. Notez que cette structure n’a pas de décoration statique, car elle doit être disponible pour l’application.

Traitement de wsdl:portType

Chaque portType dans WSDL est constitué d’une ou de plusieurs opérations. L’opération doit être cohérente avec l’extension de liaison SOAP indiquée dans wsdl:binding.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:portType name="ISimpleService">
  <wsdl:operation name="SimpleMethod">
   ...
  </wsdl:operation>
 </wsdl:portType>
</wsdl:definitions>

Dans cet exemple, le portType ISimpleService contient uniquement l’opération SimpleMethod. Cela est cohérent avec la section de liaison où il y a seulement une opération WSDL mappée à une action SOAP.

Comme le portType ISimpleService a seulement une opération (SimpleMethod), la table de fonctions correspondante contient uniquement SimpleMethod comme opération de service.

En termes de métadonnées, chaque portType est mappé par Wsutil.exe à un WS_CONTRACT_DESCRIPTION. Chaque opération au sein d’un portType est mappée à un WS_OPERATION_DESCRIPTION.

Dans cet exemple de portType, l’outil génère WS_CONTRACT_DESCRIPTION pour ISimpleService. Cette description de contrat contient le nombre spécifique d’opérations disponibles sur le portType ISimpleService, ainsi qu’un tableau de WS_OPERATION_DESCRIPTION représentant les opérations individuelles définies sur le portType pour ISimpleService. Comme il y a seulement une opération sur le portType ISimpleService pour ISimpleService, il y a seulement une définition WS_OPERATION_DESCRIPTION.

...  part of LocalDefinitions structure
{    // array of operations for DefaultBinding_ISimpleService
(WS_OPERATION_DESCRIPTION*)&example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.SimpleMethod,
},    // array of operations for DefaultBinding_ISimpleService
{    // contract description for DefaultBinding_ISimpleService
1,
(WS_OPERATION_DESCRIPTION**)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.operations,
WS_HTTP_CHANNEL_BINDING,
},  // end of contract description for DefaultBinding_ISimpleService
},    // DefaultBinding_ISimpleService       ...

Traitement de wsdl:service

WsUtil.exe utilise les services pour rechercher des liaisons/portTypes, et génère une structure de contrat qui décrit les types, les messages, les définitions de portType, etc. Les descriptions de contrat sont accessibles en externe et sont générées dans le cadre de la structure de définition globale spécifiée avec l’en-tête généré.

WsUtil.exe prend en charge les extensions EndpointReference définies dans wsdl:port. La référence de point de terminaison est définie dans WS-ADDRESSING comme un moyen de décrire les informations de point de terminaison d’un service. Le texte de l’extension de référence de point de terminaison d’entrée enregistré comme WS_XML_STRING, ainsi que la WS_ENDPOINT_ADDRESS_DESCRIPTION correspondante sont générés dans la section endpointReferences de la structure globale.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:service name="SimpleService">
  <wsdl:port name="ISimpleService" binding="tns:DefaultBinding_ISimpleService">
   <soap:address location="http://Example.org/ISimpleService" />
   <wsa:EndpointReference>
    <wsa:Address>http://example.org/wcfmetadata/WSHttpNon</wsa:Address>
   </wsa:EndpointReference> 
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>
  const _example_wsdl example_wsdl =
  {
  ... // global element description
  {// messages
  {    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_InputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethod,
},    // message description for ISimpleService_SimpleMethod_InputMessage
{    // message description for ISimpleService_SimpleMethod_OutputMessage
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_OutputMessageactionName,
  (WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethodResponse,
},    // message description for ISimpleService_SimpleMethod_OutputMessage
}, // messages
{// contracts
{   // DefaultBinding_ISimpleService
1,
(WS_OPERATION_DESCRIPTION**)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.operations,
WS_HTTP_CHANNEL_BINDING,
},    // end of DefaultBinding_ISimpleService
    }, // contracts
    {
        {
            {   // endpointAddressDescription
                WS_ADDRESSING_VERSION_0_9,
            },                    
            (WS_XML_STRING*)&xml_string_generated_in_stub // endpointReferenceString
        }, //DefaultBinding_ISimpleService
    }, // endpointReferences
}

Pour créer WS_ENDPOINT_ADDRESS en utilisant les métadonnées générées par WsUtil :

WsCreateReader      // Create a WS_XML_READER
Initialize a WS_XML_READER_BUFFER_INPUT
WsSetInput          // Set the encoding and input of the reader to generate endpointReferenceString
WsReadType        // Read WS_ENDPOINT_ADDRESS from the reader
    // Using WS_ELEMENT_TYPE_MAPPING, WS_ENDPOINT_ADDRESS_TYPE and generated endpointAddressDescription, 

Les chaînes constantes dans le proxy client ou le stub de service sont générées sous forme de champs de type WS_XML_STRING, et il y a un dictionnaire constant pour toutes les chaînes du fichier proxy ou stub. Chaque chaîne du dictionnaire est générée sous forme de champ dans la partie dictionnaire de la structure locale pour une meilleure lisibilité.

... // dictionary part of LocalDefinitions structure
{    // xmlStrings
  { // xmlStrings
    WS_XML_STRING_DICTIONARY_VALUE("a",&example_wsdlLocalDefinitions.dictionary.dict, 0), 
    WS_XML_STRING_DICTIONARY_VALUE("http://Sapphire.org",&example_wsdlLocalDefinitions.dictionary.dict, 1), 
    WS_XML_STRING_DICTIONARY_VALUE("b",&example_wsdlLocalDefinitions.dictionary.dict, 2), 
    WS_XML_STRING_DICTIONARY_VALUE("SimpleMethod",&example_wsdlLocalDefinitions.dictionary.dict, 3),
    ...
  },  // end of xmlStrings
  {   // SimpleServicedictionary
    // 45026280-d5dc-4570-8195-4d66d13bfa34
    { 0x45026280, 0xd5dc, 0x4570, { 0x81, 0x95, 0x4d,0x66, 0xd1, 0x3b, 0xfa, 0x34 } },
    (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings,
    stringCount,
    TRUE,
  },
}
...

Traitement de wsdl:type

Wsutil.exe prend uniquement en charge les documents de schéma XML (XSD) dans la spécification wsdl:type. Un cas particulier est quand un port de message spécifie une définition d’élément global. Consultez la section suivante pour plus d’informations sur l’heuristique utilisée dans ce cas.

Heuristique de traitement des paramètres

Dans le modèle de service, les messages WSDL sont mappés à des paramètres spécifiques dans une méthode. Wsutil.exe a deux styles de génération de paramètre : dans le premier style, l’opération a un paramètre pour le message d’entrée et un paramètre pour le message de sortie (si nécessaire). Dans le deuxième style, Wsutil.exe utilise une heuristique pour mapper des champs dans les structures des messages d’entrée et de sortie à différents paramètres de l’opération et développer ces champs. Les messages d’entrée et de sortie doivent avoir des éléments de message de type structure pour générer cette deuxième approche.

Wsutil.exe utilise les règles suivantes pour générer les paramètres d’opération des messages d’entrée et de sortie :

  • Pour les messages d’entrée et de sortie avec plusieurs parties de message, chaque partie de message est un paramètre distinct dans l’opération, et le nom de la partie de message est le nom du paramètre.
  • Pour le message de style RPC avec une seule partie de message, la partie de message est un paramètre dans l’opération, et le nom de la partie de message est le nom du paramètre.
  • Pour les messages d’entrée et de sortie de style document avec une seule partie de message :
    • Si le nom d’une partie de message est « parameters » et que le type d’élément est une structure, chaque champ de la structure est traité comme un paramètre distinct dont le nom de champ est le nom du paramètre.
    • Si le nom de la partie de message n’est pas « parameters », le message est un paramètre dans l’opération et le nom du message est utilisé comme nom du paramètre correspondant.
  • Pour les messages d’entrée et de sortie de style document avec un élément nillable, le message est mappé à un paramètre, et le nom de la partie de message est le nom du paramètre. Un niveau supplémentaire d’indirection est ajouté pour indiquer que le pointeur peut être NULL.
  • Si un champ s’affiche uniquement dans l’élément de message d’entrée, le champ est traité comme un paramètre [in].
  • Si un champ s’affiche uniquement dans l’élément de message de sortie, le champ est traité comme un paramètre [out].
  • S’il existe un champ de même nom et de même type qui s’affiche à la fois dans le message d’entrée et le message de sortie, le champ est traité comme un paramètre [in,out].

Les outils suivants sont utilisés pour déterminer la direction des paramètres :

  • Si un champ s’affiche uniquement dans l’élément de message d’entrée, le champ est traité comme un paramètre in uniquement.
  • Si un champ s’affiche uniquement dans l’élément de message de sortie, le champ est traité comme un paramètre out uniquement.
  • S’il existe un champ de même nom et de même type qui s’affiche à la fois dans le message d’entrée et le message de sortie, le champ est traité comme un paramètre in,out.

Wsutil.exe prend uniquement en charge les éléments séquencés. Il rejette l’ordre non valide des paramètres [in,out] si Wsutil.exe ne peut pas combiner les paramètres in et out dans une seule liste de paramètres. Des suffixes peuvent être ajoutés aux noms de paramètre pour éviter la collision de noms.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:message name="ISimpleService_SimpleMethod_InputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethod" />
 </wsdl:message>
 <wsdl:message name="ISimpleService_SimpleMethod_OutputMessage">
  <wsdl:part name="parameters" element="tns:SimpleMethodResponse" />
 </wsdl:message>
</wsdl:definitions>

Wsutil.exe considère les champs dans tns:SimpleMethod et tns:SimpleMethodResponse comme des paramètres, comme indiqué dans les définitions de paramètre ci-dessous :

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://Example.org" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://Example.org" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
 <wsdl:types>
  <xs:schema xmlns:tns="http://Example.org" elementFormDefault="qualified" 
  targetNamespace="http://Example.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:import namespace="http://Example.org" />
   <xs:element name="SimpleMethod">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="a" type="xs:unsignedInt" />
      <xs:element name="b" type="xs:unsignedInt" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
   <xs:element name="SimpleMethodResponse">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="b" type="xs:unsignedInt" />
      <xs:element name="c" type="xs:unsignedInt" />
     </xs:sequence>
    </xs:complexType>
   </xs:element>
  </xs:schema>
 </wsdl:types>
</wsdl:definitions>

Wsutil.exe développe la liste des paramètres des champs de la liste ci-dessus et génère la structure ParamStruct dans l’exemple de code suivant. L’exécution du modèle de service peut utiliser cette structure pour passer des arguments aux stubs client et serveur.

typedef struct SimpleMethodParamStruct {
  unsigned int   a;  
  unsigned int   b;
  unsigned int   c;
} ;

Cette structure est utilisée uniquement pour décrire le cadre de pile côté client et serveur. Il n’y a aucun changement de la description du message ou des descriptions d’élément référencées par la description du message.

  // following are local definitions for the complex type
  { // field description for a
  WS_ELEMENT_FIELD_MAPPING,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aLocalName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_INT32_TYPE,
  0,
  WsOffsetOf(_SimpleMethod, a),
  0,
  0,
  },    // end of field description for a
  { // field description for b
  WS_ELEMENT_FIELD_MAPPING,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.bLocalName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_INT32_TYPE,
  0,
  WsOffsetOf(_SimpleMethod, b),
  0,
  0,
  },    // end of field description for b
  {    // fields description for _SimpleMethod
  (WS_FIELD_DESCRIPTION *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.a,
  (WS_FIELD_DESCRIPTION *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.b,
  },
  {  // structure definition
  sizeof(_SimpleMethod),
  __alignof(_SimpleMethod),
  (WS_FIELD_DESCRIPTION**)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs._SimpleMethodFields,
  WsCountOf(example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs._SimpleMethodFields),
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings._SimpleMethodTypeName,
(WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  0,
  },   // struct description for _SimpleMethod
  // following are global definitions for the out parameter
  ...
  {  // element description
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings._SimpleMethodTypeName,
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.aNamespace,
  WS_STRUCT_TYPE,
  (void *)&example_wsdlLocalDefinitions.globalElements.SimpleMethod._SimpleMethoddescs.structDesc,
  },
  {    // message description for ISimpleService_SimpleMethod_InputMessage
  (WS_XML_STRING*)&example_wsdlLocalDefinitions.dictionary.xmlStrings.ISimpleService_SimpleMethod_InputMessageactionName,
(WS_ELEMENT_DESCRIPTION*)&example_wsdl.globalElements.SimpleMethod,
  },    // message description for ISimpleService_SimpleMethod_InputMessage

En règle générale, un niveau d’indirection est ajouté pour tous les paramètres [out] et [in,out].

Opération sans paramètre

Pour les opérations de document et littérales, Wsutil.exe traite l’opération comme si elle avait un paramètre d’entrée et un paramètre de sortie si :

  • Le message d’entrée ou de sortie a plusieurs parties.
  • Il y a une seule partie de message et le nom de la partie de message n’est pas « parameters ».

.. Dans l’exemple ci-dessus, en supposant que les parties de message sont nommées ParamIn et ParamOut, la signature de méthode devient le code suivant :

typedef struct SimpleMethod{
unsigned int a;
unsigned int b;
};

typedef struct SimpleMethodResponse {
unsigned int b;
unsigned int c;
};

typedef  struct ISimpleService_SimpleMethodParamStruct
{
SimpleMethod  * SimpleMethod;
SimpleMethodResponse  * SimpleMethodResponse;
} ISimpleService_SimpleMethodParamStruct;

Wsutil.exe génère une signature de version pour la description d’opération de sorte que le moteur de modèle de service côté serveur et WsCall puisse vérifier si la description générée s’applique à la plateforme actuelle.

Ces informations de version sont générées dans le cadre de la structure WS_OPERATION_DESCRIPTION. Le numéro de version peut être traité comme un sélecteur de bras d’union pour rendre la structure extensible. Actuellement, versionID est défini sur 1 sans champs qui le suivent. La future version peut incrémenter le numéro de version et inclure davantage de champs si nécessaire. Par exemple, Wsutil.exe génère actuellement le code suivant à partir de l’ID de version :

{ // SimpleMethod
{ // parameter descriptions for SimpleMethod
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)0, (USHORT)-1 },
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)1, (USHORT)-1 },
{ WS_PARAMETER_TYPE_NORMAL, (USHORT)-1, (USHORT)1 },
},    // parameter descriptions for SimpleMethod
{    // operation description for SimpleMethod
1,
(WS_MESSAGE_DESCRIPTION*)&example_wsdl.messages.ISimpleService_SimpleMethod_InputMessage,
(WS_MESSAGE_DESCRIPTION*)&example_wsdl.messages.ISimpleService_SimpleMethod_OutputMessage,
3,
(WS_PARAMETER_DESCRIPTION*)example_wsdlLocalDefinitions.contracts.DefaultBinding_ISimpleService.SimpleMethod.params,
SimpleMethodOperationStub
}, //operation description for SimpleMethod
},  // SimpleMethod

À l’avenir, il peut être développé de la façon suivante :

WS_OPERATION_DESCRIPTION simpleMethodOperationDesc =
{
  2,
  &ISimpleService_SimpleMethod_InputputMessageDesc,
  &ISimpleService_SimpleMethod_OutputMessageDesc,
  WsCountOf(SimpleMethodParameters),
  SimpleMethodParameters,
  ISimpleService_SimpleMethod_Stub,
  &forwardToString;   // just as an example.
};

Sécurité

Consultez la section de sécurité de la rubrique Outil Compilateur Wsutil.