UTL_HTTP

在现代应用架构中,数据库常常需要与外部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请求通常遵循以下状态化步骤:

  1. 开始请求:使用UTL_HTTP.BEGIN_REQUEST函数初始化一个请求,并获取一个请求句柄(req类型)。

  2. 设置请求头:调用UTL_HTTP.SET_HEADER为请求添加必要的HTTP头部信息,如Content-Type

  3. (可选)写入请求体:对于POSTPUT等请求,使用UTL_HTTP.WRITE_TEXTUTL_HTTP.WRITE_RAW写入请求体数据。

  4. 获取响应:使用UTL_HTTP.GET_RESPONSE函数发送请求并获取一个响应句柄(resp类型)。

  5. 读取响应体:循环调用UTL_HTTP.READ_TEXTUTL_HTTP.READ_LINE从响应句柄中读取返回的数据。

  6. 结束响应:调用UTL_HTTP.END_RESPONSE关闭响应句柄。

核心组件

UTL_HTTP包主要由以下几部分组成,共同支撑HTTP通信:

  • 请求与响应记录

    • UTL_HTTP.REQ:代表一个HTTP请求的句柄,包含了URL、方法等信息。

    • UTL_HTTP.RESP:代表一个HTTP响应的句柄,包含了状态码、原因短语等信息。

  • 核心函数与过程:用于构建请求、处理响应和配置会话行为,如BEGIN_REQUESTSET_HEADERGET_RESPONSEREAD_TEXT等。

  • 常量:预定义了大量的HTTP协议常量,如协议版本(HTTP_VERSION_1_1)和状态码(HTTP_OKHTTP_NOT_FOUND等),使代码更具可读性。

  • 异常:定义了一套完整的异常体系,用于捕获和处理网络错误、协议错误或超时等问题,如REQUEST_FAILEDTRANSFER_TIMEOUT等。

前提条件

您的PolarDB PostgreSQL版(兼容Oracle)集群的修订版本需为2.0.14.17.36.0及以上

说明

您可在控制台查看内核小版本号,也可以通过SHOW polardb_version;语句查看。如未满足内核小版本要求,请升级内核小版本

优势

  • 数据库原生集成:无需借助外部程序或中间件,直接在数据库层实现与外部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_HEADERWRITE_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响应的状态码,例如200404

  • reason_phrase:对状态码的文本描述,例如OKNot 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_VERSION_1_0

HTTP/1.0协议版本标识符。

HTTP_VERSION_1_1

HTTP/1.1协议版本标识符。

默认端口

DEFAULT_HTTP_PORT

HTTP协议默认端口号(80)。

DEFAULT_HTTPS_PORT

HTTPS协议默认端口号(443)。

HTTP状态码

HTTP_CONTINUE

1xx(信息性)系列,如HTTP_CONTINUEHTTP_SWITCHING_PROTOCOLS

HTTP_OK

2xx(成功)系列,如HTTP_OKHTTP_CREATEDHTTP_NO_CONTENT

HTTP_MOVED_PERMANENTLY

3xx(重定向)系列,如HTTP_MOVED_PERMANENTLYHTTP_FOUNDHTTP_NOT_MODIFIED

HTTP_BAD_REQUEST

4xx(客户端错误)系列,如HTTP_BAD_REQUESTHTTP_UNAUTHORIZEDHTTP_NOT_FOUND

HTTP_INTERNAL_SERVER_ERROR

5xx(服务器错误)系列,如HTTP_INTERNAL_SERVER_ERRORHTTP_SERVICE_UNAVAILABLE

异常

异常名称

触发条件

init_failed

包初始化失败。

request_failed

HTTP请求失败,通常由底层网络问题(如DNS解析失败、无法建立连接)引起。

bad_argument

UTL_HTTP的函数或过程传递了无效的参数。

bad_url

提供的URL格式不正确。

protocol_error

违反了HTTP协议规范,例如收到了格式错误的响应。

unknown_scheme

URL中包含不支持的协议方案(仅支持httphttps)。

header_not_found

尝试获取一个不存在的HTTP头。

end_of_body

已到达响应体末尾,但程序仍在尝试读取。

illegal_call

在错误的请求/响应阶段调用API(例如,在BEGIN_REQUEST之前调用SET_HEADER)。

http_redirect_error

收到了3xx系列的重定向响应,但自动重定向未开启或失败。

http_client_error

收到了4xx系列的客户端错误响应。

http_server_error

收到了5xx系列的服务器错误响应。

too_many_requests

同时打开的请求或响应句柄数量超过了限制。

partial_multibyte_char

在读取响应体时,一个多字节字符被截断。

transfer_timeout

网络传输操作(读或写)超时。

network_access_denied

网络访问被拒绝,通常由网络ACL或防火墙策略导致。