记录 Oracle 远程调用 HTTP 接口
前言
如果你有了解过PL/SQL, 那么下面的内容将会一目了然;当然对于没接触过PL/SQL的朋友来说,Copy/Paste 也不会影响使用。某天自己想到一个奇怪的功能需要使用Oracle调用自己的接口,于是查了些资料完成了一个Demo放过来,以便下次方便Copy。
1. 演示接口
@RestController
@RequestMapping("aboutTriggerController")
public class AboutTriggerController {
@RequestMapping("inputUpdateData")
public Object inputUpdateData(HttpServletRequest request) {
Object content= request.getParameter("content");
return content;
}
}
2. 开启Oracle ACL权限
存储过程,需要在SQL窗口中执行(使用超级管理员用户执行)。根据自己的需求对里面的内容进行修改。
principal 变量是授权的账号,根据自己的情况进行配置。
begin
dbms_network_acl_admin.create_acl ( -- 创建访问控制文件(ACL)
acl => 'utl_http.xml', -- 文件名称
description => 'HTTP Access', -- 描述
principal => 'DEMO', -- 授权或者取消授权账号,大小写敏感
is_grant => TRUE, -- 授权还是取消授权
privilege => 'connect', -- 授权或者取消授权的权限列表
start_date => null, -- 起始日期
end_date => null -- 结束日期
);
--添加访问权限列表项
dbms_network_acl_admin.add_privilege ( -- 添加访问权限列表项
acl => 'utl_http.xml', -- 刚才创建的acl名称
principal => 'DEMO', -- 授权或取消授权用户
is_grant => TRUE, -- 与上同
privilege => 'resolve', -- 权限列表
start_date => null,
end_date => null
);
--配置对应ip地址及端口
dbms_network_acl_admin.assign_acl ( -- 该段命令意思是允许访问acl名为utl_http.xml下授权的用户,使用oracle网络访问包,所允许访问的目的主机,及其端口范围。
acl => 'utl_http.xml',
host => '127.0.0.1', -- ip地址或者域名,填写http://localhost:9000/hello与http://localhost:9000/是会报host无效的
-- 且建议使用ip地址或者使用域名,若用localhost,当oracle不是安装在本机上的情况下,会出现问题
lower_port => 9102, -- 允许访问的起始端口号
upper_port => Null -- 允许访问的截止端口号
);
end;
3. 配置HTTP连接
存储过程,需要在SQL窗口中执行。r_url varchar2 变量是要调用的 URL, 需要自行配置。
CREATE OR REPLACE PROCEDURE remote_call(r_content in varchar2) IS
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
message varchar2(10000);
xmlstr varchar2(30000);
r_url varchar2(1000) := 'http://127.0.0.1:9102/xx-web/aboutTriggerController/inputUpdateData.do?content='||r_content;
begin
begin
req := UTL_HTTP.BEGIN_REQUEST(r_url);
utl_http.set_header(req, 'Content-Type', 'text/html; charset=utf-8');
utl_http.write_text(req,xmlstr); --通过body发送消息
xmlstr:=r_content;
utl_http.set_header(req, 'Content-Length',lengthb(xmlstr));
utl_http.write_text(req,xmlstr);
resp := UTL_HTTP.GET_RESPONSE(req);
LOOP
UTL_HTTP.read_line(resp,message, TRUE);
dbms_output.put_line(message);
END LOOP;
utl_http.end_request(req);
utl_http.end_response(resp);
EXCEPTION
WHEN utl_http.end_of_body THEN
utl_http.end_response(resp);
WHEN OTHERS THEN
utl_http.end_response(resp);
utl_http.end_request(req);
end;
END remote_call;
调用
调用的方式有很多种,可以自行百度关键词: 如何调用存储过程;我这里直接在SQL命令行中使用 call 方法调用。
call remote_call('hello,rpc!');
结果:
4. 可能会使用到
修改ACL文件
如果需要扩展访问权限列表项的端口或是想修改主机地址,可以使用以下存储过程来处理。
begin
dbms_network_acl_admin.assign_acl ( -- 该段命令意思是允许访问acl名为utl_http.xml下授权的用户,使用oracle网络访问包,所允许访问的目的主机,及其端口范围。
acl => 'utl_http.xml',
host => '127.0.0.1', -- ip地址或者域名,错误示例:http://127.0.0.1:80
-- 且建议使用ip地址或者使用域名,若用localhost,当oracle不是安装在本机上的情况下,会出现问题
lower_port => 80, -- 允许访问的起始端口号
upper_port => Null -- 允许访问的截止端口号
);
commit;
end;
如果开启 ACL 权限的时候提示说 ACL 文件已经存在,则将 acl 参数的值简单修改一下,这样会覆盖掉原来的数据。
查询ACL文件是否存在
select * from dba_network_acl_privileges ;
遇到的问题
网络访问被访问控制列表 (ACL) 拒绝
ORA-06512: 在 "SYS.UTL_HTTP", line 368
ORA-06512: 在 "SYS.UTL_HTTP", line 1118
ORA-29273: HTTP 请求失败
ORA-24247: 网络访问被访问控制列表 (ACL) 拒绝
如果用户并未指定 ACL 权限(访问控制列表)的话不能直接访问互联网,需要执行第 2 步开启 ACL 权限。
ACL 无效:无法解析的主用户
ORA-444416: ACL 无效: 无法解析的主用户 'xxx'
ORA-06512: 在 "SYS.DBMS_NETWORK_ACL_ADMIN", line 252
ORA-06512: 在 line2
在第 2 步 principal 指定的用户名为小写就会出现这个问题,改为大写。
编码问题
一直传入英文字符和数字,某天突然传入中文参数时既没有调用接口也没有抛异常,首先想到的就是编码,在服务端(也就是接口中)对中文进行转码是没有效果的。所以只能在 Oracle 中进行转码,参考相关资料解决方法如下:
CREATE OR REPLACE FUNCTION url_encode(urlEncode IN VARCHAR2)
RETURN VARCHAR2 AS
BEGIN
RETURN utl_url.escape(urlEncode, TRUE, 'utf-8');
END;
执行这个脚本后,在存储过程中使用这个函数将中文参数进行转码即可。