In this section, the simple web service that was introduced in Using SOAP services is extended to handle SOAP headers.
If you have followed the steps outlined in the previous section, you can skip steps 1 through 4 and go directly to step 5.
Create a database.
dbinit ftc |
Start a server using this database.
dbsrv11 -xs http(port=8082) -n ftc ftc.db |
Connect to the server using Interactive SQL.
dbisql -c "UID=DBA;PWD=sql;ENG=ftc" |
Using Interactive SQL, create a web service.
CREATE SERVICE FtoCService TYPE 'SOAP' FORMAT 'XML' AUTHORIZATION OFF USER DBA AS CALL FToCConvertor( :temperature ); |
Define the stored procedure that this service is to call to perform the calculation needed to convert from a temperature expressed in degrees Fahrenheit to a temperature expressed in degrees Celsius. Unlike the example in the previous section, this one includes additional statements to process a special SOAP header. If you have already worked through the example in the previous section, change the CREATE below to ALTER since you are now going to modify the stored procedure.
CREATE PROCEDURE FToCConvertor( temperature FLOAT ) BEGIN DECLARE hd_key LONG VARCHAR; DECLARE hd_entry LONG VARCHAR; DECLARE alias LONG VARCHAR; DECLARE first_name LONG VARCHAR; DECLARE last_name LONG VARCHAR; DECLARE xpath LONG VARCHAR; DECLARE authinfo LONG VARCHAR; DECLARE namespace LONG VARCHAR; DECLARE mustUnderstand LONG VARCHAR; header_loop: LOOP SET hd_key = NEXT_SOAP_HEADER( hd_key ); IF hd_key IS NULL THEN -- no more header entries LEAVE header_loop; END IF; IF hd_key = 'Authentication' THEN SET hd_entry = SOAP_HEADER( hd_key ); SET xpath = '/*:' || hd_key || '/*:userName'; SET namespace = SOAP_HEADER( hd_key, 1, '@namespace' ); SET mustUnderstand = SOAP_HEADER( hd_key, 1, 'mustUnderstand' ); BEGIN -- parse the XML returned in the SOAP header DECLARE crsr CURSOR FOR SELECT * FROM OPENXML( hd_entry, xpath ) WITH ( alias LONG VARCHAR '@*:alias', first_name LONG VARCHAR '*:first/text()', last_name LONG VARCHAR '*:last/text()' ); OPEN crsr; FETCH crsr INTO alias, first_name, last_name; CLOSE crsr; END; -- build a response header -- based on the pieces from the request header SET authinfo = XMLELEMENT( 'Authentication', XMLATTRIBUTES( namespace as xmlns, alias, mustUnderstand ), XMLELEMENT( 'first', first_name ), XMLELEMENT( 'last', last_name ) ); CALL SA_SET_SOAP_HEADER( 'authinfo', authinfo ); END IF; END LOOP header_loop; SELECT ROUND((temperature - 32.0) * 5.0 / 9.0, 5) AS answer; END; |
Headers in SOAP requests can be obtained using a combination of the NEXT_SOAP_HEADER and SOAP_HEADER functions. The NEXT_SOAP_HEADER function iterates through the SOAP headers included within a request and returns the next SOAP header name. Calling it with NULL causes it to return the name of the first header. Subsequent headers are retrieved by passing the name of the previous header to the NEXT_SOAP_HEADER function. This function returns NULL when called with the name of the last header. The SQL code that does the SOAP header retrieval in the example is this. It exits the loop when NULL is finally returned.
SET hd_key = NEXT_SOAP_HEADER( hd_key ); IF hd_key IS NULL THEN -- no more header entries LEAVE header_loop; END IF; |
Calling this function repeatedly returns all the header fields exactly once, but not necessarily in the order they appear in the SOAP request.
The SOAP_HEADER function returns the value of the named SOAP header field, or NULL if not called from an SOAP service. It is used when processing an SOAP request via a web service. If a header for the given field-name does not exist, the return value is NULL.
The example searches for a SOAP header named Authentication. When it finds this header, it extracts the value for entire SOAP
header and the values of the @namespace
and mustUnderstand
attributes. The SOAP header value might look something like this XML string:
<Authentication xmlns="SecretAgent" mustUnderstand="1"> <userName alias="99"> <first>Susan</first> <last>Hilton</last> </userName> </Authentication> |
For this header, the @namespace
attribute value would be:SecretAgent
Also, the mustUnderstand
attribute value would be:1
The interior of this XML string is parsed with the OPENXML function using an XPath string set to /*:Authentication/*:userName
.
SELECT * FROM OPENXML( hd_entry, xpath ) WITH ( alias LONG VARCHAR '@*:alias', first_name LONG VARCHAR '*:first/text()', last_name LONG VARCHAR '*:last/text()' ); |
Using the sample SOAP header value shown above, the SELECT statement would create a result set as follows:
alias | first_name | last_name |
99 | Susan | Hilton |
A cursor is declared on this result set and the three column values are fetched into three variables. At this point, you have all the information of interest that was passed to the web service. You have the temperature in Fahrenheit degrees and you have some additional attributes that were passed to the web service in a SOAP header. So what could you do with this information?
You could look up the name and alias that were provided to see if the person is authorized to use the web service. However, this exercise is not shown in the example.
The next step in the stored procedure is to create a response in the SOAP format. You can build the XML response as follows:
SET authinfo = XMLELEMENT( 'Authentication', XMLATTRIBUTES( namespace as xmlns, alias, mustUnderstand ), XMLELEMENT( 'first', first_name ), XMLELEMENT( 'last', last_name ) ); |
This builds the following XML string:
<Authentication xmlns="SecretAgent" alias="99" mustUnderstand="1"> <first>Susan</first> <last>Hilton</last> </Authentication> |
Finally, to return the SOAP response to the caller, the SA_SET_SOAP_HEADER stored procedure is used:
CALL SA_SET_SOAP_HEADER( 'authinfo', authinfo ); |
As in the example in the previous section, the last step is the calculation that converts from degrees Fahrenheit to degrees Celsius.
At this point, you now have a SQL Anywhere web service server running that can convert temperatures from degrees Fahrenheit to degrees Celsius as in the previous section. The major difference, however, is that it can also process a SOAP header from the caller and send a SOAP response back to the caller.
This is only half of the picture. The next step is to develop an example client that can send SOAP requests and receive SOAP responses.
If you have followed the steps outlined in the previous section, you can skip steps 1 through 3 and go directly to step 4.
Create another database for use with a second server.
dbinit ftc_client |
Start the personal server using this database.
dbeng11 ftc_client.db |
Connect to the personal server using another instance of Interactive SQL.
dbisql -c "UID=DBA;PWD=sql;ENG=ftc_client" |
Using Interactive SQL, create a stored procedure.
CREATE PROCEDURE FtoC( temperature FLOAT, INOUT inoutheader LONG VARCHAR, IN inheader LONG VARCHAR ) URL 'http://localhost:8082/FtoCService' TYPE 'SOAP:DOC' SOAPHEADER '!inoutheader!inheader'; |
The URL clause is used to reference the SOAP web service. The string 'http://localhost:8082/FtoCService'
specifies the URI of the web service that is going to be used. This is a reference to the web server that is listening on
port 8082.
The default format used when making a web service request is 'SOAP:RPC'. The format chosen in this example is 'SOAP:DOC', which is similar to 'SOAP:RPC' but allows for a richer set of datatypes. SOAP requests are always sent as XML documents. The mechanism for sending SOAP requests is 'HTTP:POST'.
The substitution variables in a SQL Anywhere client procedure (inoutheader, inheader) must be alpha-numeric. If the web service client is declared as a function, all its parameters are IN mode only (they cannot be assigned by the called function). Therefore, OPENXML or other string functions will have to be used to extract the SOAP response header information.
You need a wrapper for the FtoC stored procedure so create a second stored procedure as follows. Unlike the example in the previous section, this one includes additional statements to create a special SOAP header, send it in a web service call, and process a response from the web server. If you have already worked through the example in the previous section, change the CREATE below to ALTER since you are now going to modify the stored procedure.
CREATE PROCEDURE FahrenheitToCelsius( temperature FLOAT ) BEGIN DECLARE io_header LONG VARCHAR; DECLARE in_header LONG VARCHAR; DECLARE result LONG VARCHAR; DECLARE err INTEGER; DECLARE crsr CURSOR FOR CALL FtoC( temperature, io_header, in_header ); SET io_header = '<Authentication xmlns="SecretAgent" ' || 'mustUnderstand="1">' || '<userName alias="99">' || '<first>Susan</first><last>Hilton</last>' || '</userName>' || '</Authentication>'; SET in_header = '<Session xmlns="SomeSession">' || '123456789' || '</Session>'; MESSAGE 'send, soapheader=' || io_header || in_header; OPEN crsr; FETCH crsr INTO result, err; CLOSE crsr; MESSAGE 'receive, soapheader=' || io_header; SELECT temperature, Celsius FROM OPENXML(result, '//tns:answer', 1, result) WITH ("Celsius" FLOAT 'text()'); END; |
This stored procedure acts as a cover procedure for the call to the web service. The stored procedure has been enhanced from the example in the previous section. It creates two SOAP headers. The first one is this.
<Authentication xmlns="SecretAgent" mustUnderstand="1"> <userName alias="99"> <first>Susan</first> <last>Hilton</last> </userName></Authentication> |
The second one is this.
<Session xmlns="SomeSession">123456789</Session> |
When the cursor is opened, the SOAP request is sent to the web service.
<Authentication xmlns="SecretAgent" alias="99" mustUnderstand="1"> <first>Susan</first> <last>Hilton</last> </Authentication> |
The FtoC stored procedure returns a result set that this stored procedure will process. The result set will look something like this.
<tns:rowset xmlns:tns="http://localhost/ftc/FtoCService"> <tns:row> <tns:answer>100</tns:answer> </tns:row> </tns:rowset> |
The OPENXML function is used to parse the XML that is returned, extracting the value that is the temperature in degrees Celsius.
Call the stored procedure to send the request and obtain the response:
CALL FahrenheitToCelsius(212); |
The Fahrenheit temperature and the Celsius equivalent appears.
temperature | Celsius |
212.0 | 100.0 |
A SQL Anywhere web service client can be declared as either a function or a procedure. A SQL Anywhere client function declaration effectively restricts all parameters to in mode only (parameters cannot be assigned by the called function). Calling a SQL Anywhere web service function will return the raw SOAP envelope response whereas a procedure returns a result set.
A SOAPHEADER clause has been added to the create/alter procedure/function statements. A SOAP header can be declared as a static constant or can be dynamically set using the parameter substitution mechanism. A web service client function can define one or more in mode substitution parameters whereas a web service client procedure can also define a single inout or out substitution parameter. Therefore a web service client procedure can return the response SOAP header within an out (or inout) substitution parameter. A web service function must parse the response SOAP envelope to obtain the header entries.
The following example illustrates how a client can specify the sending of several header entries with parameters and receiving the response SOAP header data.
CREATE PROCEDURE SoapClient( INOUT hd1 VARCHAR, IN hd2 VARCHAR, IN hd3 VARCHAR ) URL 'localhost/some_endpoint' SOAPHEADER '!hd1!hd2!hd3'; |
'Expression has unsupported data type' SQLCODE=-624, ODBC 3 State-"HY000"
Discuss this page in DocCommentXchange. Send feedback about this page using email. |
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |