A service operation is specified as a C/C++ function prototype in a header file. The function is REQUIRED to return int, which is used to represent a SOAP error code, see Section 10.2. Multiple service operations MAY be declared together in one header file. The general format of a service operation specification is:
[int] [namespace_prefix__]method_name([inparam1, inparam2, ...,] outparam); |
where
This simple form can only pass a single, non-struct and non-class type output parameter. See 10.1 for passing multiple output parameters. The name of the declared function namespace_prefix__ method_name must be unique and cannot match the name of a struct, class, or enum declared in the same header file. The method request is encoded in SOAP as an XML element and the namespace prefix, method name, and input parameters are encoded using the format:
<[namespace-prefix:]method_name xsi:type="[namespace-prefix:]method_name> <inparam-name1 xsi:type="...">...</inparam-name1> <inparam-name2 xsi:type="...">...</inparam-name2> ... </[namespace-prefix:]method_name> |
where the inparam-name accessors are the element-name representations of the inparam parameter name declarations, see Section 10.3. (The optional parts are shown enclosed in [].) The XML response by the Web service is of the form:
<[namespace-prefix:]method-nameResponse xsi:type="[namespace-prefix:]method-nameResponse> <outparam-name xsi:type="...">...</outparam-name> </[namespace-prefix:]method-nameResponse> |
where the outparam-name accessor is the element-name representation of the outparam parameter name declaration, see Section 10.3. By convention, the response element name is the method name ending in Response. See 10.1 on how to change the declaration if the service response element name is different. The gSOAP soapcpp2 tool generates a stub routine for the service operation. This stub is of the form:
int soap_call_[namespace_prefix__]method_name(struct soap *soap, char *URL, char *action, [inparam1, inparam2, ...,] outparam); |
This proxy can be called by a client application to perform the service operation call. The gSOAP soapcpp2 tool generates a skeleton routine for the service operation. The skeleton function is:
int soap_serve_[namespace_prefix__]method_name(struct soap *soap); |
The skeleton routine, when called by a service application, will attempt to serve a request on the standard input. If no request is present or if the request does not match the method name, SOAP_NO_METHOD is returned. The skeleton routines are automatically called by the generated soap_serve routine that handles all requests.
The input parameters of a service operation MUST be passed by value. Input parameters cannot be passed by reference with the & reference operator, but an input parameter value MAY be passed by a pointer to the data. Of course, passing a pointer to the data is preferred when the size of the data of the parameter is large. Also, to pass instances of (derived) classes, pointers to the instance need to be used to avoid passing the instance by value which requires a temporary and prohibits passing derived class instances. When two input parameter values are identical, passing them using a pointer has the advantage that the value will be encoded only once as multi-reference (hence, the parameters are aliases). When input parameters are passed using a pointer, the data pointed to will not be modified by the service operation and returned to the caller. The output parameter MUST be passed by reference using & or by using a pointer. Arrays are passed by reference by default and do not require the use of the reference operator &. The input and output parameter types have certain limitations, see Section 9.10 If the output parameter is a struct or class type, it is considered a service operation response element instead of a simple output parameter value. That is, the name of the struct or class is the name of the response element and the struct or class fields are the output parameters of the service operation, see also 7.1.7. Hence, if the output parameter has to be a struct or class, a response struct or class MUST be declared as well. In addition, if a service operation returns multiple output parameters, a response struct or class MUST be declared. By convention, the response element is the service operation name ending with "Response". The general form of a response element declaration is:
struct [namespace_prefix__]response_element_name { outparam1; outparam2; ... }; |
where
The general form of a service operation specification with a response element declaration for (multiple) output parameters is:
[int] [namespace_prefix__]method_name([inparam1, inparam2, ...,] struct [namespace_prefix__]response_element_name {outparam1[, outparam2, ...]} &anyparam); |
The choice of name for anyparam has no effect on the SOAP encoding and decoding and is only used as a place holder for the response. The method request is encoded in SOAP as an independent element and the namespace prefix, method name, and input parameters are encoded using the format:
<[namespace-prefix:]method-name xsi:type="[namespace-prefix:]method-name> <inparam-name1 xsi:type="...">...</inparam-name1> <inparam-name2 xsi:type="...">...</inparam-name2> ... </[namespace-prefix:]method-name> |
where the inparam-name accessors are the element-name representations of the inparam parameter name declarations, see Section 10.3. (The optional parts resulting from the specification are shown enclosed in [].) The method response is expected to be of the form:
<[namespace-prefix:]response-element-name xsi:type="[namespace-prefix:]response-element-name> <outparam-name1 xsi:type="...">...</outparam-name1> <outparam-name2 xsi:type="...">...</outparam-name2> ... </[namespace-prefix:]response-element-name> |
where the outparam-name accessors are the element-name representations of the outparam parameter name declarations, see Section 10.3. (The optional parts resulting from the specification are shown enclosed in [].) The input and/or output parameters can be made anonymous, which allows the deserialization of requests/responses with different parameter names as is endorsed by the SOAP 1.1 specification, see Section 7.1.13.
The error codes returned by the stub and skeleton routines are listed below.
|
The error codes that are returned by a stub routine (proxy) upon receiving a SOAP Fault from the server are marked (*). The remaining error codes are generated by the proxy itself as a result of problems with a SOAP payload. The error code is SOAP_OK when the service operation call was successful (the SOAP_OK predefined constant is guaranteed to be 0). The error code is also stored in soap.error, where soap is a variable that contains the current runtime context. The function soap_print_fault(struct soap *soap, FILE *fd) can be called to display an error message on fd where current value of the soap.error variable is used by the function to display the error. The function soap_print_fault_location(struct soap *soap, FILE *fd) prints the location of the error if the error is a result from parsing XML. Use soap_sprint_fault(struct soap*, char *buf, size_t len) to print the error to a string. A service operation implemented in a SOAP service MUST return an error code as the function's return value. SOAP_OK denotes success and SOAP_FAULT denotes an exception. The exception details can be assigned with the soap_receiver_fault(struct soap *soap, const char *faultstring, const char *detail) which sets the strings soap.fault->faultstring and soap.fault->detail for SOAP 1.1, and soap.fault->SOAP_ENV__Reason and soap.fault->SOAP_ENV__Detail for SOAP 1.2, where soap is a variable that contains the current runtime context, see Section 12. A receiver error indicates that the service can't handle the request, but can possibly recover from the error. To return an unrecoverable SOAP 1.1/1.2 error, use soap_sender_fault(struct soap *soap, const char *faultstring, const char *detail). To return a HTTP error code a service method can simply return the HTTP error code number. For example, return 404; returns a "404 Not Found" HTTP error back to the client. The soap.error is set to the HTTP error code at the client side. The HTTP 1.1 error codes are:
|
The error codes are given for informational purposes only. The HTTP protocol requires the proper actions after an error is issued. gSOAP's HTTP 1.0/1.1 handling is automatic.
One of the "secrets" behind the power and flexibility of gSOAP's encoding and decoding of service operation names, class names, type identifiers, and struct or class fields is the ability to specify namespace prefixes with these names that are used to denote their encoding style. More specifically, a C/C++ identifier name of the form
[namespace_prefix__]element_name |
where the prefix and the element name are separated by double underscores will be encoded in XML as
<[namespace-prefix:]element-name ...> |
The underscore pair (__) separates the namespace prefix from the element name. Each namespace prefix has a namespace URI specified by a namespace mapping table 10.4, see also Section 7.1.2. The namespace URI is a unique identification that can be associated with the service operations and data types. The namespace URI disambiguates potentially identical service operation names and data type names used by disparate organizations. XML element names are NCNames (restricted strings) that MAY contain hyphens, dots, and underscores. The special characters in the XML element names of service operations, structs, classes, typedefs, and fields can be controlled using the following conventions: A single underscore in a namespace prefix or identifier name is replaced by a hyphen (-) in the XML element name. For example, the identifier name SOAP_ENC__ur_type is represented in XML as SOAP-ENC:ur-type. The sequence _DOT is replaced by a dot (.), and the sequence _USCORE is replaced by an underscore (_) in the corresponding XML element name. For example:
class n_s__biz_DOTcom { char *n_s__biz_USCOREname; }; |
is encoded in XML as:
<n-s:biz.com xsi:type="n-s:biz.com"> <n-s:biz_name xsi:type="string">Bizybiz</n-s:biz_name> </n-s:biz.com> |
Trailing underscores of an identifier name are not translated into the XML representation. This is useful when an identifier name clashes with a C++ keyword. For example, return is often used as an accessor name in a SOAP response element. The return element can be specified as return_ in the C++ source code. Note that XML should be treated as case sensitive, so the use of e.g. Return may not always work to avoid a name clash with the return keyword. The use of trailing underscores also allows for defining structs and classes with essentially the same XML Schema type name, but that have to be distinguished as seperate C/C++ types. For decoding, the underscores in identifier names act as wildcards. An XML element is parsed and matches the name of an identifier if the name is identical to the element name (case insensitive) and the underscores in the identifier name are allowed to match any character in the element name. For example, the identifier name I_want__soap_fun_the_bea___DOTcom matches the element name I-want:[email protected]. By default, soapcpp2 generates data bindings in which all XML elements are and attributes are unqualified:
//gsoap x schema namespace: urn:x struct x__record { @char * type; char * name; }; |
where the name element and the type attribute are unqualified in the XML content (for example to facilitate SOAP RPC encoding). The rules for SOAP services that are document style are different:
//gsoap x schema namespace: urn:x //gsoap x service style: document struct x__record { @char * type; char * name; }; |
where x is associated with a service. For document style all elements are qualified and attributes are unqualified. To force qualification of elements and attributes, use the "form" directive:
//gsoap x schema namespace: urn:x //gsoap x schema form: qualified struct x__record { @char * type; char * name; }; |
You can also use "elementForm" and "attributeForm" directives to (un)qualify element and attributes of the schema, respectively. Because the soapcpp2-generated serializers follow the qualified/unqualified forms of the schemas, there is normally no need to explicitly qualify struct/class members because automatic encoding rules will be used. If explicit qualification is needed, this can be done using the prefix convention:
//gsoap x schema namespace: urn:x //gsoap y schema namespace: urn:y struct x__record { @char * xsi__type; char * y__name; }; |
which ensures that there cannot be any name clashes between members of the same name defined in different schemas (consider for example name and y__name), but this can clutter the representation when clashes do not occur. An alternative to the prefix convention is the use of "colon notation" in the gSOAP header file. This deviation from the C/C++ syntax allows you to bind type names and struct and class members to qualified and unqualified XML tag names explicitly, thus bypassing the default mechanism that automatically qualifies or unqualifies element and attribute tag names based on the schema element/attribute form. The colon notation for type names, struct/class names and members overrides the prefix qualification rules explicitly:
//gsoap x schema namespace: urn:x //gsoap y schema namespace: urn:y struct x:record { @char * xsi:type; char * y:name; }; |
where x and y are namespace prefixes that MUST be declared with a directive. The xsi:type member is an XML attribute in the xsi namespace. The soapcpp2 tool maps this to the following struct without the annotations:
// This code is generated from the above by soapcpp2 in soapStub.h: struct record { char *type; /* optional attribute of type xsd:string */ char *name; /* optional element of type xsd:string */ }; |
The soapcpp2 tool also generates XML schemas with element and attribute references. That is, y:name is referenced from the y schema by the x:record complexType defined in the x schema. The colon notation also allows you to override the element/attribute form to unqualified for qualified schemas:
//gsoap x schema namespace: urn:x //gsoap x schema form: qualified struct x:record { @char * :type; char * :name; }; |
where the colon notation ensures that both type and name are unqualified in the XML content, which overrides the default qualified forms of the x schema. Note that the use of colon notation to bind namespace prefixes to type names (typedef, enum, struct, and class names) translates to code without the prefixes. This means that name clashes can occur between types with identical unquaified names:
enum x:color { RED, WHITE, BLUE }; enum y:color { YELLOW, ORANGE }; // illegal enum name: name clash with x:color |
while prefixing with double underscores never lead to clashes:
enum x__color { RED, WHITE, BLUE }; enum y__color { YELLOW, ORANGE }; // no name clash |
Also note that colon notation has a very different role than the C++ scope operator ::. The scope operator cannot be used in places where we need colon notation, such as struct/class member fields. The default mechanism that associates XML tag names with the names of struct and class member fields can be overriden by "retagging" names with the annotation of `tag` placed next to the member field name. This is particularly useful to support legacy code for which the fixed naming of member fields cannot be easily changed. For example:
//gsoap x schema namespace: urn:x //gsoap x schema form: qualified struct x:record { @char * t`type`; char * s`full-name`; }; |
This maps the t member to the x:type XML attribute tag and s member to the x:full-name XML element tag. Note that both tags are namespace qualified as per schema declaration.
A namespace mapping table MUST be defined by clients and service applications. The mapping table is used by the serializers and deserializers of the stub and skeleton routines to produce a valid SOAP payload and to validate an incoming SOAP payload. A typical mapping table is shown below:
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name"} {"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/"}, // MUST be first {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"}, // MUST be second {"xsi", "http://www.w3.org/2001/XMLSchema-instance"}, // MUST be third {"xsd", "http://www.w3.org/2001/XMLSchema"}, // Required for XML Schema types {"ns1", "urn:my-service-URI"}, // The namespace URI of the service operations {NULL, NULL} // end of table }; |
Each namespace prefix used by a identifier name in the header file specification (see Section 10.3) MUST have a binding to a namespace URI in the mapping table. The end of the namespace mapping table MUST be indicated by the NULL pair. The namespace URI matching is case insensitive. A namespace prefix is distinguished by the occurrence of a pair of underscores (__) in an identifier. An optional namespace pattern MAY be provided with each namespace mapping table entry. The patterns provide an alternative namespace matching for the validation of decoded SOAP messages. In this pattern, dashes (-) are single-character wildcards and asterisks (*) are multi-character wildcards. For example, to decode different versions of XML Schema type with different authoring dates, four dashes can be used in place of the specific dates in the namespace mapping table pattern:
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/----/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/----/XMLSchema"}, ... |
Or alternatively, asterisks can be used as wildcards for multiple characters:
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance"}, {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema"}, ... |
A namespace mapping table is automatically generated together with a WSDL file for each namespace prefix that is used for a service operation specified in the header file. This namespace mapping table has entries for all namespace prefixes. The namespace URIs need to be filled in. These appear as http://tempuri.org in the table. See Section 19.2 on how to specify the namespace URIs in the header file. For decoding elements with namespace prefixes, the namespace URI associated with the namespace prefix (through the xmlns attribute of an XML element) is searched from the beginning to the end in a namespace mapping table, and for every row the following tests are performed as part of the validation process:
When a match is found, the namespace prefix in the first column of the table is considered semantically identical to the namespace prefix used by the XML element to be decoded, though the prefix names may differ. A service will respond with the namespace that it received from a client in case it matches a pattern in the third column. For example, let's say we have the following structs:
struct a__elt { ... }; struct b__elt { ... }; struct k__elt { ... }; |
and a namespace mapping table in the program:
struct Namespace namespaces[] = { // {"ns-prefix", "ns-name", "ns-name validation pattern"} ... {"a", "some uri"}, {"b", "other uri"}, {"c", "his uri", "* uri"}, ... |
Then, the following XML elements will match the structs:
<n:elt xmlns:n="some URI"> matches the struct name a__elt ... <m:elt xmlns:m="other URI"> matches the struct name b__elt ... <k:elt xmlns:k="my URI"> matches the struct name c__elt ... |
The response of a service to a client request that uses the namespaces listed above, will include my URI for the name space of element k. It is possible to use a number of different namespace tables and select the one that is appropriate. For example, an application might contact many different Web services all using different namespace URIs. If all the URIs are stored in one table, each service operation invocation will dump the whole namespace table in the SOAP payload. There is no technical problem with that, but it can be ugly when the table is large. To use different namespace tables, declare a pointer to a table and set the pointer to a particular table before service operation invocation. For example:
struct Namespace namespacesTable1[] = { ... }; struct Namespace namespacesTable2[] = { ... }; struct Namespace namespacesTable3[] = { ... }; struct Namespace *namespaces; ... struct soap soap; ... soap_init(&soap); soap_set_namespaces(&soap, namespaceTable1); soap_call_remote_method(&soap, URL, Action, ...); ... |