Oracle® XML Developer's Kit Programmer's Guide 10g Release 2 (10.2) Part Number B14252-01 |
|
|
View PDF |
This chapter contains these topics:
The Simple Object Access Protocol (SOAP) is an XML protocol for exchanging structured and typed information between peers using HTTP and HTTPS in a distributed environment. Only HTTP 1.0 is supported in the XDK for 10g release 2. SOAP has three parts:
The SOAP envelope which defines how to present what is in the message, who must process the message, and whether that processing is optional or mandatory.
A set of serialization and deserialization rules for converting application data types to and from XML.
A SOAP remote procedure call (RPC) that defines calls and responses.
Note: RPC and serialization/deserialization are not supported in this release. |
SOAP is operating system and language-independent because it is XML-based. This chapter presents the C implementation of the functions that read and write the SOAP message.
SOAP Version 1.2 is the definition of an XML-based message which is specified as an XML Infoset (an abstract data set, it could be XML 1.0) that gives a description of the message contents. Version 1.1 is also supported.
See Also: W3C SOAP 1.2 specifications at:
|
The Simple Object Access Protocol (SOAP) is a lightweight protocol for sending and receiving requests and responses across the Internet. Because it is based on XML and transport protocols such as HTTP, it is not blocked by most firewalls. SOAP is independent of operating system, implementation language, and object model.
The power of SOAP is its ability to act as the glue between heterogeneous software components. For example, Visual Basic clients can invoke CORBA services running on UNIX computers; Macintosh clients can invoke Perl objects running on Linux.
SOAP messages are divided into the following parts:
An envelope that contains the message, defines how to process the message and who should process it, and whether processing is optional or mandatory. The Envelope
element is required.
A set of encoding rules that describe the datatypes for the application. These rules define a serialization mechanism that converts the application datatypes to and from XML.
A remote procedure call (RPC) request and response convention. This required element is called a body element. The Body
element contains a first subelement whose name is the name of a method. This method request element contains elements for each input and output parameter. The element names are the parameter names. RPC is not currently supported in this release.
SOAP is independent of any transport protocol. Nevertheless, SOAP used over HTTP for remote service invocation has emerged as a standard for delivering programmatic content over the Internet.
Besides being independent of transfer protocol, SOAP is also independent of operating system. In other words, SOAP enables programs to communicate even when they are written in different languages and run on different operating systems.
SOAP messages are of the following types:
Requests for a service, including input parameters
Responses from the requested service, including return value and output parameters
Optional fault elements containing error codes and information
In a SOAP message, the payload contains the XML-encoded data. The payload contains no processing information. In contrast, the message header may contain processing information.
In SOAP requests, the XML payload contains several elements that include the following:
Root element
Method element
Header elements (optional)
Example 18-1 shows the format of a sample SOAP message request. A GetLastTradePrice
SOAP request is sent to a StockQuote
service. The request accepts a string parameter representing the company stock symbol and returns a float representing the stock price in the SOAP response.
Example 18-1 SOAP Request Message
POST /StockQuote HTTP/1.0 Host: www.stockquoteserver.com Content-Type: application/soap+xml; charset="utf-8" Content-Length: nnnn SOAPAction: "Some-URI" <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding/"> <SOAP-ENV:Body> <m:GetLastTradePrice xmlns:m="Some-URI"> <symbol>ORCL</symbol> <m:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
In Example 18-1, the XML document is the SOAP message. The <SOAP-ENV:Envelope>
element is the top-level element of the XML document. The payload is represented by the method element <m:GetLastTradePrice>
. Note that XML namespaces distinguish SOAP identifiers from application-specific identifiers.
The first line of the header specifies that the request uses HTTP as the transport protocol:
POST /StockQuote HTTP/1.1
Because SOAP is independent of transport protocol, the rules governing XML payload format are independent of the use of HTTP for transport of the payload. This HTTP request points to the URI /StockQuote
. Because the SOAP specification is silent on the issue of component activation, the code behind this URI determines how to activate the component and invoke the GetLastTradePrice
method.
Example 18-2 shows the format of the response to the request in Example 18-1. The <Price>
element contains the stock price for ORCL
requested by the first message.
Example 18-2 SOAP Response Message
HTTP/1.0 200 OK Content-Type: application/soap+xml; charset="utf-8" Content-Length: nnnn <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding/"> <SOAP-ENV:Body> <m:GetLastTradePriceResponse xmlns:m="Some-URI"> <Price>13.5</Price> </m:GetLastTradePriceResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
The messages shown in Example 18-1 and Example 18-2 illustrate two-way SOAP messaging, that is, a SOAP request that is answered by a SOAP response. A one-way SOAP message does not require a SOAP message in response.
SOAP clients are user-written applications that generate XML documents. The documents make a request for a SOAP service and handle the SOAP response. The SOAP implementation in the XDK processes requests from any client that sends a valid SOAP request.
Note the following useful features of the SOAP client API:
Supports a synchronous invocation model for requests and responses
Facilitates the writing of client applications to make SOAP requests
Encapsulates the creation of the SOAP request and the details of sending the request over the underlying transport protocol
Supports a pluggable transport, allowing the client to easily change the transport (available transports include HTTP and HTTPS, but only HTTP 1.0 is supported in this release)
The SOAP client must perform the following steps to make a request and receive a response:
Gather all parameters that are needed to invoke a service.
Create a SOAP service request message, which is an XML message that is built according to the SOAP protocol. It contains all the values of all input parameters encoded in XML. This process is called serialization.
Submit the request to a SOAP server using a transport protocol that is supported by the SOAP server.
Receive a SOAP response message.
Determine the success or failure of the request by handling the SOAP Fault element.
Convert the returned parameter from XML to native datatype. This process is called deserialization.
Use the result as needed.
A SOAP server performs the following steps when executing a SOAP service request:
The SOAP server receives the service request.
The server parses the XML request and then decides whether to execute or reject the message.
If the message is executed, then the server determines whether the requested service exists.
The server converts all input parameters from XML into datatypes that the service understands.
The server invokes the service.
The server converts the return parameter to XML and generates a SOAP response message.
The server sends the response message back to the caller.
The SOAP C implementation uses the xml.h header. A context of type xmlctx
must be created before a SOAP context can be created.
HTTP aspects of SOAP are hidden from the user. SOAP endpoints are specified as a couple (binding, endpoint) where binding is of type xmlsoapbind
and the endpoint is a (void *
) depending on the binding. Currently, only one binding is supported, XMLSOAP_BIND_HTTP
. For HTTP binding, the endpoint is an (OraText *
) URL.
The SOAP layer creates and transports SOAP messages between endpoints, and decomposes received SOAP messages.
The C functions are declared in xmlsoap.h
. Here is the beginning of that header file:
Example 18-3 SOAP C Functions Defined in xmlsoap.h
FILE NAME xmlsoap.h - XML SOAP APIs FILE DESCRIPTION XML SOAP Public APIs PUBLIC FUNCTIONS XmlSoapCreateCtx - Create and return a SOAP context XmlSoapDestroyCtx - Destroy a SOAP context XmlSoapCreateConnection - Create a SOAP connection object XmlSoapDestroyConnection - Destroy a SOAP connection object XmlSoapCall - Send a SOAP message & wait for reply XmlSoapCreateMsg - Create and return an empty SOAP message XmlSoapDestroyMsg - Destroy a SOAP message created w/XmlSoapCreateMsg XmlSoapGetEnvelope - Return a SOAP message's envelope XmlSoapGetHeader - Return a SOAP message's envelope header XmlSoapGetBody - Return a SOAP message's envelope body XmlSoapAddHeaderElement - Adds an element to a SOAP header XmlSoapGetHeaderElement - Gets an element from a SOAP header XmlSoapAddBodyElement - Adds an element to a SOAP message body XmlSoapGetBodyElement - Gets an element from a SOAP message body XmlSoapSetMustUnderstand - Set mustUnderstand attr for SOAP hdr elem XmlSoapGetMustUnderstand - Get mustUnderstand attr from SOAP hdr elem XmlSoapSetRole - Set role for SOAP header element XmlSoapGetRole - Get role from SOAP header element XmlSoapSetRelay - Set relay Header element property XmlSoapGetRelay - Get relay Header element property XmlSoapSetFault - Set Fault in SOAP message XmlSoapHasFault - Does SOAP message have a Fault? XmlSoapGetFault - Return Fault code, reason, and details XmlSoapAddFaultReason - Add additional Reason to Fault XmlSoapAddFaultSubDetail - Add additional child to Fault Detail XmlSoapGetReasonNum - Get number of Reasons in Fault element XmlSoapGetReasonLang - Get a lang of a reasons with a particular iindex. XmlSoapError - Get error message(s) */ #ifndef XMLSOAP_ORACLE # define XMLSOAP_ORACLE # ifndef XML_ORACLE # include <xml.h> # endif /*--------------------------------------------------------------------------- Package SOAP - Simple Object Access Protocol APIs W3C: "SOAP is a lightweight protocol for exchange of information in a decentralized, distributed environment. It is an XML based protocol that consists of three parts: an envelope that defines a framework for describing what is in a message and how to process it, a set of encoding rules for expressing instances of application-defined datatypes, and a convention for representing remote procedure calls and responses." Atachments are only allowed in Soap 1.1 In Soap 1.2 body may not have other elements if Fault is present. Structure of a SOAP message: [SOAP message (XML document) [SOAP envelope [SOAP header? element* ] [SOAP body (element* | Fault)? ] ] ] ---------------------------------------------------------------------------*/ ...
Here is an XML document that illustrates a request to a travel company for a reservation on a plane flight from New York to Los Angeles for John Smith:
Example 18-4 Example 1 SOAP Message
<?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference> <m:dateAndTime>2001-11-29T13:20:00.000-05:00</m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>John Smith</n:name> </n:passenger> </env:Header> <env:Body> <p:itinerary xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing>New York</p:departing> <p:arriving>Los Angeles</p:arriving> <p:departureDate>2001-12-14</p:departureDate> <p:departureTime>late afternoon</p:departureTime> <p:seatPreference>aisle</p:seatPreference> </p:departure> <p:return> <p:departing>Los Angeles</p:departing> <p:arriving>New York</p:arriving> <p:departureDate>2001-12-20</p:departureDate> <p:departureTime>mid-morning</p:departureTime> <p:seatPreference/> </p:return> </p:itinerary> <q:lodging xmlns:q="http://travelcompany.example.org/reservation/hotels"> <q:preference>none</q:preference> </q:lodging> </env:Body> </env:Envelope>
The example used to create the XML document, send it, and receive and decompose a reply is simplified. There is some minimal error checking. The DEBUG
option is shown for correcting anomalies. The program may not work on all operating systems. To send this XML document, the first client C program follows these steps:
After declaring variables in main()
, an XML context, xctx
, is created using XmlCreate()
and the context is then used to create a SOAP context, ctx
, using XmlSoapCreateCtx()
.
To construct the message, XmlSoapCreateMsg()
is called and returns an empty SOAP message.
The header is constructed using XmlSoapAddHeaderElement()
, XmlSoapSetRole()
, XmlSoapSetMustUnderstand()
, and XmlDomAddTextElem()
to fill in the envelope with text.
The body elements are created by XmlSoapAddBodyElement()
, XmlDomCreateElemNS()
, and a series of calls to XmlDomAddTextElem()
. Then XmlDomAppendChild()
completes the section of the body specifying the New York to Los Angeles flight.
The return flight is built in an analogous way. The lodging is added with another XmlSoapAddBodyElement()
call.
The connection must be created next with XmlSoapCreateConnection()
, specifying HTTP binding (the only binding available now) and an endpoint URL.
The function XmlSoapCall()
sends the message over the defined connection by means of the SOAP server, and then waits for the reply.
The message reply is returned in the form of another SOAP message. This is done with XmlSaveDom()
and XmlSoapHasFault()
used with XmlSoapGetFault()
to check for a fault and analyze the fault. The fault is parsed into its parts, which is output in this example.
If there was no fault returned, this is followed by XmlSoapGetBody()
to return the envelope body. XmlSaveDom()
completes the analysis of the returned message.
To clean up, use XmlSoapDestroyMsg()
on the message and on the reply, XmlDestroyCtx()
to destroy the SOAP context, and XmlDestroy()
to destroy the XML context.
The C client program for Example 1 is:
Example 18-5 Example 1 SOAP C Client
#ifndef S_ORACLE # include <s.h> #endif #ifndef XML_ORACLE # include <xml.h> #endif #ifndef XMLSOAP_ORACLE # include <xmlsoap.h> #endif #define MY_URL "http://my_url.com" /* static function declaration */ static xmlerr add_ns_decl(xmlsoapctx *ctx, xmlctx *xctx, xmlelemnode *elem, oratext *pfx, oratext *uri); sb4 main( sword argc, char *argv[]) { xmlctx *xctx; xmlerr xerr; xmlsoapctx *ctx; oratext *url; xmlsoapcon *con; xmldocnode *msg1, *reply, *msg2, *msg3; xmlelemnode *res, *pas, *pref, *itin, *departure, *ret, *lodging; xmlelemnode *departing, *arriving, *trans, *text, *charge, *card, *name; xmlelemnode *body, *header; boolean has_fault; oratext *code, *reason, *lang, *node, *role; xmlelemnode *detail; oratext *comp_uri = "http://travelcompany.example.org/"; oratext *mres_uri = "http://travelcompany.example.org/reservation"; oratext *trav_uri = "http://travelcompany.example.org/reservation/travel"; oratext *hotel_uri = "http://travelcompany.example.org/reservation/hotels"; oratext *npas_uri = "http://mycompany.example.com/employees"; oratext *tparty_uri = "http://thirdparty.example.org/transaction"; oratext *estyle_uri = "http://example.com/encoding"; oratext *soap_style_uri = "http://www.w3.org/2003/05/soap-encoding"; oratext *estyle = "env:encodingStyle"; oratext *finance_uri = "http://mycompany.example.com/financial"; if (!(xctx = XmlCreate(&xerr, (oratext *)"SOAP_test",NULL))) { printf("Failed to create XML context, error %u\n", (unsigned) xerr); return EX_FAIL; } /* Create SOAP context */ if (!(ctx = XmlSoapCreateCtx(xctx, &xerr, (oratext *) "example", NULL))) { printf("Failed to create SOAP context, error %u\n", (unsigned) xerr); return EX_FAIL; } /* EXAMPLE 1 */ /* construct message */ if (!(msg1 = XmlSoapCreateMsg(ctx, &xerr))) { printf("Failed to create SOAP message, error %u\n", (unsigned) xerr); return xerr; } res = XmlSoapAddHeaderElement(ctx, msg1, "m:reservation", mres_uri, &xerr); xerr = XmlSoapSetRole(ctx, res, XMLSOAP_ROLE_NEXT); xerr = XmlSoapSetMustUnderstand(ctx, res, TRUE); (void) XmlDomAddTextElem(xctx, res, mres_uri, "m:reference", "uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d"); (void) XmlDomAddTextElem(xctx, res, mres_uri, "m:dateAndTime", "2001-11-29T13:20:00.000-05:00"); pas = XmlSoapAddHeaderElement(ctx, msg1, "n:passenger", npas_uri, &xerr); xerr = XmlSoapSetRole(ctx, pas, XMLSOAP_ROLE_NEXT); xerr = XmlSoapSetMustUnderstand(ctx, pas, TRUE); (void) XmlDomAddTextElem(xctx, pas, npas_uri, "n:name", "John Smith"); /* Fill body */ /* Itinerary */ itin = XmlSoapAddBodyElement(ctx, msg1, "p:itinerary", trav_uri, &xerr); /* Departure */ departure = XmlDomCreateElemNS(xctx, msg1, trav_uri, "p:departure"); (void) XmlDomAddTextElem(xctx, departure, trav_uri, "p:departing","New York"); (void) XmlDomAddTextElem(xctx, departure, trav_uri, "p:arriving", "Los Angeles"); (void) XmlDomAddTextElem(xctx, departure, trav_uri, "p:departureDate", "2001-12-14"); (void) XmlDomAddTextElem(xctx, departure, trav_uri, "p:departureTime", "late afternoon"); (void) XmlDomAddTextElem(xctx, departure, trav_uri, "p:seatPreference", "aisle"); XmlDomAppendChild(xctx, itin, departure); /* Return */ ret = XmlDomCreateElemNS(xctx, msg1, trav_uri, "p:return"); (void) XmlDomAddTextElem(xctx, ret, trav_uri, "p:departing", "Los Angeles"); (void) XmlDomAddTextElem(xctx, ret, trav_uri, "p:arriving", "New York"); (void) XmlDomAddTextElem(xctx, ret, trav_uri, "p:departureDate", "2001-12-20"); (void) XmlDomAddTextElem(xctx, ret, trav_uri, "p:departureTime", "mid-morning"); pref = XmlDomCreateElemNS(xctx, msg1, trav_uri, "p:seatPreference"); (void) XmlDomAppendChild(xctx, ret, pref); XmlDomAppendChild(xctx, itin, ret); /* Lodging */ lodging = XmlSoapAddBodyElement(ctx, msg1, "q:lodging", hotel_uri, &xerr); (void) XmlDomAddTextElem(xctx, lodging, hotel_uri, "q:preference", "none"); #ifdef DEBUG /* dump the message in debug mode */ printf("Message:\n"); XmlSaveDom(xctx, &xerr, msg1, "stdio", stdout, "indent_step", 1, NULL); #endif /* END OF EXAMPLE 1 */ /* create connection */ url = MY_URL; if (!(con = XmlSoapCreateConnection(ctx, &xerr, XMLSOAP_BIND_HTTP, url, NULL, 0, NULL, 0, "XTest: baz", NULL))) { printf("Failed to create SOAP connection, error %u\n", (unsigned) xerr); return xerr; } reply = XmlSoapCall(ctx, con, msg1, &xerr); XmlSoapDestroyConnection(ctx, con); if (!reply) { printf("Call failed, no message returned.\n"); return xerr; } #ifdef DEBUG printf("Reply:\n"); XmlSaveDom(xctx, &xerr, reply, "stdio", stdout, NULL); #endif printf("\n==== Header:\n "); header = XmlSoapGetHeader(ctx, reply, &xerr); if (!header) { printf("NULL\n"); } else XmlSaveDom(xctx, &xerr, header, "stdio", stdout, NULL); /* check for fault */ has_fault = XmlSoapHasFault(ctx, reply, &xerr); if(has_fault) { lang = NULL; xerr = XmlSoapGetFault(ctx, reply, &code, &reason, &lang, &node, &role, &detail); if (xerr) { printf("error getting Fault %d\n", xerr); return EX_FAIL; } if(code) printf(" Code -- %s\n", code); else printf(" NO Code\n"); if(reason) printf(" Reason -- %s\n", reason); else printf(" NO Reason\n"); if(lang) printf(" Lang -- %s\n", lang); else printf(" NO Lang\n"); if(node) printf(" Node -- %s\n", node); else printf(" NO Node\n"); if(role) printf(" Role -- %s\n", role); else printf(" NO Role\n"); if(detail) { printf(" Detail\n"); XmlSaveDom(xctx, &xerr, detail, "stdio", stdout, NULL); printf("\n"); } else printf(" NO Detail\n"); } else { body = XmlSoapGetBody(ctx, reply, &xerr); printf("==== Body:\n "); if (!body) { printf("NULL\n"); return EX_FAIL; } XmlSaveDom(xctx, &xerr, body, "stdio", stdout, NULL); } (void) XmlSoapDestroyMsg(ctx, reply); (void) XmlSoapDestroyMsg(ctx, msg1); (void) XmlSoapDestroyCtx(ctx); XmlDestroy(xctx); }
The travel company wants to know which airport in New York the traveller, John Smith, will depart from. The choices are JFK for Kennedy, EWR for Newark, or LGA for LaGuardia. So the following reply is sent:
Example 18-6 Example 2 SOAP Message
<?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference> <m:dateAndTime>2001-11-29T13:35:00.000-05:00</m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>John Smith</n:name> </n:passenger> </env:Header> <env:Body> <p:itineraryClarification xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing> <p:airportChoices> JFK LGA EWR </p:airportChoices> </p:departing> </p:departure> <p:return> <p:arriving> <p:airportChoices> JFK LGA EWR </p:airportChoices> </p:arriving> </p:return> </p:itineraryClarification> </env:Body> </env:Envelope>
To send this XML document as a SOAP message, substitute the following code block for the lines beginning with /* EXAMPLE 1 */
and ending with /* END OF EXAMPLE 1 */
in Example 18-5, "Example 1 SOAP C Client":
Example 18-7 Example 2 SOAP C Client
#define XMLSOAP_MAX_NAME 1024 /* we need this function for examples 2 and 3 */ static xmlerr add_ns_decl(xmlsoapctx *ctx, xmlctx *xctx, xmlelemnode *elem, oratext *pfx, oratext *uri) { oratext *aq, aqbuf[XMLSOAP_MAX_NAME]; xmldocnode *doc; oratext *xmlns = "xmlns:"; /* if no room for "xmlns:usersprefix\0" then fail now */ if ((strlen((char *)pfx) + strlen((char *)xmlns)) > sizeof(aqbuf)) return EX_FAIL; (void) strcpy((char *)aqbuf, (char *)xmlns); strcat((char *)aqbuf, (char *)pfx); doc = XmlDomGetOwnerDocument(xctx, elem); aq = XmlDomSaveString(xctx, doc, aqbuf); XmlDomSetAttrNS(xctx, elem, uri, aq, uri); return XMLERR_OK; } /* EXAMPLE 2 */ /* construct message */ if (!(msg2 = XmlSoapCreateMsg(ctx, &xerr))) { printf("Failed to create SOAP message, error %u\n", (unsigned) xerr); return xerr; } res = XmlSoapAddHeaderElement(ctx, msg2, "m:reservation", mres_uri, &xerr); xerr = XmlSoapSetRole(ctx, res, XMLSOAP_ROLE_NEXT); xerr = XmlSoapSetMustUnderstand(ctx, res, TRUE); (void) XmlDomAddTextElem(xctx, res, mres_uri, "m:reference", "uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d"); (void) XmlDomAddTextElem(xctx, res, mres_uri, "m:dateAndTime", "2001-11-29T13:35:00.000-05:00"); pas = XmlSoapAddHeaderElement(ctx, msg2, "n:passenger", npas_uri, &xerr); xerr = XmlSoapSetRole(ctx, pas, XMLSOAP_ROLE_NEXT); xerr = XmlSoapSetMustUnderstand(ctx, pas, TRUE); (void) XmlDomAddTextElem(xctx, pas, npas_uri, "n:name", "John Smith"); /* Fill body */ /* Itinerary */ itin = XmlSoapAddBodyElement(ctx, msg2, "p:itineraryClarification", trav_uri, &xerr); /* Departure */ departure = XmlDomCreateElemNS(xctx, msg2, trav_uri, "p:departure"); departing = XmlDomCreateElem(xctx, msg2, "p:departing"); (void) XmlDomAddTextElem(xctx, departing, trav_uri, "p:airportChoices", "JFK LGA EWR"); (void) XmlDomAppendChild(xctx, departure, departing); XmlDomAppendChild(xctx, itin, departure); /* Return */ ret = XmlDomCreateElemNS(xctx, msg2, trav_uri, "p:return"); arriving = XmlDomCreateElemNS(xctx, msg2, trav_uri, "p:arriving"); (void) XmlDomAddTextElem(xctx, arriving, trav_uri, "p:airportChoices", "JFK LGA EWR"); XmlDomAppendChild(xctx, ret, arriving); XmlDomAppendChild(xctx, itin, ret); #ifdef DEBUG XmlSaveDom(xctx, &xerr, msg2, "stdio", stdout, "indent_step", 1, NULL); #endif
Credit card information for John Smith is sent in the final XML document using the POST method. The XmlSoapCall()
writes the HTTP header that precedes the XML message in the following example:
Example 18-8 Example 3 SOAP Message
POST /Reservations HTTP/1.0 Host: travelcompany.example.org Content-Type: application/soap+xml; charset="utf-8" Content-Length: nnnn <?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" > <env:Header> <t:transaction xmlns:t="http://thirdparty.example.org/transaction" env:encodingStyle="http://example.com/encoding" env:mustUnderstand="true" >5</t:transaction> </env:Header> <env:Body> <m:chargeReservation env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" xmlns:m="http://travelcompany.example.org/"> <m:reservation xmlns:m="http://travelcompany.example.org/reservation"> <m:code>FT35ZBQ</m:code> </m:reservation> <o:creditCard xmlns:o="http://mycompany.example.com/financial"> <n:name xmlns:n="http://mycompany.example.com/employees"> John Smith </n:name> <o:number>123456789099999</o:number> <o:expiration>2005-02</o:expiration> </o:creditCard> </m:chargeReservation> </env:Body> </env:Envelope>
The C Client includes the following code block which is substituted like the second example in Example 18-5, "Example 1 SOAP C Client":
Example 18-9 Example 3 SOAP C Client
#define XMLSOAP_MAX_NAME 1024 /* we need this function for examples 2 and 3 */ static xmlerr add_ns_decl(xmlsoapctx *ctx, xmlctx *xctx, xmlelemnode *elem, oratext *pfx, oratext *uri) { oratext *aq, aqbuf[XMLSOAP_MAX_NAME]; xmldocnode *doc; oratext *xmlns = "xmlns:"; /* if no room for "xmlns:usersprefix\0" then fail now */ if ((strlen((char *)pfx) + strlen((char *)xmlns)) > sizeof(aqbuf)) return EX_FAIL; (void) strcpy((char *)aqbuf, (char *)xmlns); strcat((char *)aqbuf, (char *)pfx); doc = XmlDomGetOwnerDocument(xctx, elem); aq = XmlDomSaveString(xctx, doc, aqbuf); XmlDomSetAttrNS(xctx, elem, uri, aq, uri); return XMLERR_OK; } /* EXAMPLE 3 */ if (!(msg3 = XmlSoapCreateMsg(ctx, &xerr))) { printf("Failed to create SOAP message, error %u\n", (unsigned) xerr); return xerr; } trans = XmlSoapAddHeaderElement(ctx,msg3, "t:transaction", tparty_uri, &xerr); xerr = XmlSoapSetMustUnderstand(ctx, trans, TRUE); XmlDomSetAttr(xctx, trans, estyle, estyle_uri); text = XmlDomCreateText(xctx, msg3, "5"); XmlDomAppendChild(xctx, trans, text); /* Fill body */ /* Charge Reservation */ charge = XmlSoapAddBodyElement(ctx,msg3,"m:chargeReservation",comp_uri,&xerr); XmlDomSetAttr(xctx, charge, estyle, soap_style_uri); res = XmlDomCreateElemNS(xctx, msg3, mres_uri, "m:reservation"); if (add_ns_decl(ctx, xctx, res, "m", mres_uri)) return EX_FAIL; (void) XmlDomAddTextElem(xctx, res, mres_uri, "m:code", "FT35ZBQ"); (void) XmlDomAppendChild(xctx, charge, res); /* create card elem with namespace */ card = XmlDomCreateElemNS(xctx, msg3, finance_uri, "o:creditCard"); if (add_ns_decl(ctx, xctx, card, "o", finance_uri)) return EX_FAIL; name = XmlDomAddTextElem(xctx, card, npas_uri, "n:name", "John Smith"); /* add namespace */ if (add_ns_decl(ctx, xctx, name, "n", npas_uri)) return EX_FAIL; (void) XmlDomAddTextElem(xctx, card, finance_uri, "o:number", "123456789099999"); (void) XmlDomAddTextElem(xctx, card, finance_uri, "o:expiration", "2005-02"); (void) XmlDomAppendChild(xctx, charge, card); #ifdef DEBUG XmlSaveDom(xctx, &xerr, msg3, "stdio", stdout, "indent_step", 1, NULL); #endif