Contratos de servicios y WSDL
La utilidad Wsutil.exe genera un código auxiliar del lenguaje C según los metadatos WSDL proporcionados, así como definiciones de tipos de datos y descripciones para los tipos de datos descritos por esquemas XML creados por el usuario.
A continuación se muestra un ejemplo de documento WSDL y esquema XML que sirve como base para la explicación siguiente:
<?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>
En este ejemplo se proporciona un contrato, ISimpleService, con un único método SimpleMethod. "SimpleMethod" tiene dos parámetros de entrada de tipo entero, a y b, que se envían desde el cliente al servicio. Del mismo modo, SimpleMethod tiene dos parámetros de salida de tipo entero, b y c, que se devuelven al cliente después de la finalización correcta. En la sintaxis de SAL anotada en C, la definición del método aparece de la siguiente manera:
void SimpleMethod(__in int a, __inout int * b, __out int * c );
En esta definición, ISimpleService es un contrato de servicio con una sola operación de servicio: SimpleMethod.
El archivo de encabezado de salida contiene definiciones y descripciones para referencia externa. Esto incluye:
- Definiciones de estructura de C para tipos de elementos globales.
- Un prototipo de operación tal como se define en el archivo actual.
- Prototipo de tabla de funciones para los contratos especificados en el archivo WSDL.
- Prototipos de proxy de cliente y código auxiliar de servicio para todas las funciones especificadas en el archivo actual.
- Estructura de datos WS_ELEMENT_DESCRIPTION para los elementos de esquema globales definidos en el archivo actual.
- Estructura de datos WS_MESSAGE_DESCRIPTION para todos los mensajes especificados en el archivo actual.
- Estructura de datos WS_CONTRACT_DESCRIPTION para todos los contratos especificados en el archivo actual.
Se genera una estructura global para encapsular todas las descripciones globales de los tipos de esquema y los tipos de modelo de servicio a los que puede hacer referencia la aplicación. La estructura se denomina con un nombre de archivo normalizado. En este ejemplo, Wsutil.exe genera una estructura global de definiciones denominada "example_wsdl" que contiene todas las descripciones del servicio web. La definición de la estructura se genera en el archivo de código auxiliar.
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 las definiciones de elementos globales en el documento de esquema XML (XSD), se genera un prototipo de WS_ELEMENT_DESCRIPTION así como la definición de tipo C correspondiente para cada uno de los elementos. Los prototipos de las descripciones de elementos para SimpleMethod y SimpleMethodResponse se generan como miembros de la estructura anterior. Las estructuras de C se generan de la siguiente manera:
typedef struct SimpleMethod
{
int a;
int b;
} SimpleMethod;
typedef struct SimpleMethodResponse
{
int b;
int c;
} SimpleMethodResponse;
Del mismo modo, para los tipos complejos globales, Wsutil.exe genera definiciones de estructura de tipo C como anteriormente, sin descripciones de elementos coincidentes.
Wsutil.exe genera los siguientes prototipos y definiciones para la entrada WSDL:
- Se genera un prototipo de WS_MESSAGE_DESCRIPTION para la descripción del mensaje. El modelo de servicio puede usar esta descripción, así como la capa de mensajes. Las estructuras de descripción del mensaje son campos denominados "messagename" en la estructura global. En este ejemplo, la descripción del mensaje se genera como el campo ISimpleService_SimpleMethod_InputMessage de la estructura ISimpleService_SimpleMethod_InputMessage tal como se especifica en el archivo WSDL.
- El prototipo WS_CONTRACT_DESCRIPTION se genera para la descripción del contrato. Este modelo de servicio usa esta descripción. Las estructuras de descripción del contrato son campos denominados "contractname" en la estructura global. En este ejemplo, la descripción del contrato se genera como el campo DefaultBinding_ISimpleService de la estructura "_example_wsdl".
Las especificaciones de operación y tipo son comunes tanto para proxy como para código auxiliar y se generan en ambos archivos. Wsutil.exe genera una copia solo si el proxy y el código auxiliar se generan en el mismo archivo.
Las estructuras de C generadas automáticamente enumeradas anteriormente se crean en función del nombre especificado en el archivo WSDL. Un NCName XML no suele considerarse un identificador de C válido y los nombres se normalizan según sea necesario. Los valores hexadecimales no se convierten, y las letras comunes como ':', '/' y '.' se convierten en el carácter de guion bajo '_' para mejorar la legibilidad.
Se genera una rutina de devolución de llamada denominada "<operationname>Callback" por cada operación del contrato de servicio. (Por ejemplo, la operación "SimpleMethod" del contrato de servicio de ejemplo tiene una devolución de llamada generada denominada "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 WSDL, Wsutil.exe genera una tabla de funciones que representa portType. Cada operación de portType tiene un puntero de función correspondiente a la devolución de llamada presente en la tabla de funciones.
struct ISimpleServiceMethodTable
{
ISimpleService_SimpleMethodCallback SimpleMethod;
};
Los prototipos de proxy se generan para todas las operaciones. El nombre del prototipo es el nombre de la operación (en este caso, "SimpleMethod") especificado en el archivo WSDL para el contrato de servicio.
HRESULT WINAPI SimpleMethod(WS_CHANNEL *channel,
WS_HEAP *heap,
int a,
int *b,
int *c,
const WS_ASYNC_CONTEXT * asyncContext,
WS_ERROR * error );
Los archivos proxy y de código auxiliar contienen la definición de la estructura global de definiciones, incluidos los prototipos y las definiciones de las estructuras que contienen descripciones solo locales e implementaciones de código auxiliar de proxy o servicio de cliente.
Todos los prototipos y definiciones que son locales del archivo de código auxiliar se generan como parte de una estructura de encapsulación. Esta estructura de descripción local general proporciona una jerarquía clara de las descripciones requeridas por la capa de serialización y el modelo de servicio. La estructura de descripción local tiene prototipos similares a los que se muestran a continuación:
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;
Las definiciones locales pueden hacer referencia a descripciones generadas en otro archivo. Por ejemplo, el mensaje se puede definir en el archivo de código C generado a partir del archivo WSDL, pero el elemento de mensaje puede definirse en otro lugar del archivo de código C generado a partir del archivo XSD. En este caso, Wsutil.exe genera una referencia al elemento global del archivo que contiene la definición del mensaje, como se muestra a continuación:
{ // WS_MESSAGE_DESCRIPTION
...
(WS_ELEMENT_DESRIPTION *)b_xsd.globalElement.<elementname>
};
Por cada elemento global definido en un archivo wsdl:type o XSD, hay un campo coincidente denominado elementNamedentro del campo GlobalElement. En este ejemplo, se genera una estructura denominada SimpleMethod:
typedef struct _SimpleServiceLocal
{
struct // global elements
{
struct // SimpleMethod
{
...
WS_ELEMENT_DESCRIPTION SimpleMethod;
} SimpleMethod;
...
} globalElements;
}
Otras descripciones requeridas por la descripción del elemento se generan como parte de la estructura contenedora. Si el elemento es un elemento de tipo simple, solo hay un campo WS_ELEMENT_DESCRIPTION. Si el tipo de elemento es una estructura, todos los campos y descripciones de estructura relacionados se generan como parte de la estructura de elementos. En este ejemplo, el elemento SimpleMethod es una estructura que contiene dos campos, a y b. Wsutil.exe genera la estructura de la siguiente manera:
...
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;
...
Las estructuras y los elementos incrustados se generan como substrucciones según sea necesario.
Wsutil.exe genera un campo en la sección WSDL para cada uno de los valores portType definidos en el wsdl:service especificado.
...
struct { // WSDL
struct { // portTypeName
struct { // operationName
} operationName;
...
WS_OPERATION_DESCRIPTION* operations[numOperations];
WS_CONTRACT_DESCRIPTION contractDesc;
} portTypeName;
}
...
Wsutil.exe genera un campo que contiene todas las descripciones necesarias para la operación, una única matriz de punteros a cada una de las descripciones de la operación para cada método y una WS_CONTRACT_DESCRIPTION para el portType especificado.
Todas las descripciones necesarias para las operaciones se generan dentro del campo operationName en el portType especificado. Estos incluyen el campo WS_ELEMENT_DESCRIPTION, así como la subestructura para los parámetros de entrada y salida. Del mismo modo, los campos WS_MESSAGE_DESCRIPTION del mensaje de entrada y el mensaje de salida opcional se incluyen junto con el campo de lista WS_PARAMETER_DESCRIPTION para todos los parámetros de operación y el campo WS_OPERATION_DESCRIPTION de la propia operación. En este ejemplo, la estructura de código de la descripción SimpleMethod se genera como se muestra a continuación:
...
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;
...
Los nombres y los espacios de nombres usados en varias descripciones se generan como campos de tipo WS_XML_STRING. Todas estas cadenas se generan como parte de un diccionario de constantes por archivo. La lista de cadenas y el campo WS_XML_DICTIONARY (denominado dict en el ejemplo siguiente) se generan como parte del campo diccionario de la estructura 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 matriz de WS_XML_STRINGse genera como una serie de campos de tipo WS_XML_STRING denominados con nombres fáciles de usar. El código auxiliar generado usa los nombres descriptivos en varias descripciones para mejorar la legibilidad.
Proxy de cliente para operaciones WSDL
Wsutil.exe genera un proxy de cliente para todas las operaciones. Las aplicaciones pueden sobrescribir la firma del método mediante una opción de línea de comandos de prefijo.
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
);
}
El llamador de la operación debe pasar un parámetro de montón válido. Los parámetros de salida se asignan mediante el valor WS_HEAP especificado en el parámetro de montón. La función de llamada puede restablecer o liberar el montón para liberar memoria para todos los parámetros de salida. Si se produce un error en la operación, se puede recuperar información de error adicional del objeto de error opcional si está disponible.
Wsutil.exe genera un código auxiliar de servicio para todas las operaciones descritas en el enlace.
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 );
}
En la sección anterior se describe el prototipo de la estructura local que contiene todas las definiciones locales solo para el archivo de código auxiliar. En las secciones posteriores se describen las definiciones de las descripciones.
Wsutil.exe genera una estructura estática constante (const static) denominada *<file_name>*LocalDefinitions de tipo *<service_name>*Local que contiene todas las definiciones solo locales.
const static _SimpleServiceLocal example_wsdlLocalDefinitions =
{
{ // global types
...
}, // global types
{ // global elements
...
}, // global elements
{ // messages
...
}, //messages
...
{ // dictionary
...
}, // dictionary
},
Se admiten las siguientes descripciones de WSDL:
- wsdl:service
- wsdl:binding
- wsdl:portType
- wsdl:operation
- wsdl:message
Wsutil.exe asigna cada operación especificada en el documento WSDL a una operación de servicio. La herramienta genera definiciones independientes de las operaciones de servicio para el servidor y el 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>
La herramienta evalúa el diseño de los elementos de datos de los mensajes de entrada y salida para generar los metadatos de serialización de la infraestructura junto con la firma real de la operación de servicio resultante a la que están asociados los mensajes de entrada y salida.
Los metadatos de cada operación dentro de un portType específico tienen entrada y, opcionalmente, un mensaje de salida. Cada uno de estos mensajes se asigna a un WS_MESSAGE_DESCRIPTION. En este ejemplo, los mensajes de entrada y salida de la operación del portType se asignan a inputMessageDescription y, opcionalmente, el outputMessageDescription del WS_OPERATION_DESCRIPTION respectivamente.
Por cada mensaje WSDL, la herramienta genera WS_MESSAGE_DESCRIPTION que hace referencia a la definición de WS_ELEMENT_DESCRIPTION, como se muestra a continuación:
...
{ // 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 descripción del mensaje hace referencia a la descripción del elemento de entrada. Dado que el elemento está definido globalmente, la descripción del mensaje hace referencia a la definición global, en lugar del elemento estático local. Del mismo modo, si el elemento se define en otro archivo, Wsutil.exe genera una referencia a la estructura definida globalmente en ese archivo. Por ejemplo, si SimpleMethodResponse se define en otro archivo example.xsd, Wsutil.exe genera lo siguiente:
...
{ // 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 descripción del mensaje contiene la acción y la descripción del elemento específica (un campo de tipo WS_ELEMENT_DESCRIPTION) para todos los elementos de datos del mensaje. En el caso de un mensaje de estilo RPC o un mensaje con varias partes, se crea un elemento contenedor para encapsular la información adicional.
Wsutil.exe admite operaciones de estilo de documento, así como operaciones de estilo RPC según la extensión de enlace WSDL 1.1 para la especificación SOAP 1.2. Las operaciones de estilo RPC y literal se marcan como WS_RPC_LITERAL_OPERATION. El modelo de servicio omite el nombre del elemento contenedor del cuerpo de respuesta en las operaciones RPC/literales.
Wsutil.exe no admite de forma nativa operaciones de estilo de codificación. El parámetro WS_XML_BUFFER se genera para codificar mensajes, y los desarrolladores deben rellenar directamente el búfer opaco.
Wsutil.exe admite varias partes de mensaje en un mensaje. Se puede especificar un mensaje de varias partes como se indica a continuación:
<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 para el elemento de mensaje si el mensaje contiene varias partes. Si el mensaje se representa mediante el estilo de documento, Wsutil.exe genera un elemento contenedor con tipo struct. El elemento contenedor no tiene un nombre o un espacio de nombres específico, y la estructura contenedora contiene todos los elementos de todas las partes como campos. El elemento contenedor es solo para uso interno y no se serializará en el cuerpo del mensaje.
Si el mensaje usa la representación de estilo RPC o literal, Wsutil.exe crea un elemento contenedor con el nombre de la operación como el nombre del elemento y el espacio de nombres especificado como espacio de nombres de servicio, según la especificación de extensión SOAP de WSDL. La estructura del elemento contiene una matriz de campos que representan los tipos especificados en las partes del mensaje. El elemento contenedor se asigna al elemento superior real del cuerpo del mensaje, como se indica en la especificación SOAP.
En el lado servidor, cada operación da como resultado una definición de tipo de la operación de servicio del servidor resultante. Esta definición de tipo se usa para hacer referencia a la operación de la tabla de funciones, tal como se ha descrito anteriormente. Cada operación también da como resultado la generación de una función de código auxiliar que la infraestructura llama en nombre del delegado al 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
);
La definición de tipo SimpleMethodOperation para la operación SimpleMethod se definió anteriormente. Tenga en cuenta que el método generado tiene una lista de argumentos expandidos con la parte del mensaje de entrada y salida para la operación SimpleMethod como parámetros con nombre.
En el lado cliente, cada operación se asigna a una operación de servicio de 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);
El modelo de servicio WWSAPI admite la extensión de enlace de SOAP. Para cada enlace hay un portType asociado.
El transporte especificado en la extensión de enlace SOAP es solo un aviso. La aplicación debe proporcionar información de transporte al crear un canal. Actualmente se admiten los enlaces WS_HTTP_BINDING y 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>
En nuestro documento WSDL de ejemplo solo tenemos un portType para ISimpleService. El enlace SOAP proporcionado indica el transporte HTTP, que se especifica como WS_HTTP_BINDING. Tenga en cuenta que esta estructura no tiene decoración estática, ya que esta estructura debe estar disponible para la aplicación.
Procesamiento de wsdl:portType
Cada portType de WSDL se compone de una o varias operaciones. La operación debe ser coherente con la extensión de enlace SOAP indicada en 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>
En este ejemplo, el portTypeISimpleService solo contiene la operación SimpleMethod. Esto es coherente con la sección de enlace en la que solo hay una operación WSDL que se asigna a una acción SOAP.
Dado que el portType de ISimpleService solo tiene una operación (SimpleMethod), la tabla de funciones correspondiente solo contiene SimpleMethod como una operación de servicio.
En términos de metadatos, Wsutil.exe asigna cada portType a un WS_CONTRACT_DESCRIPTION. Cada operación dentro de un portType se asigna a un WS_OPERATION_DESCRIPTION.
En este ejemplo de portType, la herramienta genera WS_CONTRACT_DESCRIPTION para ISimpleService. Esta descripción del contrato contiene el número específico de operaciones disponibles en el portType ISimpleService junto con una matriz de WS_OPERATION_DESCRIPTION que representa las operaciones individuales definidas en portType para ISimpleService. Puesto que solo hay una operación en portType ISimpleService para ISimpleService, también hay una definición 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 ...
WsUtil.exe usa los servicios para buscar enlaces/porttypes y genera una estructura de contrato que describe tipos, mensajes, definiciones de porttype, etc. Las descripciones del contrato son accesibles externamente y se generan como parte de la estructura de definición global especificada a través del encabezado generado.
WsUtil.exe admite extensiones EndpointReference definidas en wsdl:port. La referencia de punto de conexión se define en WS-ADDRESSING como una manera de describir la información del punto de conexión de un servicio. El texto de la extensión de referencia del punto de conexión de entrada guardado como WS_XML_STRING, junto con los WS_ENDPOINT_ADDRESS_DESCRIPTION coincidentes se generan en la sección endpointReferences de la estructura 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 crear WS_ENDPOINT_ADDRESS mediante los metadatos generados por 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,
Las cadenas constantes del proxy de cliente o el código auxiliar del servicio se generan como campos de tipo WS_XML_STRING, y hay un diccionario constante para todas las cadenas del archivo proxy o de código auxiliar. Cada cadena del diccionario se genera como un campo en la parte del diccionario de la estructura local para mejorar la legibilidad.
... // 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,
},
}
...
Wsutil.exe solo admite documentos de esquema XML (XSD) en la especificación wsdl:type. Un caso especial se da cuando un puerto de mensaje especifica una definición de elemento global. Consulte la siguiente sección para obtener más detalles sobre la heurística que se usa en estos casos.
En el modelo de servicio, los mensajes WSDL se asignan a parámetros específicos en un método. Wsutil.exe tiene dos estilos de generación de parámetros: en el primero, la operación tiene un parámetro para el mensaje de entrada y un otro para el de salida (si es necesario). En el segundo estilo, Wsutil.exe usa una heurística para asignar y expandir campos en las estructuras para los mensajes de entrada y de salida a distintos parámetros de la operación. Los mensajes de entrada y salida deben tener elementos de mensaje de tipo estructura para generar este segundo enfoque.
Wsutil.exe usa las siguientes reglas al generar parámetros de operación a partir de los mensajes de entrada y salida:
- Para los mensajes de entrada y salida con varias partes del mensaje, cada elemento de mensaje es un parámetro independiente en la operación, con el nombre del elemento de mensaje como nombre de parámetro.
- Para el mensaje de estilo RPC con una parte de mensaje, esta es un parámetro en la operación. El nombre del elemento de mensaje es el nombre del parámetro.
- Para los mensajes de entrada y salida de estilo de documento con una parte de mensaje:
- Si el nombre de una parte de mensaje es "parámetros" y el tipo de elemento es una estructura, cada campo de la estructura se trata como un parámetro independiente con un nombre de campo que es el nombre del parámetro.
- Si el nombre del elemento del mensaje no es "parámetros", el mensaje es un parámetro de la operación con el nombre del mensaje usado como nombre de parámetro correspondiente.
- En el caso de los mensajes de entrada y salida de estilo de documento con elemento que puede ser NULL, el mensaje se asigna a un parámetro, con el nombre del elemento de mensaje como nombre de parámetro. Se agrega un nivel adicional de direccionamiento indirecto para indicar que el puntero puede ser NULL.
- Si un campo solo aparece en el elemento de mensaje de entrada, el campo se trata como un parámetro [in].
- Si un campo solo aparece en el elemento de mensaje de salida, el campo se trata como un parámetro [out].
- Si hay un campo con el mismo nombre y el mismo tipo que aparece en el mensaje de entrada y en el mensaje de salida, el campo se trata como un parámetro [in,out].
Las siguientes herramientas se usan para determinar la dirección de los parámetros:
- Si un campo solo aparece en el elemento de mensaje de entrada, el campo se trata como un parámetro solo.
- Si un campo solo aparece en el elemento de mensaje de salida, el campo se trata como parámetro solo out.
- Si hay un campo con el mismo nombre y el mismo tipo que aparece en el mensaje de entrada y el mensaje de salida, el campo se trata como un parámetro in,out.
Wsutil.exe solo admite elementos secuenciados. Rechaza la ordenación no válida con respecto a los parámetros [in,out] si Wsutil.exe no puede combinar los parámetros in y out en una sola lista de parámetros. Es posible que los sufijos se agreguen a los nombres de parámetros para evitar la colisión de nombres.
<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 los campos de tns:SimpleMethod y tns:SimpleMethodResponse como parámetros, como se muestra en las definiciones de parámetros siguientes:
<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 expande la lista de parámetros de los campos de la lista anterior y genera la estructura ParamStruct en el ejemplo de código siguiente. El tiempo de ejecución del modelo de servicio puede usar esta estructura para pasar argumentos al código auxiliar del cliente y del servidor.
typedef struct SimpleMethodParamStruct {
unsigned int a;
unsigned int b;
unsigned int c;
} ;
Esta estructura solo se usa para describir el marco de pila en el lado cliente y servidor. No hay ningún cambio en la descripción del mensaje o en las descripciones de elementos a las que hace referencia la descripción del mensaje.
// 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 regla general, se agrega un nivel de direccionamiento indirecto para todos los parámetros [out] e [in,out].
Para las operaciones de documento y literal, Wsutil.exe trata la operación como tener un parámetro de entrada y un parámetro de salida si:
- El mensaje de entrada o salida tiene más de una parte.
- Solo hay una parte de mensaje y el nombre del elemento de mensaje no es "parámetros".
.. En el ejemplo anterior, suponiendo que las partes del mensaje se denominan ParamIn" y ParamOut, la firma del método se convierte en el código siguiente:
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 de versión para la descripción de la operación de forma que el motor de modelo de servicio WsCall y del lado servidor pueda comprobar si la descripción generada es aplicable a la plataforma actual.
Esta información de versión se genera como parte de la estructura WS_OPERATION_DESCRIPTION. El número de versión se puede tratar como un selector de brazo de unión para que la estructura sea extensible. Actualmente, el versionID se establece en 1 sin campos posteriores. El número de versión puede incrementar en futuras versiones e incluir más campos según sea necesario. Por ejemplo, Wsutil.exe genera actualmente el código siguiente basado en el identificador de versión:
{ // 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
En el futuro, podría expandirse de la siguiente manera:
WS_OPERATION_DESCRIPTION simpleMethodOperationDesc =
{
2,
&ISimpleService_SimpleMethod_InputputMessageDesc,
&ISimpleService_SimpleMethod_OutputMessageDesc,
WsCountOf(SimpleMethodParameters),
SimpleMethodParameters,
ISimpleService_SimpleMethod_Stub,
&forwardToString; // just as an example.
};
Consulte la sección seguridad del tema herramienta del compilador de Wsutil.