Oracle データベースから直接 REST API や Web サービスを呼び出したい場面があります。バッチ処理中に外部 API へデータを送信する、Webhook を起動する、外部サービスからデータを取得してテーブルに格納するといった要件です。
UTL_HTTP パッケージは PL/SQL から HTTP/HTTPS リクエストを送信できる Oracle 組み込みパッケージです。GET・POST・PUT・DELETE など各種メソッドに対応し、カスタムヘッダーや認証も設定できます。
この記事でわかること
- UTL_HTTP の基本フロー(BEGIN_REQUEST → SET_HEADER → WRITE_TEXT → GET_RESPONSE → READ_TEXT → END_RESPONSE)
- GET リクエストと POST リクエスト(JSON ボディ)の書き方
- Authorization ヘッダーで Bearer トークン認証を設定する方法
- HTTPS 接続に必要な Oracle ウォレット(WALLET)の設定
- タイムアウト設定と例外処理・END_RESPONSE の正しい後処理
- UTL_HTTP に必要な権限と ACL(アクセス制御リスト)の設定
必要な権限と ACL(アクセス制御リスト)の設定
Oracle 11g 以降、外部ネットワークへのアクセスには ACL(アクセス制御リスト)の設定が必要です。SYS または DBA が DBMS_NETWORK_ACL_ADMIN パッケージで許可ホストを登録します。
ACL でアクセス許可ホストを登録する(Oracle 12c 以降の方法)
-- Oracle 12c 以降の簡略化された ACL 設定
-- DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE で特定ホストへのアクセスを許可する
BEGIN
-- api.example.com への HTTP アクセスを hr_user に許可する
DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
host => 'api.example.com', -- 許可するホスト名(ワイルドカード *.example.com も可)
lower_port => 80, -- 許可するポート範囲(開始)
upper_port => 443, -- 許可するポート範囲(終了)
ace => xs$ace_type(
privilege_list => xs$name_list('connect', 'resolve'),
principal_name => 'HR_USER',
principal_type => xs_acl.ptype_db
)
);
END;
/
-- ACL の設定を確認する
SELECT host, lower_port, upper_port, aclid
FROM DBA_HOST_ACES
WHERE principal = 'HR_USER'
ORDER BY host;
-- UTL_HTTP パッケージへの EXECUTE 権限(通常は PUBLIC に付与済み)
GRANT EXECUTE ON UTL_HTTP TO hr_user;
GET リクエストの基本パターン
UTL_HTTP で GET リクエストを送信する
DECLARE
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
v_buffer VARCHAR2(32767);
v_response CLOB := EMPTY_CLOB();
BEGIN
-- タイムアウトを設定する(接続タイムアウト・読み取りタイムアウト)
UTL_HTTP.SET_TRANSFER_TIMEOUT(30); -- 読み取りタイムアウト: 30 秒
-- UTL_HTTP.SET_CONNECT_TIMEOUT(10); -- 接続タイムアウト(Oracle 12.1.0.2+)
-- リクエストを開始する
v_req := UTL_HTTP.BEGIN_REQUEST(
url => 'https://jsonplaceholder.typicode.com/posts/1',
method => 'GET',
http_version => 'HTTP/1.1'
);
-- リクエストヘッダーを設定する
UTL_HTTP.SET_HEADER(v_req, 'Accept', 'application/json');
UTL_HTTP.SET_HEADER(v_req, 'User-Agent', 'Oracle-UTL_HTTP/12.2');
-- レスポンスを取得する
v_resp := UTL_HTTP.GET_RESPONSE(v_req);
DBMS_OUTPUT.PUT_LINE('HTTP Status: ' || v_resp.status_code || ' ' || v_resp.reason_phrase);
-- レスポンスボディを読み込む(32767バイトずつ)
BEGIN
LOOP
UTL_HTTP.READ_TEXT(v_resp, v_buffer, 32767);
v_response := v_response || v_buffer;
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN NULL; -- ボディ末尾は END_OF_BODY 例外で検出する
END;
DBMS_OUTPUT.PUT_LINE('Response: ' || SUBSTR(v_response, 1, 500));
-- レスポンスを必ず閉じる(ソケットのリークを防ぐ)
UTL_HTTP.END_RESPONSE(v_resp);
EXCEPTION
WHEN UTL_HTTP.TRANSFER_TIMEOUT THEN
UTL_HTTP.END_RESPONSE(v_resp);
DBMS_OUTPUT.PUT_LINE('タイムアウトが発生しました');
WHEN OTHERS THEN
-- 例外発生時もレスポンスを閉じる
BEGIN UTL_HTTP.END_RESPONSE(v_resp); EXCEPTION WHEN OTHERS THEN NULL; END;
RAISE;
END;
/
POST リクエストで JSON ボディを送信する
JSON ボディを持つ POST リクエスト
DECLARE
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
v_buffer VARCHAR2(32767);
v_response VARCHAR2(32767);
v_body VARCHAR2(4000);
v_body_raw RAW(32767);
BEGIN
-- 送信する JSON ボディを組み立てる
v_body := '{"title":"Oracle Test","body":"Posted from PL/SQL","userId":1}';
-- リクエストを開始する
v_req := UTL_HTTP.BEGIN_REQUEST(
url => 'https://jsonplaceholder.typicode.com/posts',
method => 'POST'
);
-- ヘッダーを設定する
UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'application/json');
UTL_HTTP.SET_HEADER(v_req, 'Accept', 'application/json');
-- Content-Length は文字数でなくバイト数を指定する
UTL_HTTP.SET_HEADER(v_req, 'Content-Length',
TO_CHAR(LENGTHB(v_body)));
-- ボディを送信する(文字列として送信)
UTL_HTTP.WRITE_TEXT(v_req, v_body);
-- レスポンスを取得する
v_resp := UTL_HTTP.GET_RESPONSE(v_req);
DBMS_OUTPUT.PUT_LINE('HTTP Status: ' || v_resp.status_code);
BEGIN
LOOP
UTL_HTTP.READ_TEXT(v_resp, v_buffer, 32767);
v_response := v_response || v_buffer;
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN NULL;
END;
DBMS_OUTPUT.PUT_LINE('Response: ' || v_response);
UTL_HTTP.END_RESPONSE(v_resp);
EXCEPTION
WHEN OTHERS THEN
BEGIN UTL_HTTP.END_RESPONSE(v_resp); EXCEPTION WHEN OTHERS THEN NULL; END;
RAISE;
END;
/
Authorization ヘッダーで API 認証する
Bearer トークン認証と Basic 認証の設定
DECLARE
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
v_buffer VARCHAR2(32767);
v_token VARCHAR2(500);
BEGIN
v_token := 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; -- JWT トークン
v_req := UTL_HTTP.BEGIN_REQUEST(
url => 'https://api.example.com/v1/data',
method => 'GET'
);
-- Bearer トークン認証
UTL_HTTP.SET_HEADER(v_req, 'Authorization', 'Bearer ' || v_token);
UTL_HTTP.SET_HEADER(v_req, 'Accept', 'application/json');
v_resp := UTL_HTTP.GET_RESPONSE(v_req);
DBMS_OUTPUT.PUT_LINE('Status: ' || v_resp.status_code);
UTL_HTTP.END_RESPONSE(v_resp);
END;
/
-- Basic 認証の場合(ユーザー名:パスワードを Base64 エンコードする)
DECLARE
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
v_credential VARCHAR2(200);
v_encoded VARCHAR2(200);
BEGIN
v_credential := 'api_user:secret_password';
-- UTL_RAW.CAST_TO_RAW で文字列を RAW に変換してから Base64 エンコードする
v_encoded := UTL_RAW.CAST_TO_VARCHAR2(
UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(v_credential))
);
v_req := UTL_HTTP.BEGIN_REQUEST('https://api.example.com/resource', 'GET');
UTL_HTTP.SET_HEADER(v_req, 'Authorization', 'Basic ' || v_encoded);
v_resp := UTL_HTTP.GET_RESPONSE(v_req);
UTL_HTTP.END_RESPONSE(v_resp);
END;
/
HTTPS 接続と Oracle ウォレットの設定
HTTPS を使用する場合、Oracle は SSL/TLS 証明書の検証にOracle ウォレットを使用します。ウォレットに接続先の CA 証明書(ルート証明書)をインポートしていないと ORA-29024: Certificate validation failure が発生します。
HTTPS 接続時のウォレット設定
-- Oracle ウォレットを使った HTTPS 接続
-- ウォレットパス(orapki コマンドで作成・証明書をインポート済みのもの)
-- $ orapki wallet create -wallet /etc/oracle/wallet -auto_login
-- $ orapki wallet add -wallet /etc/oracle/wallet -trusted_cert -cert /path/to/ca.crt
-- PL/SQL でウォレットを指定して HTTPS に接続する
DECLARE
v_req UTL_HTTP.REQ;
v_resp UTL_HTTP.RESP;
BEGIN
-- ウォレットパスとパスワードを設定する(BEGIN_REQUEST の前に呼ぶ)
UTL_HTTP.SET_WALLET(
path => 'file:/etc/oracle/wallet', -- ウォレットのパス
password => NULL -- auto_login ウォレットは NULL でよい
);
v_req := UTL_HTTP.BEGIN_REQUEST('https://api.example.com/', 'GET');
v_resp := UTL_HTTP.GET_RESPONSE(v_req);
DBMS_OUTPUT.PUT_LINE('Status: ' || v_resp.status_code);
UTL_HTTP.END_RESPONSE(v_resp);
EXCEPTION
WHEN OTHERS THEN
BEGIN UTL_HTTP.END_RESPONSE(v_resp); EXCEPTION WHEN OTHERS THEN NULL; END;
RAISE;
END;
/
-- 証明書検証を無効化する(開発・テスト環境のみ)
-- 本番環境では必ずウォレットに証明書をインポートして検証を有効にすること
EXEC UTL_HTTP.SET_HTTPS_CERTIFICATES(NULL); -- 検証無効(非推奨)
まとめ
- 基本フロー:BEGIN_REQUEST → SET_HEADER → (POST の場合 WRITE_TEXT)→ GET_RESPONSE → READ_TEXT ループ(END_OF_BODY で終了)→ END_RESPONSE
- END_RESPONSE は必須:例外発生時も必ず END_RESPONSE を呼ぶ。呼ばないとソケットがリークしてセッションリソースが枯渇する
- ACL 設定:DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE で接続先ホストを許可リストに登録する。12c 以降は ACL ビューが DBA_HOST_ACES に統合された
- HTTPS:Oracle ウォレットに CA 証明書をインポートして SET_WALLET でパスを指定する。証明書検証の無効化は本番 NG
- タイムアウト:SET_TRANSFER_TIMEOUT で読み取りタイムアウトを必ず設定する。デフォルトは 60 秒
PL/SQL からファイルを操作する UTL_FILE については UTL_FILE完全ガイドを参照してください。DBMS_CRYPTO でデータを暗号化してから送信する場合は DBMS_CRYPTO完全ガイドも参照してください。