Contratos de serviço e WSDL
O utilitário Wsutil.exe gera um stub de linguagem C de acordo com metadados WSDL fornecidos, bem como definições de tipo de dados e descrições para tipos de dados descritos por esquemas XML criados pelo usuário.
Veja a seguir um exemplo de documento WSDL e esquema XML que serve como base para a discussão a seguir:
<?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>
Esse exemplo fornece um contrato, ISimpleService, com um único método, SimpleMethod. "SimpleMethod" tem dois parâmetros de entrada do tipo inteiro, a e b, que são enviados do cliente para o serviço. Da mesma forma, SimpleMethod tem dois parâmetros de saída do tipo inteiro, b e c, que são retornados ao cliente após a conclusão bem-sucedida. Na sintaxe C anotada por SAL, a definição do método é exibida da seguinte maneira:
void SimpleMethod(__in int a, __inout int * b, __out int * c );
Nesta definição, ISimpleService é um contrato de serviço com uma única operação de serviço: SimpleMethod.
O arquivo de cabeçalho de saída contém definições e descrições para referência externa. Isso inclui:
- Definições de estrutura C para tipos de elementos globais.
- Um protótipo de operação conforme definido no arquivo atual.
- Um protótipo de tabela de funções para os contratos especificados no arquivo WSDL.
- Protótipos de proxy de cliente e stub de serviço para todas as funções especificadas no arquivo atual.
- Uma estrutura de dados WS_ELEMENT_DESCRIPTION para os elementos de esquema globais definidos no arquivo atual.
- Uma estrutura de dados WS_MESSAGE_DESCRIPTION para todas as mensagens especificadas no arquivo atual.
- Uma estrutura de dados WS_CONTRACT_DESCRIPTION para todos os contratos especificados no arquivo atual.
Uma estrutura global é gerada para encapsular todas as descrições globais para os tipos de esquema e tipos de modelo de serviço aos quais o aplicativo pode se referir. A estrutura é nomeada usando um nome de arquivo normalizado. Neste exemplo, o Wsutil.exe gera uma estrutura de definições globais chamada "example_wsdl" que contém todas as descrições do serviço Web. A definição da estrutura é gerada no arquivo 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;
Para definições de elemento global no documento de esquema XML (XSD), um protótipo WS_ELEMENT_DESCRIPTION, bem como a definição de tipo C correspondente, são gerados para cada um dos elementos. Os protótipos das descrições do elemento para SimpleMethod e SimpleMethodResponse são gerados como membros na estrutura acima. As estruturas C são geradas da seguinte maneira:
typedef struct SimpleMethod
{
int a;
int b;
} SimpleMethod;
typedef struct SimpleMethodResponse
{
int b;
int c;
} SimpleMethodResponse;
Da mesma forma para tipos complexos globais, o Wsutil.exe gera definições de estrutura do tipo C, como acima, sem a correspondência de descrições de elementos.
Para a entrada WSDL, o Wsutil.exe gera os seguintes protótipos e definições:
- Um protótipo WS_MESSAGE_DESCRIPTION é gerado para a descrição da mensagem. Essa descrição pode ser usada pelo modelo de serviço, bem como pela camada de mensagem. As estruturas de descrição da mensagem são campos chamados "messagename" na estrutura global. Nesse exemplo, a descrição da mensagem é gerada como o campo ISimpleService_SimpleMethod_InputMessage na estrutura ISimpleService_SimpleMethod_InputMessage conforme especificado no arquivo WSDL.
- O protótipoWS_CONTRACT_DESCRIPTION é gerado para a descrição do contrato. Essa descrição é usada pelo modelo de serviço. As estruturas de descrição do contrato são campos chamados "contractname" na estrutura global. Nesse exemplo, a descrição do contrato é gerada como o campo DefaultBinding_ISimpleService na estrutura "_example_wsdl".
As especificações de operação e tipo são comuns ao proxy e ao stub e são geradas em ambos os arquivos. O Wsutil.exe só gerará uma cópia se o proxy e o stub forem gerados no mesmo arquivo.
Geração do identificador
As estruturas C geradas automaticamente listadas acima são criadas com base no nome especificado no arquivo WSDL. Um NCName XML geralmente não é considerado um identificador C válido e os nomes são normalizados conforme necessário. Valores hexadecimais não são convertidos e letras comuns como ":", '/' e "." são convertidas no caractere de sublinhado "_" para melhorar a legibilidade.
Cabeçalho para o stub
Para cada operação no contrato de serviço, uma rotina de retorno de chamada "<operationname>" é gerada. (Por exemplo, a operação "SimpleMethod" no contrato de serviço de exemplo tem um retorno de chamada gerado chamado "SimpleMethodCallback".)
typedef HRESULT (CALLBACK *SimpleMethodCallback) (
const WS_OPERATION_CONTEXT * context,
int a, int *b, int *c,
const WS_ASYNC_CONTEXT *asyncContext,
WS_ERROR * error);
Para cada portType Wsutil.exe do WSDL gera uma tabela de funções que representa o portType . Cada operação no portType tem um ponteiro de função correspondente para o retorno de chamada presente na tabela de funções.
struct ISimpleServiceMethodTable
{
ISimpleService_SimpleMethodCallback SimpleMethod;
};
Os protótipos de proxy são gerados para todas as operações. O nome do protótipo é o nome da operação (neste caso, "SimpleMethod") especificado no arquivo WSDL para o contrato de serviço.
HRESULT WINAPI SimpleMethod(WS_CHANNEL *channel,
WS_HEAP *heap,
int a,
int *b,
int *c,
const WS_ASYNC_CONTEXT * asyncContext,
WS_ERROR * error );
Geração de protótipo de descrição somente local
Os arquivos de proxy e stub contêm a definição para a estrutura de definições globais, incluindo protótipos e definições para as estruturas que contêm descrições somente local e implementações de stub de proxy/serviço do cliente.
Todos os protótipos e definições que são locais para o arquivo stub são gerados como parte de uma estrutura de encapsulamento. Essa estrutura de descrição local abrangente fornece uma hierarquia clara das descrições exigidas pela camada de serialização e pelo modelo de serviço. A estrutura de descrição local tem protótipos semelhantes ao mostrado abaixo:
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;
Como referenciar definições de outros arquivos
Definições locais podem referenciar descrições geradas em outro arquivo. Por exemplo, a mensagem pode ser definida no arquivo de código C gerado a partir do arquivo WSDL, mas o elemento de mensagem pode ser definido em outro lugar no arquivo de código C gerado a partir do arquivo XSD. Nesse caso, o Wsutil.exe gera referência ao elemento global do arquivo que contém a definição da mensagem, conforme demonstrado abaixo:
{ // WS_MESSAGE_DESCRIPTION
...
(WS_ELEMENT_DESRIPTION *)b_xsd.globalElement.<elementname>
};
Descrições de elemento global
Para cada elemento global definido em um arquivo wsdl:type ou XSD, há um campo correspondente chamado elementNamedentro do campo GlobalElement . Nesse exemplo, uma estrutura chamada SimpleMethod é gerada:
typedef struct _SimpleServiceLocal
{
struct // global elements
{
struct // SimpleMethod
{
...
WS_ELEMENT_DESCRIPTION SimpleMethod;
} SimpleMethod;
...
} globalElements;
}
Outras descrições exigidas pela descrição do elemento são geradas como parte da estrutura que contém. Se o elemento for um elemento de tipo simples, haverá apenas um campo WS_ELEMENT_DESCRIPTION. Se o tipo de elemento for uma estrutura, todos os campos relacionados e descrições de estrutura serão gerados como parte da estrutura do elemento. Neste exemplo, o elemento SimpleMethod é uma estrutura que contém dois campos, a e b. O Wsutil.exe gera a estrutura da seguinte maneira:
...
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;
...
Estruturas e elementos inseridos são gerados como subestruturas, conforme necessário.
Definições relacionadas ao WSDL
O Wsutil.exe gera um campo na seção WSDL para cada um dos valores de portType definidos no wsdl:service especificado.
...
struct { // WSDL
struct { // portTypeName
struct { // operationName
} operationName;
...
WS_OPERATION_DESCRIPTION* operations[numOperations];
WS_CONTRACT_DESCRIPTION contractDesc;
} portTypeName;
}
...
O Wsutil.exe gera um campo f que contém todas as descrições necessárias para a operação, uma matriz de sinais de ponteiros para cada uma das descrições da operação para cada método e uma WS_CONTRACT_DESCRIPTION para o portType especificado.
Todas as descrições necessárias pelas operações são geradas dentro do campo operationName no portType especificado. Elas incluem o campo WS_ELEMENT_DESCRIPTION, bem como a subestrutura para parâmetros de entrada e saída. Da mesma forma, os campos WS_MESSAGE_DESCRIPTION para a mensagem de entrada e a mensagem de saída opcional são incluídos junto com o campo de lista WS_PARAMETER_DESCRIPTION para todos os parâmetros de operação e o campo WS_OPERATION_DESCRIPTION para a operação em si. Nesse exemplo, a estrutura de código para a descrição SimpleMethod é gerada, conforme visto abaixo:
...
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;
...
Definições relacionadas ao dicionário XML
Nomes e namespaces usados em várias descrições são gerados como campos do tipo WS_XML_STRING. Todas essas cadeias de caracteres são geradas como parte de um dicionário constante por arquivo. A lista de cadeias de caracteres e o campo WS_XML_DICTIONARY (nomeadodict no exemplo abaixo) são gerados como parte do campo de dicionário da estrutura fileNameLocal .
struct { // fileNameLocal
...
struct { // dictionary
struct { // XML string list
WS_XML_STRING firstFieldName;
WS_XML_STRING firstFieldNS;
...
} xmlStrings;
WS_XML_DICTIONARY dict;
} dictionary;
}; // fileNameLocal;
A matriz de WS_XML_STRINGs é gerada como uma série de campos do tipo WS_XML_STRING, nomeados com nomes fáceis para o usuário. O stub gerado usa os nomes amigáveis do usuário em várias descrições para melhor legibilidade.
Proxy de cliente para operações WSDL
O Wsutil.exe gera um proxy de cliente para todas as operações. Os aplicativos podem substituir a assinatura do método usando uma opção de linha de comando de prefixo.
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
);
}
O chamador de operação deve passar um parâmetro de heapválido. Os parâmetros de saída são alocados usando o valor de WS_HEAP especificado no parâmetro heap. A função de chamada pode redefinir ou liberar o heap para liberar memória para todos os parâmetros de saída. Se a operação falhar, informações adicionais de erro de detalhes poderão ser recuperadas do objeto de erro opcional se estiverem disponíveis.
O Wsutil.exe gera um stub de serviço para todas as operações descritas na associação.
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 );
}
A seção acima descreve o protótipo da estrutura local que contém todas as definições locais apenas para o arquivo stub. As seções subsequentes descrevem as definições das descrições.
Geração de definição do WSDL
O Wsutil.exe gera uma estrutura estática constante (const static) chamada *<file_name>*LocalDefinitions do tipo *<service_name>*Local que contém todas as definições somente locais.
const static _SimpleServiceLocal example_wsdlLocalDefinitions =
{
{ // global types
...
}, // global types
{ // global elements
...
}, // global elements
{ // messages
...
}, //messages
...
{ // dictionary
...
}, // dictionary
},
Há suporte para as seguintes descrições do WSDL:
- wsdl:service
- wsdl:binding
- wsdl:portType
- wsdl:operation
- wsdl:message
Processando wsdl:operation e wsdl:message
Cada operação especificada no documento WSDL é mapeada para uma operação de serviço pelo Wsutil.exe. A ferramenta gera definições separadas das operações de serviço para o servidor e o cliente.
<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>
O layout dos elementos de dados de mensagem de entrada e saída é avaliado pela ferramenta para gerar os metadados de serialização para a infraestrutura, juntamente com a assinatura real da operação de serviço resultante à qual as mensagens de entrada e saída estão associadas.
Os metadados de cada operação em um portType específico têm entrada e, opcionalmente, uma mensagem de saída. Cada uma dessas mensagens é mapeada para um WS_MESSAGE_DESCRIPTION. Nesse exemplo, a entrada e a mensagem de saída na operação no portType mapeadas para inputMessageDescription e, opcionalmente, a outputMessageDescription no WS_OPERATION_DESCRIPTION respectivamente.
Para cada mensagem WSDL, a ferramenta gera WS_MESSAGE_DESCRIPTION que faz referência à definição WS_ELEMENT_DESCRIPTION, conforme demonstrado abaixo:
...
{ // 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
...
A descrição da mensagem refere-se à descrição do elemento de entrada. Como o elemento está definido globalmente, a descrição da mensagem faz referência à definição global em vez do elemento estático local. Da mesma forma, se o elemento for definido em outro arquivo, o Wsutil.exe gerará uma referência à estrutura definida globalmente nesse arquivo. Por exemplo, se SimpleMethodResponse for definido em outro arquivo example.xsd, o Wsutil.exe gerará o seguinte em vez disso:
...
{ // 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
...
Cada descrição de mensagem contém a ação e a descrição do elemento específico (um campo do tipo WS_ELEMENT_DESCRIPTION) para todos os elementos de dados da mensagem. No caso de uma mensagem no estilo RPC ou uma mensagem com várias partes, um elemento encapsulador é criado para encapsular as informações adicionais.
Suporte ao estilo RPC
O Wsutil.exe é compatível com o estilo de documento e operações de estilo RPC de acordo com a especificação da Extensão de Associação WSDL 1.1 para SOAP 1.2. As operações de estilo RPC e literal são marcadas como WS_RPC_LITERAL_OPERATION. O modelo de serviço ignora o nome do elemento encapsulador do corpo da resposta em operações RPC/literal.
O Wsutil.exe não dá suporte nativo a operações de estilo de codificação. O parâmetro WS_XML_BUFFER é gerado para codificar mensagens e os desenvolvedores devem preencher o buffer opaco diretamente.
Compatibilidade com várias partes de mensagem
O Wsutil.exe é compatível com várias partes de mensagem em uma mensagem. Uma mensagem de várias partes pode ser especificada da seguinte maneira:
<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>
O Wsutil.exe gerará um campo de WS_STRUCT_TYPE para o elemento de mensagem se a mensagem contiver várias partes. Se a mensagem for representada usando o estilo do documento, o Wsutil.exe gerará um elemento encapsulador com o tipo struct. O elemento encapsulador não tem um nome ou um namespace específico e a estrutura de encapsulador contém todos os elementos em todas as partes como campos. O elemento encapsulador é somente para uso interno e não será serializado no corpo da mensagem.
Se a mensagem estiver usando RPC ou representação de estilo literal, o Wsutil.exe criará um elemento encapsulador com o nome da operação como o nome do elemento e o namespace especificado como namespace de serviço de acordo com a especificação de extensão SOAP do WSDL. A estrutura do elemento contém uma matriz de campos que representam os tipos especificados nas partes da mensagem. O elemento encapsulador é mapeado para o elemento superior real no corpo da mensagem, conforme indicado na especificação SOAP.
No lado do servidor, cada operação resulta em um typedef da operação de serviço do servidor resultante. Esse typedef é usado para se referir à operação na tabela de funções, conforme descrito anteriormente. Cada operação também resulta na geração de uma função stub que a infraestrutura chama em nome do delegado para o método real.
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
);
Para a operação SimpleMethod, o typedef SimpleMethodOperation é definido acima. Observe que o método gerado tem uma lista de argumentos expandida com a parte da mensagem para a mensagem de entrada e saída da operação SimpleMethod como parâmetros nomeados.
No lado do cliente, cada operação é mapeada para uma operação de serviço 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);
Processando wsdl:binding
O modelo de serviço WWSAPI é compatível com a extensão de associação SSOAP. Para cada associação, há um portTypeassociado.
O transporte especificado na extensão de associação do SOAP é somente de consultoria. O aplicativo precisa fornecer informações de transporte ao criar um canal. Atualmente, damos suporte às associações 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>
Em nosso documento WSDL de exemplo, temos apenas um portType para ISimpleService. A associação SOAP fornecida indica o transporte HTTP, que é especificado como WS_HTTP_BINDING. Observe que essa estrutura não tem decoração estática, pois essa estrutura precisa estar disponível para o aplicativo.
Processando wsdl:portType
Cada portType no WSDL é composto por uma ou mais operações. A operação deve ser consistente com a extensão de associação SOAP indicada em 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>
Neste exemplo, o portType ISimpleService contém apenas a operação SimpleMethod. Isso é consistente com a seção de associação em que há apenas uma operação WSDL mapeada para uma ação SOAP.
Como o portType ISimpleService tem apenas uma operação, SimpleMethod, a tabela de funções correspondente contém apenas SimpleMethod como uma operação de serviço.
Em termos de metadados, cada portType é mapeado pelo Wsutil.exe para um WS_CONTRACT_DESCRIPTION. Cada operação dentro de um portType é mapeada para um WS_OPERATION_DESCRIPTION.
Nesse exemplo, portType a ferramenta gera WS_CONTRACT_DESCRIPTION para ISimpleService. Esta descrição do contrato contém o número específico de operações disponíveis no portType ISimpleService, juntamente com uma matriz deWS_OPERATION_DESCRIPTION que representa as operações individuais definidas no portType para ISimpleService. Como há apenas uma operação no ISimpleService portType para ISimpleService, também há apenas uma definição de 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 ...
Processando wsdl:service
O WsUtil.exe usa os serviços para localizar associações/porttypes e gera uma estrutura de contrato que descreve tipos, mensagens, definições de tipo de porta e assim por diante. As descrições do contrato são acessíveis externamente e são geradas como parte da estrutura de definição global especificada por meio do cabeçalho gerado.
O WsUtil.exe é compatível com extensões EndpointReference definidas em wsdl:port. A referência de ponto de extremidade é definida em WS-ADDRESSING como uma maneira de descrever informações de ponto de extremidade de um serviço. O texto da extensão de referência do ponto de extremidade de entrada salvo como WS_XML_STRING, juntamente com WS_ENDPOINT_ADDRESS_DESCRIPTION correspondentes, são gerados na seção endpointReferences na estrutura global.
<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
}
Para criar WS_ENDPOINT_ADDRESS usando os metadados gerados pelo 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,
Cadeias de caracteres constantes no proxy do cliente ou no stub de serviço são geradas como campos do tipo WS_XML_STRING e há um dicionário constante para todas as cadeias de caracteres no arquivo proxy ou stub. Cada cadeia de caracteres no dicionário é gerada como um campo na parte de dicionário da estrutura local para melhor legibilidade.
... // 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,
},
}
...
Processando wsdl:type
O Wsutil.exe só é compatível com documentos do esquema XML (XSD) na especificação wsdl:type. Um caso especial é quando uma porta de mensagem especifica uma definição de elemento global. Consulte a seção a seguir para obter mais detalhes sobre a heurística usada nesses casos.
Heurística de processamento de parâmetros
No modelo de serviço, as mensagens WSDL são mapeadas para parâmetros específicos em um método. O Wsutil.exe tem dois estilos de geração de parâmetros: no primeiro estilo, a operação tem um parâmetro para mensagem de entrada e um parâmetro para a mensagem de saída (se necessário); no segundo estilo, o Wsutil.exe usa uma heurística para mapear e expandir campos nas estruturas para mensagens de entrada e mensagens de saída para parâmetros diferentes na operação. As mensagens de entrada e saída precisam ter elementos de mensagem de tipo de estrutura para gerar essa segunda abordagem.
O Wsutil.exe usa as seguintes regras ao gerar parâmetros de operação das mensagens de entrada e saída:
- Para mensagens de entrada e saída com várias partes de mensagem, cada parte da mensagem é um parâmetro separado na operação, com o nome da parte da mensagem como nome do parâmetro.
- Para mensagem de estilo RPC com uma parte de mensagem, a parte da mensagem é um parâmetro na operação, com o nome da parte da mensagem como nome do parâmetro.
- Para mensagens de entrada e saída no estilo de documento com uma parte da mensagem:
- Se o nome de uma parte de mensagem for "parâmetros" e o tipo de elemento for uma estrutura, cada campo na estrutura será tratado como um parâmetro separado com o nome do campo sendo o nome do parâmetro.
- Se o nome da parte da mensagem não for "parâmetros", a mensagem será um parâmetro na operação com o nome da mensagem usado como o nome do parâmetro correspondente.
- Para entrada de estilo de documento e mensagem de saída com elemento anulável, a mensagem é mapeada para um parâmetro, com o nome da parte da mensagem como nome do parâmetro. Um nível adicional de indireção é adicionado para indicar que o ponteiro pode ser NULL.
- Se um campo aparecer somente no elemento de mensagem de entrada, o campo será tratado como um parâmetro [in].
- Se um campo aparecer somente no elemento de mensagem de saída, o campo será tratado como um parâmetro [out].
- Se houver um campo com o mesmo nome e o mesmo tipo que aparece na mensagem de entrada e na mensagem de saída, o campo será tratado como um parâmetro [in,out].
As ferramentas a seguir são usadas para determinar a direção dos parâmetros:
- Se um campo aparecer somente no elemento de mensagem de entrada, o campo será tratado como apenas no parâmetro.
- Se um campo aparecer somente no elemento de mensagem de saída, o campo será tratado como parâmetro somente para fora.
- Se houver um campo com o mesmo nome e o mesmo tipo que aparece na mensagem de entrada e na mensagem de saída, o campo será tratado como um parâmetro de entrada e saída.
O Wsutil.exe só é compatível com elementos sequenciados. Ele rejeita a ordenação inválida em relação aos parâmetros [in,out] se o Wsutil.exe não puder combinar os parâmetros in e out em uma única lista de parâmetros. Sufixos podem ser adicionados a nomes de parâmetro para evitar colisão de nome.
<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>
O Wsutil.exe considera os campos em tns:SimpleMethod e tns:SimpleMethodResponse ato como parâmetros, conforme visto nas definições de parâmetro abaixo:
<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>
O Wsutil.exe expande a lista de parâmetros dos campos na lista acima e gera a estrutura ParamStruct no exemplo de código a seguir. O tempo de execução do modelo de serviço pode usar essa estrutura para passar argumentos para os stubs do cliente e do servidor.
typedef struct SimpleMethodParamStruct {
unsigned int a;
unsigned int b;
unsigned int c;
} ;
Essa estrutura é usada apenas para descrever o quadro de pilha no lado do cliente e do servidor. Não há nenhuma alteração na descrição da mensagem ou nas descrições do elemento referenciadas pela descrição da mensagem.
// 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
Como regra geral, um nível de indireção é adicionado para todos os parâmetros [out] e [in,out].
Operação sem parâmetros
Para operações de documento e literal, o Wsutil.exe tratará a operação como tendo um parâmetro de entrada e um parâmetro de saída se:
- A mensagem de entrada ou saída tem mais de uma parte.
- Há apenas uma parte da mensagem e o nome da parte da mensagem não é "parâmetros".
.. No exemplo acima, supondo que as partes da mensagem sejam chamadas ParamIn" e ParamOut, a assinatura do método se tornará o seguinte código:
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;
O Wsutil.exe gera uma assinatura de versão para a descrição da operação de modo que o WsCall e o mecanismo de modelo de serviço do lado do servidor possam verificar se a descrição gerada é aplicável à plataforma atual.
Essas informações de versão são geradas como parte da estrutura WS_OPERATION_DESCRIPTION. O número da versão pode ser tratado como um seletor de braço de união para tornar a estrutura extensível. Atualmente, a versionID é definida como 1, sem campos subsequentes. Versões futuras podem incrementar o número de versão e incluir mais campos conforme necessário. Por exemplo, o Wsutil.exe atualmente gera o seguinte código com base na ID da versão:
{ // 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
No futuro, ele poderá ser expandida da seguinte maneira:
WS_OPERATION_DESCRIPTION simpleMethodOperationDesc =
{
2,
&ISimpleService_SimpleMethod_InputputMessageDesc,
&ISimpleService_SimpleMethod_OutputMessageDesc,
WsCountOf(SimpleMethodParameters),
SimpleMethodParameters,
ISimpleService_SimpleMethod_Stub,
&forwardToString; // just as an example.
};
Segurança
Consulte a seção de segurança no tópico ferramenta do Compilador Wsutil.