Um einen Webservice aufzurufen, werden folgende Schritte durchlaufen:
Das findet sich in PL/SQL in entsprechenden UTL_HTTP-Aufrufen wieder. Ein Standard-Aufruf würde dann in etwa so aussehen:
DECLARE
v_url VARCHAR2 (300);
v_length NUMBER;
v_text VARCHAR2 (2000);
v_request UTL_HTTP.REQ;
v_response UTL_HTTP.RESP;
v_response_text VARCHAR2 (3000);
BEGIN
-- Variablen belegen
/*
v_url := <aufzurufende URL>;
v_text := <Text des Reqest>:
v_length := <Länge des Request-Textes in Bytes>;
*/
v_request :=
UTL_HTTP.begin_request (url => v_url,
method => 'POST', -- oder GET
http_version => UTL_HTTP.HTTP_VERSION_1_1);
UTL_HTTP.set_header (v_request,
'Content-Type',
'text/xml; charset=utf-8');
UTL_HTTP.set_header (v_request,
'Content-Length',
v_length);
UTL_HTTP.write_text (v_request,
v_text);
v_response := UTL_HTTP.get_response ( v_request);
UTL_HTTP.read_text (r => v_response,
data => v_response_text);
UTL_HTTP.end_response ( v_response);
-- <Verarbeitung von v_response_text >
END;
/
Beim Aufbau der Verbindung sind als method GET oder POST erlaubt; was zu wählen ist, hängt vom Webservice ab.Die Längenangaben der Variablen sind dabei natürlich ggf. den erwarteten Längen anzupassen.
Sowohl Request als auch Response werden als VARCHAR2 übermittelt. Wenn der zu übertragende Text nicht über die VARCHAR2-Grenze hinausgeht – was i. d. R. der Fall ist - kann er in einem Schritt übertragen werden, wie oben gezeigt.
Es kann aber vorkommen, dass Request und / oder Response zu lang werden bzw. dass zumindest die Möglichkeit dazu besteht. In diesem Fall muss auf CLOB umgestellt werden, und die Übertragung geschieht per Chunks.
Im Falle des Requests bedeutet das, dass die die Längenangabe im Header entfällt. Stattdessen wird der Gegenseite über den Header mitgeteilt, dass der Request gestückelt gesendet wird:
UTL_HTTP.set_header (v_request, 'Transfer-Encoding', 'chunked');
v_length := DBMS_LOB.getlength(v_text);
v_chunk_length := 2000;
v_offest := 1;
WHILE v_offest < v_length LOOP
DBMS_LOB.read (v_text ,
v_chunk_length,
v_offest,
v_chunk_in);
UTL_HTTP.write_text (v_request, v_chunk_in);
v_offest := v_offest + v_chunk_length;
END LOOP;
Bei der Response könnte die Schleife so aussehen:
DBMS_LOB.createtemporary (v_response_clob, FALSE);
BEGIN
LOOP
UTL_HTTP.read_text (v_response, v_chunk_out, 4000);
DBMS_LOB.writeappend (v_response_clob, LENGTH (v_chunk_out), v_chunk_out);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (v_response);
END;
Wird die Verbindung über einen Proxy hergestellt, so muss dieser ebenfalls bekanntgemacht werden, und zwar vor Aufbau der Verbindung über begin_request:
UTL_HTTP.set_proxy (proxy => <proxy>);
Wird der Webservice über https aufgerufen, ist ein Wallet zwingend erforderlich: Die vollständige Zertifikatshierarchie der Zielseite (mit Ausnahme des Zertifikats der Seite selber – zumindest ab Version 12c) muss im Wallet abgelegt werden. Darauf wird an dieser Stelle nicht näher eingegangen; es ist aber vielfach beschrieben.
Der Speicherort des Wallet muss ebenfalls vor Aufbau der Verbindung mitgeteilt werden:
UTL_HTTP.set_wallet (path => 'file:<vollständiger Pfad zum Wallet>');
Es empfiehlt sich, das Wallet mit auto-login anlegen zu lassen; dann muss das Passwort nicht mitgegeben werden, wie hier gezeigt.
In diversen Projekten mussten für Aufrufe unterschiedliche Arten von Authentifizierung implementiert werden, die hier kurz vorgestellt werden sollen.
Die erste Variante hat keinen Einfluss auf den Aufruf des Webservice: Username / Password (oder auch eine vorher abgeholte Session-Id o. ä.) werden als Bestandteil des Requests mitgeschickt und müssen entsprechend eingearbeitet werden, z. B. in das XML eines SOAP-Calls (nein, SOAP ist noch lange nicht ausgestorben, auch wenn JSON auf dem Vormarsch ist).
In diesem Fall wird ein Authorisierungsstring oder -key über einen Aufruf von UTL_HTTP.set_header übermittelt:
UTL_HTTP.set_header (v_request,
'Authorization',
v_key);
Dies geschieht an gleicher Stelle wie bei den anderen Aufrufen von SET_HEADER auch: Nach begin_request.
Diese speziell innerhalb von Organisationen beliebte Methode ist aus PL/SQL-Sicht die aufwendigste. Hier kommt erneut ein Wallet ins Spiel: Ein Administrator muss auf den Datenbank-Server ein passendes Client-Zertifikat in einem Wallet hinterlegen und für den User freigeben (s. u.).
Dieses Zertifikat muss dann in Form eines „Request Context“ eingebunden werden (in folgendem Fall ist beim Wallet auto-login konfiguriert, deshalb kann das Passwort entfallen):
DECLARE
…
…
v_req_context UTL_HTTP.REQUEST_CONTEXT_KEY;
BEGIN
v_req_context :=
UTL_HTTP.create_request_context (wallet_path => <wallet_mit_Zertifikat>,
wallet_password => NULL);
v_request :=
UTL_HTTP.begin_request (url => v_url,
method => 'POST',
http_version => UTL_HTTP.HTTP_VERSION_1_1,
request_context => v_req_context);
…
UTL_HTTP.DESTROY_REQUEST_CONTEXT (request_context => v_req_context);
UTL_HTTP.end_response ( v_response);
END;
/
Eine Prozedur zum Aufruf eines Webservice, bei der alle oben genannten Erfordernisse zutreffen, könnte dann z. B. so aussehen (wobei bei Authentifizierung über ein Zertifikat innerhalb einer Organisation i. d. R kein Proxy nötig ist):
PROCEDURE send (p_xml IN CLOB,
p_response OUT NOCOPY CLOB)
IS
v_url VARCHAR2 (200);
v_req_context UTL_HTTP.REQUEST_CONTEXT_KEY;
v_request UTL_HTTP.REQ;
v_response UTL_HTTP.RESP;
v_response_text VARCHAR2 (32000);
v_chunk_in VARCHAR2 (2000 CHAR);
v_chunk_out VARCHAR2 (4000 CHAR);
v_chunk_length NUMBER;
v_clob_length NUMBER;
v_offest NUMBER;
v_ret NUMBER;
BEGIN
v_url := '<URL>';
UTL_HTTP.set_wallet (path => '<Pfad zum Wallet für https-Aufruf>');
UTL_HTTP.set_proxy (proxy => <proxy>);
v_req_context :=
UTL_HTTP.create_request_context (wallet_path => '<Pfad zum Wallet mit Zertifikat>',
wallet_password => NULL);
v_request :=
UTL_HTTP.begin_request (url => v_url,
method => 'POST',
http_version => UTL_HTTP.HTTP_VERSION_1_1,
request_context => v_req_context);
UTL_HTTP.set_header (v_request,
'Content-Type',
'text/xml; charset=utf-8');
UTL_HTTP.set_header (v_request,
'Transfer-Encoding',
'chunked');
UTL_HTTP.set_body_charset ('UTF-8');
v_clob_length := DBMS_LOB.getlength ( p_xml);
v_chunk_length := 2000;
v_offest := 1;
WHILE v_offest < v_clob_length LOOP
DBMS_LOB.read (p_xml,
v_chunk_length,
v_offest,
v_chunk_in);
UTL_HTTP.write_text (v_request,
v_chunk_in);
v_offest := v_offest + v_chunk_length;
END LOOP;
v_response := UTL_HTTP.GET_RESPONSE ( v_request);
DBMS_LOB.createtemporary (p_response,
FALSE);
BEGIN
LOOP
UTL_HTTP.read_text (v_response,
v_chunk_out,
4000);
DBMS_LOB.writeappend (p_response,
LENGTH ( v_chunk_out),
v_chunk_out);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response ( v_response);
END;
UTL_HTTP.destroy_request_context ( request_context => v_req_context);
UTL_HTTP.end_response ( v_response);
END send;
In Abhängigkeit vom jeweiligen Webservice können natürlich noch weitere Header-Angaben und / oder sonstige Aufrufe nötig oder zumindest sinnvoll sein. An dieser Stelle sei auf die Oracle-Dokumentation zum Package UTL_HTTP verwiesen.
Seit Oracle Version 11 müssen Zugriffe auf externe Ressourcen freigegeben werden über Access Control Lists ab Version 12c spricht man auch von Access Control Entries.
Im obigen Beispiel können bis zu drei Freigaben nötig sein:
Kontrolle der benötigten Freigaben über das Data Dictionary (als Schema-User des Aufrufs):
select * from user_host_aces -- Hosts, also Punkt 1 und 2
select * from user_wallet_aces -- Wallet-Freigabe
In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den
Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.