Leggere in inglese

Condividi tramite


WSDL e contratti di servizio

L'utilità Wsutil.exe genera uno stub del linguaggio C in base ai metadati WSDL forniti, nonché alle definizioni e alle descrizioni dei tipi di dati per i tipi di dati descritti dagli XML Schema creati dall'utente.

Di seguito è riportato un esempio di documento WSDL e XML Schema che funge da base per la discussione seguente:

<?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>

Questo esempio fornisce un contratto, ISimpleService, con un singolo metodo SimpleMethod. "SimpleMethod" ha due parametri di input di tipo integer, a e b, inviati dal client al servizio. Analogamente, SimpleMethod ha due parametri di output di tipo integer, b e c, che vengono restituiti al client dopo il completamento corretto. Nella sintassi C con annotazioni SAL la definizione del metodo viene visualizzata come segue:

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

In questa definizione ISimpleService è un contratto di servizio con una singola operazione di servizio: SimpleMethod.

Il file di intestazione di output contiene definizioni e descrizioni per riferimento esterno. Tali controlli includono:

  • Definizioni di struttura C per i tipi di elemento globali.
  • Prototipo di operazione definito nel file corrente.
  • Prototipo di tabella delle funzioni per i contratti specificati nel file WSDL.
  • Prototipi di stub del proxy client e del servizio per tutte le funzioni specificate nel file corrente.
  • Struttura dei dati WS_ELEMENT_DESCRIPTION per gli elementi dello schema globale definiti nel file corrente.
  • Struttura dei dati WS_MESSAGE_DESCRIPTION per tutti i messaggi specificati nel file corrente.
  • Struttura dei dati WS_CONTRACT_DESCRIPTION per tutti i contratti specificati nel file corrente.

Viene generata una struttura globale per incapsulare tutte le descrizioni globali per i tipi di schema e i tipi di modello di servizio a cui l'applicazione può fare riferimento. La struttura viene denominata usando un nome di file normalizzato. In questo esempio Wsutil.exe genera una struttura di definizioni globale denominata "example_wsdl" che contiene tutte le descrizioni del servizio Web. La definizione della struttura viene generata nel file 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;

Per le definizioni di elementi globali nel documento XML Schema (XSD), viene generato un prototipo WS_ELEMENT_DESCRIPTION e la definizione del tipo C corrispondente per ognuno degli elementi. I prototipi per le descrizioni degli elementi per SimpleMethod e SimpleMethodResponse vengono generati come membri nella struttura precedente. Le strutture C vengono generate come segue:

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

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

Analogamente per i tipi complessi globali, Wsutil.exe genera definizioni di struttura C di tipo come sopra, senza descrizioni di elementi corrispondenti.

Per l'input WSDL, Wsutil.exe genera i prototipi e le definizioni seguenti:

  • Viene generato un prototipo WS_MESSAGE_DESCRIPTION per la descrizione del messaggio. Questa descrizione può essere usata dal modello di servizio e dal livello messaggio. Le strutture di descrizione del messaggio sono campi denominati "messagename" nella struttura globale. In questo esempio, la descrizione del messaggio viene generata come campo ISimpleService_SimpleMethod_InputMessage nella struttura ISimpleService_SimpleMethod_InputMessage come specificato nel file WSDL.
  • WS_CONTRACT_DESCRIPTION prototipo viene generato per la descrizione del contratto. Questa descrizione viene usata dal modello di servizio. Le strutture di descrizione del contratto sono campi denominati "contractname" nella struttura globale. In questo esempio la descrizione del contratto viene generata come campo DefaultBinding_ISimpleService nella struttura "_example_wsdl".

Le specifiche relative a operazioni e tipi sono comuni sia al proxy che allo stub e vengono generate in entrambi i file. Wsutil.exe genera una copia solo se il proxy e lo stub vengono generati nello stesso file.

Generazione di identificatori

Le strutture C generate automaticamente elencate sopra vengono create in base al nome specificato nel file WSDL. Un NCName XML non viene in genere considerato un identificatore C valido e i nomi vengono normalizzati in base alle esigenze. I valori esadecimali non vengono convertiti e le lettere comuni come ':', '/' e '.' vengono convertite nel carattere di sottolineatura '_' per migliorare la leggibilità.

Intestazione per lo stub

Per ogni operazione nel contratto di servizio, viene generata una routine di callback denominata "<operationname>Callback". Ad esempio, l'operazione "SimpleMethod" nel contratto di servizio di esempio ha un callback generato denominato "SimpleMethodCallback".

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

Per ogni portType WSDL Wsutil.exe genera una tabella di funzioni che rappresenta portType. Ogni operazione sul portType ha un puntatore di funzione corrispondente al callback presente nella tabella delle funzioni.

struct ISimpleServiceMethodTable
{
  ISimpleService_SimpleMethodCallback SimpleMethod;
};

I prototipi proxy vengono generati per tutte le operazioni. Il nome del prototipo è il nome dell'operazione (in questo caso, "SimpleMethod") specificato nel file WSDL per il contratto di servizio.

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

Generazione di prototipi di descrizione solo locale

I file proxy estub contengono la definizione per la struttura delle definizioni globali, inclusi prototipi e definizioni per le strutture contenenti descrizioni solo locali e implementazioni di stub proxy/servizio client.

Tutti i prototipi e le definizioni locali del file stub vengono generati come parte di una struttura incapsulata. Questa struttura di descrizione locale a livello generale fornisce una gerarchia chiara delle descrizioni richieste dal livello di serializzazione e dal modello di servizio. La struttura di descrizione locale ha prototipi simili a quello illustrato di seguito:

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;

Riferimento alle definizioni da altri file

Le definizioni locali possono fare riferimento alle descrizioni generate in un altro file. Ad esempio, il messaggio può essere definito nel file di codice C generato dal file WSDL, ma l'elemento messaggio può essere definito altrove nel file di codice C generato dal file XSD. In questo caso, Wsutil.exe genera un riferimento all'elemento globale dal file che contiene la definizione del messaggio, come illustrato di seguito:

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

Descrizioni degli elementi globali

Per ogni elemento globale definito in un file wsdl:type o XSD, è presente un campo corrispondente denominato elementName all'interno del campo GlobalElement . In questo esempio viene generata una struttura denominata SimpleMethod:

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

Altre descrizioni richieste dalla descrizione dell'elemento vengono generate come parte della struttura contenitore. Se l'elemento è un elemento di tipo semplice, è presente un solo campo WS_ELEMENT_DESCRIPTION . Se il tipo di elemento è una struttura, tutti i campi correlati e le descrizioni della struttura vengono generati come parte della struttura dell'elemento. In questo esempio l'elemento SimpleMethod è una struttura contenente due campi, a e b. Wsutil.exe genera la struttura come segue:

...
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;
...

Le strutture incorporate e gli elementi incorporati vengono generati come sotto-strutture in base alle esigenze.

Wsutil.exe genera un campo nella sezione WSDL per ognuno dei valori portType definiti nel wsdl:service specificato.

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

Wsutil.exe genera un campo f che contiene tutte le descrizioni necessarie per l'operazione, una matrice di puntatori a ogni descrizione dell'operazione e una WS_CONTRACT_DESCRIPTION per il portType specificato.

Tutte le descrizioni necessarie per le operazioni vengono generate all'interno del campo operationName sotto il portType specificato. Questi includono il campo WS_ELEMENT_DESCRIPTION e la sottostruttura per i parametri di input e output. Analogamente, i campi WS_MESSAGE_DESCRIPTION per il messaggio di input e il messaggio di output facoltativo sono inclusi insieme a ; WS_PARAMETER_DESCRIPTION campo elenco per tutti i parametri dell'operazione e il campo WS_OPERATION_DESCRIPTION per l'operazione stessa. In questo esempio viene generata la struttura di codice per la descrizione SimpleMethod, come illustrato di seguito:

...
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;
...

I nomi e gli spazi dei nomi usati in varie descrizioni vengono generati come campi di tipo WS_XML_STRING. Tutte queste stringhe vengono generate come parte di un dizionario costante per file. L'elenco di stringhe e il campo WS_XML_DICTIONARY (denominato dict nell'esempio seguente) vengono generati come parte del campo dizionario della struttura fileNameLocal .

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

La matrice di WS_XML_STRINGs viene generata come una serie di campi di tipo WS_XML_STRING, denominati con nomi descrittivi. Lo stub generato usa i nomi descrittivi in varie descrizioni per migliorare la leggibilità.

Proxy client per operazioni WSDL

Wsutil.exe genera un proxy client per tutte le operazioni. Le applicazioni possono sovrascrivere la firma del metodo usando un'opzione della riga di comando prefisso.

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

Il chiamante dell'operazione deve passare un parametro heap valido. I parametri di output vengono allocati usando il valore WS_HEAP specificato nel parametro heap . La funzione chiamante può reimpostare o liberare l'heap per rilasciare memoria per tutti i parametri di output. Se l'operazione ha esito negativo, è possibile recuperare informazioni aggiuntive sull'errore dall'oggetto errore facoltativo, se disponibile.

