在现代应用架构中,数据库常常需要与外部Web服务进行交互,例如,在数据变更后调用外部API通知下游系统,或是在PL/SQL程序中获取外部数据来丰富业务逻辑。PolarDB PostgreSQL版(兼容Oracle)提供的UTL_HTTP
内置包正是为了解决这一场景。它使您能够直接在PL/SQL代码中发起HTTP/HTTPS请求,从而实现数据库与外部Web服务的无缝集成。
使用UTL_HTTP
内置包需要配置网络通道,目前暂不支持您自行创建,如有相关需求,请提交工单联系我们处理。
功能简介
UTL_HTTP
是一个高度兼容Oracle的内置工具包,提供了一整套用于在PL/SQL环境中创建、发送和解析HTTP请求的函数和过程。它不仅支持GET、POST等标准HTTP方法,还涵盖了代理、SSL/TLS(通过钱包)、Cookie管理、认证等高级功能。
工作流程
使用UTL_HTTP
发起一个完整的HTTP请求通常遵循以下状态化步骤:
开始请求:使用
UTL_HTTP.BEGIN_REQUEST
函数初始化一个请求,并获取一个请求句柄(req
类型)。设置请求头:调用
UTL_HTTP.SET_HEADER
为请求添加必要的HTTP头部信息,如Content-Type
。(可选)写入请求体:对于POST或PUT等请求,使用
UTL_HTTP.WRITE_TEXT
或UTL_HTTP.WRITE_RAW
写入请求体数据。获取响应:使用
UTL_HTTP.GET_RESPONSE
函数发送请求并获取一个响应句柄(resp
类型)。读取响应体:循环调用
UTL_HTTP.READ_TEXT
或UTL_HTTP.READ_LINE
从响应句柄中读取返回的数据。结束响应:调用
UTL_HTTP.END_RESPONSE
关闭响应句柄。
核心组件
UTL_HTTP
包主要由以下几部分组成,共同支撑HTTP通信:
请求与响应记录:
UTL_HTTP.REQ
:代表一个HTTP请求的句柄,包含了URL、方法等信息。UTL_HTTP.RESP
:代表一个HTTP响应的句柄,包含了状态码、原因短语等信息。
核心函数与过程:用于构建请求、处理响应和配置会话行为,如
BEGIN_REQUEST
、SET_HEADER
、GET_RESPONSE
、READ_TEXT
等。常量:预定义了大量的HTTP协议常量,如协议版本(
HTTP_VERSION_1_1
)和状态码(HTTP_OK
、HTTP_NOT_FOUND
等),使代码更具可读性。异常:定义了一套完整的异常体系,用于捕获和处理网络错误、协议错误或超时等问题,如
REQUEST_FAILED
、TRANSFER_TIMEOUT
等。
前提条件
您的PolarDB PostgreSQL版(兼容Oracle)集群的修订版本需为2.0.14.17.36.0及以上
优势
数据库原生集成:无需借助外部程序或中间件,直接在数据库层实现与外部Web服务的通信,简化了系统架构。
支持丰富场景:可用于数据同步、消息推送、调用第三方服务(如天气、汇率API)、实现数据库触发的Webhook等多种业务场景。
功能全面且兼容:提供了与Oracle
UTL_HTTP
高度兼容的接口,支持HTTPS、代理、认证等企业级应用所需的高级特性,便于应用迁移。
注意事项
HTTPS请求:要发起HTTPS请求,需先通过
UTL_HTTP.SET_WALLET
配置Oracle钱包(Wallet),用于管理SSL/TLS证书。资源管理:每次请求结束后,需调用
UTL_HTTP.END_RESPONSE
(以及在更复杂的场景中调用END_REQUEST
)来关闭请求和响应句柄,避免资源泄漏。超时设置:网络环境不稳定或目标服务响应较慢时,建议使用
UTL_HTTP.SET_TRANSFER_TIMEOUT
设置合理的传输超时时间,以防程序长时间阻塞。
数据类型定义
req记录类型
表示一个HTTP请求的句柄(handle)。它在调用BEGIN_REQUEST
时被创建,并作为后续请求相关过程(如SET_HEADER
、WRITE_TEXT
)的输入参数。
TYPE req IS RECORD (
url VARCHAR2(32767),
method VARCHAR2(64),
http_version VARCHAR2(64),
private_hndl INTEGER
);
字段说明
url
:请求的目标URL。method
:请求使用的HTTP方法(如GET、POST)。http_version
:请求使用的HTTP协议版本。private_hndl
:供包内部使用的句柄标识,请忽略。
resp记录类型
表示一个HTTP响应的句柄。它在调用GET_RESPONSE
后返回,并作为后续响应相关过程(如READ_TEXT
)的输入参数。
TYPE resp IS RECORD (
status_code INTEGER,
reason_phrase VARCHAR2(256),
http_version VARCHAR2(64),
private_hndl INTEGER
);
字段说明
status_code
:HTTP响应的状态码,例如200
或404
。reason_phrase
:对状态码的文本描述,例如OK或Not Found。http_version
:HTTP响应中返回的协议版本。private_hndl
:供包内部使用的句柄标识,请忽略。
cookie记录类型
用于表示和管理一个HTTP Cookie的详细信息。
TYPE cookie IS RECORD (
name VARCHAR2(4096),
value VARCHAR2(4096),
domain VARCHAR2(256),
expire TIMESTAMP WITH TIME ZONE,
path VARCHAR2(1024),
secure BOOLEAN,
version INTEGER,
comment VARCHAR2(1024)
);
字段说明
name
:Cookie的名称。value
:Cookie的值。domain
:该Cookie适用的域名。expire
:Cookie的过期时间。path
:该Cookie适用的路径。secure
:如果为TRUE
,表示该Cookie仅通过安全的HTTPS连接发送。version
:Cookie规范的版本。comment
:与Cookie相关的注释信息。
其他数据类型
html_pieces
:VARCHAR2表类型,用于返回HTML片段。cookie_table
:Cookie表类型。connection
:表示一个持久化连接类型。connection_table
:用于管理持久连接表类型。request_context_key
:用于标识请求上下文。
函数与过程定义
全局配置函数
SET_BODY_CHARSET
设置后续所有请求的请求体的默认字符集。
PROCEDURE set_body_charset(charset IN VARCHAR2 DEFAULT NULL);
参数说明
charset
:字符集名称。如果为NULL
,则使用数据库的默认字符集。
SET_TRANSFER_TIMEOUT
设置后续所有请求的网络传输超时时间。
PROCEDURE set_transfer_timeout(timeout IN INTEGER DEFAULT 60);
参数说明
timeout
:超时时间,单位为秒。默认值为60。
SET_WALLET
设置用于HTTPS连接的Oracle钱包路径和密码。
PROCEDURE set_wallet(path IN VARCHAR2, password IN VARCHAR2 DEFAULT NULL);
参数说明
path
:钱包的目录路径,格式为"file:directory-path"
。password
:钱包的访问密码。如果为NULL
,则以只读模式打开钱包。
SET_PROXY
设置用于后续所有请求的代理服务器。
PROCEDURE set_proxy(proxy IN VARCHAR2, no_proxy_domains IN VARCHAR2 DEFAULT NULL);
参数说明
proxy
:代理服务器地址,格式为"[http://]host[:port][/]"
。no_proxy_domains
:一个以逗号、分号或空格分隔的域名列表,访问这些域名时将不使用代理。
请求处理函数
BEGIN_REQUEST
开始一个HTTP请求会话,并返回一个请求句柄
FUNCTION begin_request(url IN VARCHAR2,
method IN VARCHAR2 DEFAULT 'GET',
http_version IN VARCHAR2 DEFAULT NULL,
request_context IN INTEGER DEFAULT NULL,
https_host IN VARCHAR2 DEFAULT NULL)
RETURN req;
参数说明
url
:要请求的目标URL。method
:HTTP方法,默认为'GET'。http_version
:HTTP协议版本,默认为NULL
,表示使用最新支持的版本。request_context
:请求上下文,用于包含私有钱包和Cookie表信息。https_host
:当通过HTTPS代理访问时,指定目标主机的真实名称,用于SSL/TLS证书验证。
返回值
UTL_HTTP.req
:一个代表此HTTP请求的记录类型句柄。
SET_HEADER
为指定的HTTP请求设置一个头部信息。
PROCEDURE set_header(r IN OUT req,
name IN VARCHAR2,
value IN VARCHAR2 DEFAULT NULL);
参数说明
r
:请求句柄。name
:HTTP头的名称。value
:HTTP头的值。如果为NULL
,则删除该头。
SET_BODY_CHARSET(请求级别)
为单个特定请求设置请求体的字符集,覆盖全局设置。
PROCEDURE set_body_charset(r IN OUT req,
charset IN VARCHAR2 DEFAULT NULL);
参数说明
r
:请求句柄。charset
:字符集名称。
SET_TRANSFER_TIMEOUT(请求级别)
为单个特定请求设置网络传输超时时间,覆盖全局设置。
PROCEDURE set_transfer_timeout(r IN OUT req,
timeout IN INTEGER DEFAULT 60);
参数说明
r
:请求句柄。timeout
:超时时间,单位为秒。
SET_AUTHENTICATION
为请求设置HTTP认证信息。
PROCEDURE set_authentication(r IN OUT NOCOPY req,
username IN VARCHAR2,
password IN VARCHAR2 DEFAULT NULL,
scheme IN VARCHAR2 DEFAULT 'Basic',
for_proxy IN BOOLEAN DEFAULT FALSE);
参数说明
r
:请求句柄。username
:认证用户名。password
:认证密码。scheme
:认证方案,如'Basic'、'Digest'、'AWS'等。for_proxy
:如果为TRUE
,则此认证信息用于代理服务器认证。
WRITE_TEXT
向请求体中写入文本数据。
PROCEDURE write_text(r IN OUT req,
data IN VARCHAR2);
参数说明
r
:请求句柄。data
:要写入的文本数据。
WRITE_RAW
向请求体中写入二进制数据。
PROCEDURE write_raw(r IN OUT req,
data IN RAW);
参数说明
r
[IN OUT]UTL_HTTP.req
:请求句柄。data
[IN]RAW
:要写入的二进制数据。
END_REQUEST
结束一个HTTP请求会话,释放与请求句柄相关的资源。
PROCEDURE end_request(r IN OUT req);
参数说明
r
:要关闭的请求句柄。
响应处理函数
GET_RESPONSE
发送已构建的HTTP请求,并获取响应句柄。
FUNCTION get_response(r IN OUT req,
return_info_response BOOLEAN DEFAULT FALSE)
RETURN resp;
参数说明
r
:请求句柄。return_info_response
:是否返回1xx信息性响应,默认为FALSE
。
返回值
UTL_HTTP.resp
:一个代表HTTP响应的记录类型句柄。
READ_TEXT
从响应体中读取一段文本数据。
PROCEDURE read_text(r IN OUT resp,
data OUT VARCHAR2,
len IN INTEGER DEFAULT NULL);
参数说明
r
:响应句柄。data
:用于存放读取到的文本数据的变量。len
:要读取的最大字符数。默认为NULL
,表示尽可能多地读取。
READ_LINE
从响应体中读取一行文本数据。
PROCEDURE read_line(r IN OUT resp,
data OUT VARCHAR2,
remove_crlf IN BOOLEAN DEFAULT FALSE);
参数说明
r
:响应句柄。data
:用于存放读取到的行数据。remove_crlf
:是否移除行尾的换行符(CRLF),默认为FALSE
。
END_RESPONSE
结束响应处理,释放与响应句柄相关的资源。
PROCEDURE end_response(r IN OUT resp);
参数说明
r
:要关闭的响应句柄。
便捷函数
REQUEST函数
发送简单的GET请求并直接返回响应体内容。
FUNCTION request(url IN VARCHAR2) RETURN VARCHAR2;
参数说明
url
:要请求的目标URL。
返回值
VARCHAR2
:HTTP响应的主体内容。
错误处理函数
GET_DETAILED_SQLERRM
获取与UTL_HTTP
相关的最近一次错误的详细信息。
FUNCTION get_detailed_sqlerrm RETURN VARCHAR2;
返回值
VARCHAR2
:详细的错误描述文本。
使用示例
以下示例展示了UTL_HTTP
包的常见用法。
示例1:发送简单的GET请求
这是最基础的用法,用于从一个公开的API获取数据。
DECLARE
l_req UTL_HTTP.req;
l_resp UTL_HTTP.resp;
l_url VARCHAR2(256) := 'https://xxx.com/get';
l_buffer VARCHAR2(32767);
BEGIN
-- 开始GET请求
l_req := UTL_HTTP.begin_request(l_url, 'GET', UTL_HTTP.HTTP_VERSION_1_1);
-- 获取响应
l_resp := UTL_HTTP.get_response(l_req);
-- 循环读取响应体内容
DBMS_OUTPUT.put_line('Response Status: ' || l_resp.status_code);
LOOP
UTL_HTTP.read_text(l_resp, l_buffer, 32767);
DBMS_OUTPUT.put_line(l_buffer);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
-- 读取完成,关闭响应
UTL_HTTP.end_response(l_resp);
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Request Failed: ' || SQLERRM);
-- 确保在异常时也关闭响应
IF l_resp.private_hndl IS NOT NULL THEN
UTL_HTTP.end_response(l_resp);
END IF;
END;
/
示例2:发送带JSON体的POST请求
此示例演示如何向一个测试API发送POST请求,并在请求体中包含JSON数据。
DECLARE
l_req UTL_HTTP.req;
l_resp UTL_HTTP.resp;
l_url VARCHAR2(256) := 'https://xxx.com/post';
l_body VARCHAR2(100) := '{"name":"test", "value":123}';
l_buffer VARCHAR2(32767);
BEGIN
l_req := UTL_HTTP.begin_request(l_url, 'POST', UTL_HTTP.HTTP_VERSION_1_1);
-- 设置请求头
UTL_HTTP.set_header(l_req, 'Content-Type', 'application/json; charset=utf-8');
UTL_HTTP.set_header(l_req, 'Content-Length', LENGTH(l_body));
-- 写入请求体
UTL_HTTP.write_text(l_req, l_body);
-- 获取并处理响应
l_resp := UTL_HTTP.get_response(l_req);
DBMS_OUTPUT.put_line('Response Status: ' || l_resp.status_code);
LOOP
UTL_HTTP.read_text(l_resp, l_buffer, 32767);
DBMS_OUTPUT.put_line(l_buffer);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.end_response(l_resp);
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Request Failed: ' || UTL_HTTP.get_detailed_sqlerrm);
IF l_resp.private_hndl IS NOT NULL THEN
UTL_HTTP.end_response(l_resp);
END IF;
END;
/
附录
常量
分类 | 常量名称(示例) | 描述 |
HTTP协议版本 |
| HTTP/1.0协议版本标识符。 |
| HTTP/1.1协议版本标识符。 | |
默认端口 |
| HTTP协议默认端口号(80)。 |
| HTTPS协议默认端口号(443)。 | |
HTTP状态码 |
| 1xx(信息性)系列,如 |
| 2xx(成功)系列,如 | |
| 3xx(重定向)系列,如 | |
| 4xx(客户端错误)系列,如 | |
| 5xx(服务器错误)系列,如 |
异常
异常名称 | 触发条件 |
| 包初始化失败。 |
| HTTP请求失败,通常由底层网络问题(如DNS解析失败、无法建立连接)引起。 |
| 向 |
| 提供的URL格式不正确。 |
| 违反了HTTP协议规范,例如收到了格式错误的响应。 |
| URL中包含不支持的协议方案(仅支持 |
| 尝试获取一个不存在的HTTP头。 |
| 已到达响应体末尾,但程序仍在尝试读取。 |
| 在错误的请求/响应阶段调用API(例如,在 |
| 收到了3xx系列的重定向响应,但自动重定向未开启或失败。 |
| 收到了4xx系列的客户端错误响应。 |
| 收到了5xx系列的服务器错误响应。 |
| 同时打开的请求或响应句柄数量超过了限制。 |
| 在读取响应体时,一个多字节字符被截断。 |
| 网络传输操作(读或写)超时。 |
| 网络访问被拒绝,通常由网络ACL或防火墙策略导致。 |