可以,但需预先配置ACL授权目标域名,手动设置JSON请求头与Bearer Token,严格匹配字符集和Content-Length,并及时读取响应体以防400错误信息丢失。
UTL_HTTP 能否直接调用外部 HTTPS API?
可以,但有硬性前提:目标域名必须提前加入 oracle 的访问控制列表(acl),否则会报 ora-29273: http request failed 和 ora-06512: at "sys.utl_http"。这不是代码写错,而是数据库级安全拦截。
常见误区是以为配好代理或网络通了就能发请求——Oracle 11gR2+ 强制 ACL 验证,连 localhost 或内网地址都需显式授权。
- ACL 文件本质是 XML,存于数据字典,用
DBMS_NETWORK_ACL_ADMIN包管理 - 一个 ACL 可绑定多个主机,但端口范围必须明确(如
443不能写成440-450) - 用户需被授予该 ACL 的
connect权限,否则即使 DBA 执行了配置,存储过程运行时仍失败
如何构造带 JSON Body 和 Bearer Token 的 POST 请求?
UTL_HTTP 不自动处理 JSON 序列化或 Authorization 头,所有内容需手动拼装。关键点在于字符集、Content-Length 和 Body 编码一致性。
示例场景:向 https://api.example.com/v1/data 发送含 token 的 JSON:
DECLARE
req UTL_HTTP.req;
resp UTL_HTTP.resp;
url VARCHAR2(500) := 'https://api.example.com/v1/data';
json_body VARCHAR2(1000) := '{"id":123,"name":"test"}';
token VARCHAR2(200) := 'eyJhbGciOiJIUzI1NiIs...';
BEGIN
req := UTL_HTTP.begin_request(url, 'POST');
UTL_HTTP.set_header(req, 'Content-Type', 'application/json; charset=utf-8');
UTL_HTTP.set_header(req, 'Authorization', 'Bearer ' || token);
UTL_HTTP.set_header(req, 'Content-Length', LENGTH(json_body));
UTL_HTTP.write_text(req, json_body); -- 注意:不是 write_raw,除非你转成 RAW
resp := UTL_HTTP.get_response(req);
-- 处理 resp.status_code 和 resp.reason_phrase
UTL_HTTP.end_response(resp);
END;- 若 JSON 含中文,
write_text自动按数据库字符集编码;若数据库是 AL32UTF8,一般没问题;若是 ZHS16GBK,需先用CONVERT转 UTF-8 再write_raw -
Content-Length必须是字节数(非字符数),对 UTF-8 中文建议用LENGTHB(json_body) - 超时默认仅 60 秒,长响应需调
UTL_HTTP.set_transfer_timeout
为什么收到 400 Bad Request 却看不出原因?
因为 UTL_HTTP.get_response 只返回状态码和 reason phrase,不附带响应体。400 错误常因 JSON 格式错、必填字段缺失或时间戳格式不对,但服务端返回的详细错误信息被丢弃了。
- 必须在
get_response后立即用UTL_HTTP.read_text或read_line读取 body,否则连接关闭后无法再取 - 若响应体较大,需循环读取直到
UTL_HTTP.end_of_body为 TRUE - 某些 API 对 User-Agent 敏感,可加
UTL_HTTP.set_header(req, 'User-Agent', 'Oracle-UTLHTTP/1.0')避免被拒 - 调试时优先用
curl -v对比请求头和 body,确认 Oracle 发出的内容是否与预期一致
生产环境必须考虑的三个实际限制
UTL_HTTP 不是 curl,它跑在数据库进程里,资源受严格约束:
- 单次请求最大响应体默认 2000 字节(
UTL_HTTP.set_body_charset不影响此限制),超长需设UTL_HTTP.set_response_error_check(FALSE)并手动处理截断 - 不能异步调用,阻塞会话;高并发调用外部 API 容易撑满
shared_pool或触发ORA-12516 - SSL 证书验证默认开启,自签名证书会报
ORA-29273: HTTP request failed+ORA-06512: at "SYS.UTL_HTTP", line 1312;临时方案是设UTL_HTTP.set_wallet指向含根证书的 wallet 目录
真正难的不是写出能跑的代码,而是让每次调用都稳定、可观测、不拖垮数据库。ACL 配置、字符集对齐、响应体读取时机,这三个点漏掉任何一个,都会在凌晨三点把你叫醒。


