Wsutil.exe genera uno stub del servizio per tutte le operazioni descritte nell'associazione.

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 sezione precedente descrive il prototipo della struttura locale contenente tutte le definizioni locali solo nel file stub. Le sezioni successive descrivono le definizioni delle descrizioni.

Generazione di definizioni WSDL

Wsutil.exe genera una struttura statica costante (statica const) denominata *<file_name>*LocalDefinitions di tipo *<service_name>*Local che contiene tutte le definizioni solo locali.

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

Sono supportate le descrizioni WSDL seguenti:

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

Elaborazione di wsdl:operation e wsdl:message

Ogni operazione specificata nel documento WSDL viene mappata a un'operazione del servizio da Wsutil.exe. Lo strumento genera definizioni separate delle operazioni del servizio sia per il server che per il 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>

Il layout degli elementi dati del messaggio di input eoutput viene valutato dallo strumento per generare i metadati di serializzazione per l'infrastruttura insieme alla firma effettiva dell'operazione del servizio risultante a cui sono associati i messaggi di input e output.

I metadati per ogni operazione all'interno di un portType specifico hanno input e, facoltativamente, un messaggio di output, ognuno di questi messaggi viene mappato a un WS_MESSAGE_DESCRIPTION. In questo esempio, l'input e il messaggio di output sull'operazione nel portType mappato rispettivamente a inputMessageDescription e facoltativamente all'outputMessageDescription nel WS_OPERATION_DESCRIPTION .

Per ogni messaggio WSDL lo strumento genera WS_MESSAGE_DESCRIPTION che fa riferimento alla definizione di WS_ELEMENT_DESCRIPTION, come illustrato di seguito:

... 
{    // 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 descrizione del messaggio fa riferimento alla descrizione dell'elemento di input. Poiché l'elemento è definito a livello globale, la descrizione del messaggio fa riferimento alla definizione globale anziché all'elemento statico locale. Analogamente, se l'elemento è definito in un altro file, Wsutil.exe genera un riferimento alla struttura definita a livello globale in tale file. Ad esempio, se SimpleMethodResponse è definito in un altro file example.xsd, Wsutil.exe genera invece quanto segue:

...
{    // 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
...

Ogni descrizione del messaggio contiene l'azione e la descrizione dell'elemento specifico (un campo di tipo WS_ELEMENT_DESCRIPTION) per tutti gli elementi di dati del messaggio. Nel caso di un messaggio in stile RPC o di un messaggio con più parti, viene creato un elemento wrapper per incapsulare le informazioni aggiuntive.

Supporto dello stile RPC

Wsutil.exe supporta operazioni di tipo documento e di tipo RPC in base alla specifica WSDL 1.1 Binding Extension for SOAP 1.2. Le operazioni di tipo RPC e letterale vengono contrassegnate come WS_RPC_LITERAL_OPERATION. Il modello di servizio ignora il nome dell'elemento wrapper del corpo della risposta nelle operazioni RPC/literal.

Wsutil.exe non supporta in modo nativo le operazioni di codifica. Il parametro WS_XML_BUFFER viene generato per la codifica dei messaggi e gli sviluppatori devono popolare direttamente il buffer opaco.

Supporto di più parti di messaggio

Wsutil.exe supporta più parti di messaggio in un unico messaggio. È possibile specificare un messaggio in più parti nel modo seguente:

<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 genera un campo WS_STRUCT_TYPE per l'elemento del messaggio se il messaggio contiene più parti. Se il messaggio viene rappresentato utilizzando lo stile del documento, Wsutil.exe genera un elemento wrapper con tipo struct. L'elemento wrapper non ha un nome o uno spazio dei nomi specifico e la struttura wrapper contiene tutti gli elementi in tutte le parti come campi. L'elemento wrapper è solo per uso interno e non verrà serializzato nel corpo del messaggio.

Se il messaggio usa la rappresentazione di stile RPC o letterale , Wsutil.exe crea un elemento wrapper con il nome dell'operazione come nome dell'elemento e lo spazio dei nomi specificato come spazio dei nomi del servizio in base alla specifica dell'estensione SOAP WSDL. La struttura dell'elemento contiene una matrice di campi che rappresentano i tipi specificati nelle parti del messaggio. L'elemento wrapper viene mappato all'elemento principale effettivo nel corpo del messaggio, come indicato nella specifica SOAP.

Sul lato server, ogni operazione genera un typedef dell'operazione del servizio server risultante. Questo typedef viene usato per fare riferimento all'operazione nella tabella delle funzioni come descritto in precedenza. Ogni operazione comporta anche la generazione di una funzione stub che nfrastructure chiama per conto del delegato al metodo effettivo.

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
  );

Per l'operazione SimpleMethod, il typedef SimpleMethodOperation è definito in precedenza. Si noti che il metodo generato ha un elenco di argomenti espanso con la parte del messaggio di input e output per l'operazione SimpleMethod come parametri denominati.

Sul lato client viene eseguito il mapping di ogni operazione a un'operazione del servizio 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);

Elaborazione di wsdl:binding

Il modello di servizio WWSAPI supporta l'estensione di associazione SOAP. Per ogni associazione è presente un portType associato.

Il trasporto specificato nell'estensione dell'associazione soap è solo consultivo. L'applicazione deve fornire informazioni di trasporto durante la creazione di un canale. Attualmente sono supportate le associazioni WS_HTTP_BINDING e 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>

Nel documento WSDL di esempio è disponibile un solo portType per ISimpleService. L'associazione SOAP fornita indica il trasporto HTTP, specificato come WS_HTTP_BINDING. Si noti che questa struttura non dispone di decorazione statica perché questa struttura deve essere disponibile per l'applicazione.

Elaborazione di wsdl:portType

Ogni portType in WSDL è costituito da una o più operazioni. L'operazione deve essere coerente con l'estensione di associazione SOAP indicata in 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>

In questo esempio, il portType ISimpleService contiene solo l'operazione SimpleMethod. Ciò è coerente con la sezione di associazione in cui è presente una sola operazione WSDL che esegue il mapping a un'azione SOAP.

Poiché il portType ISimpleService ha una sola operazione, SimpleMethod, la tabella della funzione corrispondente contiene solo SimpleMethod come operazione di servizio.

In termini di metadati, ogni portType viene mappato da Wsutil.exe a un WS_CONTRACT_DESCRIPTION. Ogni operazione all'interno di un portType viene mappata a un WS_OPERATION_DESCRIPTION.

In questo esempio portType lo strumento genera WS_CONTRACT_DESCRIPTION per ISimpleService. Questa descrizione del contratto contiene il numero specifico di operazioni disponibili nel portType ISimpleService insieme a una matrice di WS_OPERATION_DESCRIPTION che rappresenta le singole operazioni definite nel portType per ISimpleService. Poiché è presente una sola operazione in ISimpleService portType per ISimpleService, esiste anche una sola definizione di 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       ...

Elaborazione di wsdl:service

WsUtil.exe usa i servizi per trovare binding/porttypes e genera una struttura del contratto che descrive tipi, messaggi, definizioni di porttype e così via. Le descrizioni dei contratti sono accessibili esternamente e vengono generate come parte della struttura di definizione globale specificata tramite l'intestazione generata.

WsUtil.exe supporta le estensioni EndpointReference definite in wsdl:port. Il riferimento all'endpoint viene definito in WS-ADDRESSING come modo per descrivere le informazioni sull'endpoint di un servizio. Il testo dell'estensione di riferimento dell'endpoint di input salvato come WS_XML_STRING, insieme alle WS_ENDPOINT_ADDRESS_DESCRIPTION corrispondenti, viene generato nella sezione endpointReferences nella struttura 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
}

Per creare WS_ENDPOINT_ADDRESS usando i metadati generati da 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, 

Le stringhe costanti nello stub del proxy client o del servizio vengono generate come campi di tipo WS_XML_STRING ed è presente un dizionario costante per tutte le stringhe nel file proxy o stub. Ogni stringa nel dizionario viene generata come campo nella parte del dizionario della struttura locale per una migliore leggibilità.

... // 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,
  },
}
...

Elaborazione di wsdl:type

Wsutil.exe supporta solo documenti XML Schema (XSD) nella specifica wsdl:type. Un caso speciale è quando una porta di messaggio specifica una definizione di elemento globale. Per altri dettagli sull'euristica usata in questi casi, vedere la sezione seguente.

Euristica dell'elaborazione dei parametri

Nel modello di servizio, i messaggi WSDL vengono mappati a parametri specifici in un metodo. Wsutil.exe ha due stili di generazione di parametri: in primo stile, l'operazione ha un parametro per il messaggio di input e un parametro per il messaggio di output (se necessario); nel secondo stile, Wsutil.exe usa un'euristica per eseguire il mapping e l'espansione dei campi nelle strutture sia per i messaggi di input che per i messaggi di output a parametri diversi nell'operazione. Sia i messaggi di input che di output devono avere elementi messaggio di tipo struttura per generare questo secondo approccio.

Wsutil.exe usa le regole seguenti durante la generazione di parametri dell'operazione dai messaggi di input e output:

  • Per i messaggi di input e output con più parti del messaggio, ogni parte del messaggio è un parametro separato nell'operazione, con il nome della parte del messaggio come nome del parametro.
  • Per il messaggio di stile RPC con una parte del messaggio, la parte del messaggio è un parametro nell'operazione, con il nome della parte del messaggio come nome del parametro.
  • Per i messaggi di input e output in stile documento con una parte del messaggio:
    • Se il nome di una parte del messaggio è "parameters" e il tipo di elemento è una struttura, ogni campo della struttura viene considerato come un parametro separato con il nome del campo come nome del parametro.
    • Se il nome della parte del messaggio non è "parameters", il messaggio è un parametro nell'operazione con il nome del messaggio usato come nome del parametro corrispondente.
  • Per l'input dello stile del documento e il messaggio di output con elemento nillable, il messaggio viene mappato a un parametro, con il nome della parte del messaggio come nome del parametro. Viene aggiunto un ulteriore livello di riferimento indiretto per indicare che il puntatore può essere NULL.
  • Se un campo viene visualizzato solo nell'elemento del messaggio di input, il campo viene considerato come un parametro [in].
  • Se un campo viene visualizzato solo nell'elemento del messaggio di output, il campo viene considerato come un parametro [out].
  • Se è presente un campo con lo stesso nome e lo stesso tipo visualizzato sia nel messaggio di input che nel messaggio di output, il campo viene considerato come un parametro [in,out].

Per determinare la direzione dei parametri vengono usati gli strumenti seguenti:

  • Se un campo viene visualizzato solo nell'elemento del messaggio di input, il campo viene considerato come solo nel parametro .
  • Se un campo viene visualizzato solo nell'elemento del messaggio di output, il campo viene considerato come solo parametro out.
  • Se è presente un campo con lo stesso nome e lo stesso tipo visualizzato sia nel messaggio di input che nel messaggio di output, il campo viene considerato come un parametro in,out.

Wsutil.exe supporta solo gli elementi sequenziati. Rifiuta l'ordinamento non valido per quanto riguarda i parametri [in,out] se Wsutil.exe non può combinare i parametri in parametri e parametri out in un singolo elenco di parametri. È possibile aggiungere suffissi ai nomi dei parametri per evitare conflitti di nomi.

<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 considera i campi in tns:SimpleMethod e tns:SimpleMethodResponse ato be parameters, come illustrato nelle definizioni dei parametri seguenti:

<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 espande l'elenco dei parametri dai campi nell'elenco precedente e genera la struttura ParamStruct nell'esempio di codice seguente. Il runtime del modello di servizio può usare questa struttura per passare argomenti agli stub client e server.

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

Questa struttura viene usata solo per descrivere lo stack frame sul lato client e server. Non viene apportata alcuna modifica alla descrizione del messaggio o alle descrizioni degli elementi a cui fa riferimento la descrizione del messaggio.

  // 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

Come regola generale, viene aggiunto un livello di riferimento indiretto per tutti i parametri [out] e [in,out].

Operazione senza parametri

Per le operazioni sui documenti e sui valori letterali, Wsutil.exe considera l'operazione come un parametro di input e un parametro di output se:

  • Il messaggio di input o di output ha più di una parte.
  • Esiste una sola parte del messaggio e il nome della parte del messaggio non è "parameters".

.. Nell'esempio precedente, supponendo che le parti del messaggio siano denominate ParamIn" e ParamOut, la firma del metodo diventa il codice seguente:

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 genera una firma di versione per la descrizione dell'operazione, in modo che il motore del modello di servizio lato server e WsCall possa controllare se la descrizione generata è applicabile per la piattaforma corrente.

Queste informazioni sulla versione vengono generate come parte della struttura WS_OPERATION_DESCRIPTION. Il numero di versione può essere considerato come selettore di arm di unione per rendere estendibile la struttura. Attualmente, versionID è impostato su1 senza campi successivi. Il versiosn futuro può incrementare il numero di versione e includere più campi in base alle esigenze. Ad esempio, Wsutil.exe genera attualmente il codice seguente in base all'ID versione:

{ // 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

In futuro, potrebbe essere espansa come segue:

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

Sicurezza

Vedere la sezione relativa alla sicurezza nell'argomento relativo allo strumento del compilatore Wsutil.