UTL_HTTP to Call a Web Service from PL/SQL

April 24, 2015

in Exceptions, PL/SQL, Scripts, UTL packages

UTL_HTTP to Call a Web Service from PL/SQL

You can call a web service from PL/SQL using the Oracle-supplied package UTL_HTTP. Here’s a demo of calling a public web service that returns latest city weather by ZIP.

A PL/SQL script to invoke a web service, followed by its execution…

Call a web service from PL/SQL using UTL_HTTP

PL/SQL script that invokes the web service to get latest weather, then reads the response into a CLOB variable. The response can later be converted to XMLType and the values can be extracted using XMLTable.

declare
  -- String that holds the HTTP request string
  soap_request       varchar2(32767);

  -- PL/SQL record to represent an HTTP request, 
  -- as returned from the call to begin_request
  http_req           utl_http.req;

  -- PL/SQL record to represent the output of
  -- get_response 
  http_resp          utl_http.resp;

  -- HTTP version that can be used in begin_request: 1.0 or 1.1
  t_http_version     varchar2(10) :=  'HTTP/1.1';

  t_content_type     varchar2(50) := 'text/xml; charset=utf-8';

  --  URL of the HTTP request, set after begin_request.
  t_url              varchar2(100) := 'wsf.cdyne.com/WeatherWS/Weather.asmx';

  -- Variables to handle the web service response
  x_clob             CLOB;
  l_buffer           VARCHAR2(32767); 
  
  -- US city ZIP for which the weather data has 
  -- to be fetched via web service
  l_zip              varchar2(10) := '10007'; 

begin

  dbms_output.put_line('ZIP: ' || l_zip );

  -- Build the HTTP soap request
  soap_request := '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetCityWeatherByZIP xmlns="http://ws.cdyne.com/WeatherWS/">
      <ZIP>' || l_zip || '</ZIP>
    </GetCityWeatherByZIP>
  </soap:Body>
</soap:Envelope>';  

  -- Begin a new HTTP request: establish the network 
  -- connection to the target web server or proxy 
  -- server and send the HTTP request line.
  http_req := utl_http.begin_request(t_url
                        , 'POST' -- method
                        , t_http_version);

  -- Set HTTP request header attributes. The 
  -- request header is sent to the web server 
  -- as soon as it is set.
  utl_http.set_header(http_req
    , 'Content-Type', t_content_type);
  utl_http.set_header(http_req
    , 'Content-Length'
    , length(soap_request));
  utl_http.set_header(http_req
    , 'SOAPAction'
    , '"http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP"');

  -- Write soap request text data in the HTTP request
  utl_http.write_text(http_req, soap_request);

  -- get_response output of record type utl_http.resp. 
  -- http_resp contains a 3-digit status_code, a
  -- reason_phrase and HTTP version.
  http_resp := utl_http.get_response(http_req);

  -- Build a CLOB variable to hold web service response
  dbms_lob.createtemporary(x_clob, FALSE );
  dbms_lob.open( x_clob, dbms_lob.lob_readwrite );

  begin

    loop
      -- Copy the web service response body 
      -- in a buffer string variable l_buffer
      utl_http.read_text(http_resp, l_buffer);

      -- Append data from l_buffer to CLOB variable
      dbms_lob.writeappend(x_clob
                        , length(l_buffer)
                        , l_buffer);
    end loop;

  exception
      when others then
        --  Exit loop without exception
        --  when end-of-body is reached
        if sqlcode <> -29266 then
          raise;
        end if;
  end;

  -- Verify the response status and text
  dbms_output.put_line('Response Status: ' 
                   ||http_resp.status_code
                   ||' ' || http_resp.reason_phrase);
  dbms_output.put_line('Response XML:' 
                   || cast(x_clob as varchar2));

  utl_http.end_response(http_resp) ;  
  
  -- x_clob response can now be used for extracting text
  -- values from specific XML nodes, using XMLExtract
end;
/

Execution of PL/SQL Script Using UTL_HTTP

When the above script is run, this is what happens:

SQL> declare
  2    -- String that holds the HTTP request string
  3    soap_request       varchar2(32767);
  4  
  5    -- PL/SQL record to represent an HTTP request,
  6    -- as returned from the call to begin_request
  7    http_req           utl_http.req;
  8  
  9    -- PL/SQL record to represent the output of
 10    -- get_response
 11    http_resp          utl_http.resp;
 12  
 13    -- HTTP version that can be used in begin_request: 1.0 or 1.1
 14    t_http_version     varchar2(10) :=  'HTTP/1.1';
 15  
 16    t_content_type     varchar2(50) := 'text/xml; charset=utf-8';
 17  
 18    --  URL of the HTTP request, set after begin_request.
 19    t_url              varchar2(100) := 'wsf.cdyne.com/WeatherWS/Weather.asmx';
 20  
 21    -- Variables to handle the web service response
 22    x_clob             CLOB;
 23    l_buffer           VARCHAR2(32767);
 24  
 25    -- US city ZIP for which the weather data has
 26    -- to be fetched via web service
 27    l_zip              varchar2(10) := '10007';
 28  
 29  begin
 30  
 31    dbms_output.put_line('ZIP: ' || l_zip );
 32  
 33    -- Build the HTTP soap request
 34    soap_request := '<?xml version="1.0" encoding="utf-8"?>
 35  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 36  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 37    <soap:Body>
 38      <GetCityWeatherByZIP xmlns="http://ws.cdyne.com/WeatherWS/">
 39        <ZIP>' || l_zip || '</ZIP>
 40      </GetCityWeatherByZIP>
 41    </soap:Body>
 42  </soap:Envelope>';
 43  
 44    -- Begin a new HTTP request: establish the network
 45    -- connection to the target web server or proxy
 46    -- server and send the HTTP request line.
 47    http_req := utl_http.begin_request(t_url
 48                          , 'POST' -- method
 49                          , t_http_version);
 50  
 51    -- Set HTTP request header attributes. The
 52    -- request header is sent to the web server
 53    -- as soon as it is set.
 54    utl_http.set_header(http_req
 55      , 'Content-Type', t_content_type);
 56    utl_http.set_header(http_req
 57      , 'Content-Length'
 58      , length(soap_request));
 59    utl_http.set_header(http_req
 60      , 'SOAPAction'
 61      , '"http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP"');
 62  
 63    -- Write soap request text data in the HTTP request
 64    utl_http.write_text(http_req, soap_request);
 65  
 66    -- get_response output of record type utl_http.resp.
 67    -- http_resp contains a 3-digit status_code, a
 68    -- reason_phrase and HTTP version.
 69    http_resp := utl_http.get_response(http_req);
 70  
 71    -- Build a CLOB variable to hold web service response
 72    dbms_lob.createtemporary(x_clob, FALSE );
 73    dbms_lob.open( x_clob, dbms_lob.lob_readwrite );
 74  
 75    begin
 76  
 77      loop
 78        -- Copy the web service response body
 79        -- in a buffer string variable l_buffer
 80        utl_http.read_text(http_resp, l_buffer);
 81  
 82        -- Append data from l_buffer to CLOB variable
 83        dbms_lob.writeappend(x_clob
 84                          , length(l_buffer)
 85                          , l_buffer);
 86      end loop;
 87  
 88    exception
 89        when others then
 90          --  Exit loop without exception
 91          --  when end-of-body is reached
 92          if sqlcode <> -29266 then
 93            raise;
 94          end if;
 95    end;
 96  
 97    -- Verify the response status and text
 98    dbms_output.put_line('Response Status: '
 99                     ||http_resp.status_code
100                     ||' ' || http_resp.reason_phrase);
101    dbms_output.put_line('Response XML:'
102                     || cast(x_clob as varchar2));
103  
104    utl_http.end_response(http_resp) ;
105  
106    -- x_clob response can now be used for extracting text
107    -- values from specific XML nodes, using XMLExtract
108  end;
109  /
ZIP: 10007                                                                      
Response Status: 200 OK                                                         
Response XML:<?xml version="1.0" encoding="utf-8"?><soap:Envelope               
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"                          
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                           
xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><GetCityWeatherByZIPResp
onse                                                                            
xmlns="http://ws.cdyne.com/WeatherWS/"><GetCityWeatherByZIPResult><Success>true<
/Success><ResponseText>City Found</ResponseText><State>NY</State><City>New      
York</City><WeatherStationCity>White                                            
Plains</WeatherStationCity><WeatherID>15</WeatherID><Description>N/A</Descriptio
n><Temperature>63</Temperature><RelativeHumidity>87</RelativeHumidity><Wind>E7</
Wind><Pressure>29.97S</Pressure><Visibility /><WindChill /><Remarks             
/></GetCityWeatherByZIPResult></GetCityWeatherByZIPResponse></soap:Body></soap:E
nvelope>                                                                        

PL/SQL procedure successfully completed.

A response status of 200 OK indicates that the request has succeeded. The response XML shows the weather details for the input ZIP.

Possible Errors in UTL_HTTP Call

A few errors you might encounter when using UTL_HTTP, and their possible causes.

PLS-00201: identifier ‘UTL_HTTP’ must be declared

You will see the above error when the database user (say [utluser]) executing the PL/SQL code does not have execute permission on UTL_HTTP.

To resolve the error, login as SYS or SYSTEM, and provide the execute permission.

grant execute on sys.utl_http to [utluser];

Log back again as [utluser] and run the PL/SQL code again. It should work this time.

ORA-29273: HTTP request failed or ORA-12535: TNS:operation timed out

This suggests a connection error. Are you behind a firewall or is the web service down? From command prompt, ping the server and verify.

You will receive a response of this form if the connectivity is fine:

Pinging ws.cdyne.com [4.59.146.110] with 32 bytes of data:
Reply from 4.59.146.110: bytes=32 time=269ms TTL=120
Reply from 4.59.146.110: bytes=32 time=275ms TTL=120
Reply from 4.59.146.110: bytes=32 time=259ms TTL=120
Reply from 4.59.146.110: bytes=32 time=272ms TTL=120

Ping statistics for 4.59.146.110:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 259ms, Maximum = 275ms, Average = 268ms

You will receive a response of this form if the connectivity is not OK:

[Pinging ws.cdyne.com [4.59.146.110] with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.

Ping statistics for 87.106.3.248:
    Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),

In the second case, test the PL/SQL from outside the firewall or ensure that the web service is up.

ORA-24247: network access denied by access control list (ACL)

This error indicates that an essential piece of security setup – ACL configuration – is missing. Look up Access Control List for details on what ACL is and how to set it up.

For Further Reading

Oracle 10G documentation on UTL_HTTP
PSOUG on UTL_HTTP
Keep the Response Body with non-2xx Responses in UTL_HTTP

{ 5 comments… read them below or add one }

1 Bala Subhramanyam Singumahanti May 21, 2015 at 5:53 pm

Excellent Posting…

2 Maheem May 17, 2017 at 3:23 pm

Thank you best written article on calling web service from PL/SQL I have found on the internet.

3 addy January 17, 2018 at 12:46 pm

thansk man for posting

4 Eugene October 5, 2021 at 12:35 am

Could you please show an example of how to POST .xml file that is larger than varchar2(32767)

5 Madou April 18, 2022 at 5:46 pm

Hi,
I got the following error
ORA-12545: Connect failed because target host or object does not exist

Leave a Comment

Previous post:

Next post: