oracle利用UTL_HTTP包访问SOAP、HTTP接口并通过管道函数解析xml响应文件用游标以表方式输出

1.楼主所在公司有个业务需求是需要访问地方医保局发布的一个DRGs查询SOAP接口,POST入参,同时获取接口返回数据。

2.楼主搜了一些资料之后,自己研究用oracle里的UTL_HTTP包成功的访问到了接口,并获取了接口返回。

3.教程都整理成word,放在附件了。下面简单说下步骤。

4.使用utl_http包之前需要做以下数据库配置配置脚本如下

1.	创建ACL,如果原来数据库就已经创建了ACL文件,可以直接略过这步,如果没有则脚本如下。
begin
  dbms_network_acl_admin.create_acl(  
                                    acl         => 'utl_http.xml', -- 自定义文件名称   
                                    description => 'HTTP Access', -- 自定义描述    
                                    principal   => 'xx', -- 要配置的数据库账号,大小写敏感     
                                    is_grant    => TRUE, -- 授权还是取消授权     
                                    privilege   => 'connect', -- 连接权限    
                                    start_date  => null, -- 起始日期,不维护则为不限制,下同   
                                    end_date    => null -- 结束日期  
                                    );
end;

2.	然后再在这个acl中给这个用户增加一个resolve的权限。
begin
  dbms_network_acl_admin.add_privilege( acl        => 'utl_http.xml', -- 刚才创建的acl名称     
                                       principal  => 'xx', -- 与上同   
                                       is_grant   => TRUE, -- 与上同     
                                       privilege  => 'resolve', -- resolve权限   
                                       start_date => null,
                                       end_date   => null);
end;

3.	最后在acl中添加要访问的目标网址(ip和域名均可),可以指定端口范围。
begin
  dbms_network_acl_admin.assign_acl( -- 该段命令意思是允许访问acl名为utl_http.xml下授权的用户,使用oracle网络访问包,所允许访问的目的主机,及其端口范围。
                                    acl  => 'utl_http.xml',
                                    host => '192.168.xxx.xxx',  -- ip地址或者域名,填写https://localhost:9000/hello与https://localhost:9000/是会报host无效的,且建议使用ip地址或者使用域名,若用localhost,当oracle不是安装在本机上的情况下,会出现问题
                                    lower_port => '10344', -- 允许访问的起始端口号    
                                    upper_port => '10344' -- 允许访问的截止端口号  
                                    );
  commit;
end;

 5.创建解析接口响应xml格式文件的管道函数,附件有响应报文用例。

1.	自定义表类型
因为需要以表的方式来输出解析xml响应文件,所以函数需要使用管道函数,不清楚什么是管道函数的可以百度下,所以创建管道函数之前需要创建表类型drg_table作为管道函数的返回值类型。
创建函数脚本如下
create or replace type drg_table is table of drg_data_type

2.	自定义表类型字段
定义drg_table表类型中所含的字段,这里根据要解析的xml响应文件,也就是接口里的返回报文里面各个xml节点值对应得定义成表类型的字段。

创建drg_table表类型里所包含的字段脚本如下
create or replace type drg_data_type as object
(grp_time           date,--分组时间
grp_ver            varchar2(100),--分组版本
adrg_name          varchar2(400),--ADRG 名称
grp_name           varchar2(400),--分组名称
mdc_codg           varchar2(100),--MDC 编码
grp_spec_ver       varchar2(100),--分组规范版本号
ccmcc              varchar2(400),--合并症并发症
grp_rslt           varchar2(12),--分组成功标志
fixmedins_code     varchar2(100),--定点医药机构编号
mdc_name           varchar2(500),--MDC 名称
psn_no             varchar2(50),--人员编号
place_grp_pay_std  number(16,2),--地方分组支付标准
err_info           varchar2(1000),--错误信息
place_drg_codg     varchar2(100),--地方 DRG 编码
abbr               varchar2(500),--简称
drg_name           varchar2(500),--DRG 名称
place_grp_spec_ver varchar2(12),--地方分组规范版本号
adrg_codg          varchar2(100),--ADRG 编码
setl_id            varchar2(100),--结算 ID
drg_admdvs         varchar2(12),--DRG 医保区划
setl_list_sn       varchar2(200),--结算清单流水号
place_drg_name     varchar2(400),--地方 DRG 名称
setl_ym     varchar2(12),--结算年月
place_grp_ver      varchar2(100),--地方分组版本
vali_flag          varchar2(40),--有效标志
drg_code           varchar2(100),--DRG 编码
grp_type           varchar2(12)--分组类别
)

3.	创建管道函数
创建函数,解析xml响应文件脚本如下
/****************创建函数代码****************/
function of_get_drgval (as_clob_string in clob,as_clob_mark in varchar2) return drg_table pipelined—利用管道函数来存储xml解析出来的节点值
 as 
vs_drg_table drg_data_type;
vs_bmark VARCHAR2(1000);
vs_emark VARCHAR2(1000);
vi_bpos   integer;
vi_epos   integer;
vs_clob_subr clob;--响应文件入参,clob类型支持超过32767长度的大文本

vs_grp_time_str       varchar2(50);
vs_grp_time           date;--分组时间
vs_grp_ver            varchar2(50);--分组版本
vs_adrg_name          varchar2(100);--ADRG 名称
vs_grp_name           varchar2(200);--分组名称
vs_mdc_codg           varchar2(40);--MDC 编码
vs_grp_spec_ver       varchar2(50);--分组规范版本号
vs_ccmcc              varchar2(200);--合并症并发症
vs_grp_rslt           varchar2(6);--分组成功标志
vs_fixmedins_code     varchar2(50);--定点医药机构编号
vs_mdc_name           varchar2(100);--MDC 名称
vs_psn_no             varchar2(30);--人员编号
vs_place_grp_pay_std  number(16,2);--地方分组支付标准
vs_err_info           varchar2(500);--错误信息
vs_place_drg_codg     varchar2(50);--地方 DRG 编码
vs_abbr               varchar2(50);--简称
vs_drg_name           varchar2(100);--DRG 名称
vs_place_grp_spec_ver varchar2(6);--地方分组规范版本号
vs_adrg_codg          varchar2(50);--ADRG 编码
vs_setl_id            varchar2(50);--结算 ID
vs_drg_admdvs         varchar2(6);--DRG 医保区划
vs_setl_list_sn       varchar2(50);--结算清单流水号
vs_place_drg_name     varchar2(100);--地方 DRG 名称
vs_setl_ym            varchar2(6);--结算年月
vs_place_grp_ver      varchar2(50);--地方分组版本
vs_vali_flag          varchar2(20);--有效标志
vs_drg_code           varchar2(50);--DRG 编码
vs_grp_type           varchar2(6);--分组类别



begin
vs_bmark  := '<' || lower(as_clob_mark) || '>';--截取段开始标记前缀,xml
格式文本一般每个节点的标记是以< 和/>括起来的内容。
vs_emark  := '</' || lower(as_clob_mark) || '>'; --截取段结束标记前缀
vi_bpos:=dbms_lob.instr(dbms_lob.substr(as_clob_string,5000),vs_bmark);-- 截取开始节点标记位置
vi_epos:=dbms_lob.instr(dbms_lob.substr(as_clob_string,5000),vs_emark);--截取结束节点标记位置

/**因为xml文件长度超过32767,所以需要一段一段截取出来,这里以5000长度为一次截取长度循环截取出来,然后循环提取节点值**/
while dbms_lob.instr(dbms_lob.substr(as_clob_string,5000,vi_bpos),vs_emark)>0  loop
vi_epos:=dbms_lob.instr(dbms_lob.substr(as_clob_string,5000,vi_bpos),vs_emark);
vs_clob_subr:=dbms_lob.substr(dbms_lob.substr(as_clob_string,5000,vi_bpos),vi_epos+LENGTH(vs_bmark));
vi_bpos:=vi_bpos+DBMS_LOB.GETLENGTH(vs_clob_subr);

/** xmltable 函数逐个解析xml里的节点出来,注意路径,这里举例的路径是//data**/
select x.* into  vs_grp_time_str, vs_grp_ver, vs_adrg_name, vs_grp_name, vs_mdc_codg, vs_grp_spec_ver, vs_ccmcc, vs_grp_rslt, vs_fixmedins_code, vs_mdc_name, vs_psn_no, vs_place_grp_pay_std, vs_err_info, vs_place_drg_codg, vs_abbr, vs_drg_name, vs_place_grp_spec_ver, vs_adrg_codg, vs_setl_id, vs_drg_admdvs, vs_setl_list_sn, vs_place_drg_name, vs_setl_ym, vs_place_grp_ver, vs_vali_flag, vs_drg_code, vs_grp_type
  from xmltable('//data' passing xmltype(vs_clob_subr)
                columns
                grp_time           varchar2(50)   path '//grp_time',
                grp_ver            varchar2(50)  path '//grp_ver',
                adrg_name          varchar2(100)  path '//adrg_name',
                grp_name           varchar2(200)  path '//grp_name',
                mdc_codg           varchar2(40)  path '//mdc_codg',
                grp_spec_ver       varchar2(50)  path '//grp_spec_ver',
                ccmcc              varchar2(200)  path '//ccmcc',
                grp_rslt           varchar2(6)  path '//grp_rslt',
                fixmedins_code     varchar2(50)  path '//fixmedins_code',
                mdc_name           varchar2(100)  path '//mdc_name',
                psn_no             varchar2(30)  path '//psn_no',
                place_grp_pay_std  number(16,2)  path '//place_grp_pay_std',
                err_info           varchar2(500)  path '//err_info',
                place_drg_codg     varchar2(50)  path '//place_drg_codg',
                abbr               varchar2(50)  path '//abbr',
                drg_name           varchar2(100)  path '//drg_name',
                place_grp_spec_ver varchar2(6)  path '//place_grp_spec_ver',
                adrg_codg          varchar2(50)  path '//adrg_codg',
                setl_id            varchar2(50)  path '//setl_id',
                drg_admdvs         varchar2(6)  path '//drg_admdvs',
                setl_list_sn       varchar2(50)  path '//setl_list_sn',
                place_drg_name     varchar2(100)  path '//place_drg_name',
                setl_ym            varchar2(6)  path '//setl_ym',
                place_grp_ver      varchar2(50)  path '//place_grp_ver',
                vali_flag          varchar2(20)  path '//vali_flag',
                drg_code           varchar2(50)  path '//drg_code',
                grp_type           varchar2(6)   path '//grp_type') x;
                
vs_grp_time:=vs_grp_time_str/(1000*60*60*24)+TO_DATE('1970-01-01 08:00:00','yyyy-MM-dd hh24:mi:ss');

vs_drg_table :=drg_data_type(vs_grp_time, vs_grp_ver, vs_adrg_name, vs_grp_name, vs_mdc_codg, vs_grp_spec_ver, vs_ccmcc, vs_grp_rslt, vs_fixmedins_code, vs_mdc_name, vs_psn_no, vs_place_grp_pay_std, vs_err_info, vs_place_drg_codg, vs_abbr, vs_drg_name, vs_place_grp_spec_ver, vs_adrg_codg, vs_setl_id, vs_drg_admdvs, vs_setl_list_sn, vs_place_drg_name, vs_setl_ym, vs_place_grp_ver, vs_vali_flag, vs_drg_code, vs_grp_type);

pipe row(vs_drg_table);--pipe row将提取出来的节点值全部填充到表类型vs_drg_table里的字段中,然后在存储过程中就可以用select a.* from table(of_get_drgval(vs_clob, 'data')) a语法像访问一般的数据库表一样访问到函数返回值。

end loop;
return;
end of_get_drgval;

6.创建访问soap/http接口,并解析xml的存储过程 

/****************创建存储过程代码****************/
/*存储过程入参什么的可以自己定义,我这里是有4个入参,一个出参作为返回用*/
create or replace procedure sp_yourname(as_setl_ym in VARCHAR2,
                           as_page_num in number,
                           as_page_size in number,
                           as_setl_id in VARCHAR2,
aCur_temp     out sys_refcursor) is


HTTP_REQ    UTL_HTTP.REQ;-- UTL_HTTP包请求类型参数
HTTP_RESP   UTL_HTTP.RESP; -- UTL_HTTP包响应类型参数
REQUEST_ENV   CLOB;--通过存储过程的入参拼接成,请求入参
VS_ERROR_ERRM VARCHAR2(200);--自定义异常参数
VS_ERROR_CODE VARCHAR2(50);--自定义异常代码
VS_TEXT       VARCHAR2(32767);--接收响应文本
VS_CLOB        CLOB;

begin

/***这段三个if条件是楼主自己根据对方接口文档加的一个逻辑前置判断,可以无理会****/
    if as_setl_ym is null  then RAISE_APPLICATION_ERROR(-20001,'请输入结算年月'); end if; 
    if as_page_num is null or as_page_num=0  then RAISE_APPLICATION_ERROR(-20002,'请输入分页页码'); end if;
    if as_page_size is null or as_page_size=0  then RAISE_APPLICATION_ERROR(-20003,'请输入分页数量'); end if;

/***这段初始化,是因为使用了CLOB 字段作为入参,CLOB 类型参数需要初始化****/
    request_env := empty_clob();--初始化请求入参
    vs_clob := empty_clob();--初始化请求入参
    DBMS_LOB.CREATETEMPORARY(request_env, TRUE);--初始化请求入参
    dbms_lob.createtemporary(vs_clob, TRUE);--初始化请求入参

/***这段根据存储过程4个入参拼接成访问接口的入参,用DBMS_LOB.APPEND ()函数添加内容到CLOB 类型参数request_env 中,作为访问接口的入参,CLOB 类型参数支持超过32767长度的数据,而varchar2不支持。****/

    DBMS_LOB.APPEND(request_env,'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
  <soapenv:Header/>
  <soapenv:Body>
    <tem:ClientRequest>
      <!--Optional:-->
      <tem:xmlParam><![CDATA[<paramMethod>3xx5</paramMethod> <paramInputBody>{"infno":"3xx5","msgid": 3xxxxxx2131'||to_char(sysdate,'yyyymmddhh24miss')||'","mdtrtarea_admvs": "6xxx99", "insuplc_admdvs": "", "recer_admdvs": "6xxx02", "dev_no": "", "dev_safe_info": "", "signtype": "SM3", "cainfo": "", "infver": "V1.0", "opter_type": "1", "opter": "999", "opter_name": "xx员", "inf_time": "'||sysdate||'", "fixmedins_code": "Hxxx502xxxxx", "fixmedins_name": "xxxxxxx", "sign_no": "3453422", "recer_sys_code": "1", "input": { "data": { "setl_ym": "'||as_setl_ym||'", "page_num": "'||as_page_num||'", "page_size": "'||as_page_size||'", "setl_id": "'||as_setl_id||'" } } }</paramInputBody>]]></tem:xmlParam>
    </tem:ClientRequest>
  </soapenv:Body>
</soapenv:Envelope>');

   begin
  http_req := UTL_HTTP.begin_request('http://IP:端口/jiekouhouzui?wsdl','POST','HTTP/1.1');--本文是post接口,这段需要配置所访问接口的IP/端口/地址,发送请求
  Utl_Http.Set_Header(http_req, 'Content-Type', 'text/xml;charset=utf-8');--设置请求头,字符集,请求格式,也可以支持json,text/xml改成application/json即可
  Utl_Http.Set_Body_Charset(http_req, 'utf-8');--设置请求体字符集
  Utl_Http.Set_Persistent_Conn_Support(http_req, TRUE);--这段是保持接口连接活动状态
  Utl_Http.Set_Header(http_req,'Content-Length',DBMS_LOB.GETLENGTH(request_env));--设置请求报文长度,不能少
  UTL_HTTP.SET_HEADER(http_req,'Transfer-Encoding', 'chunked');--设置请求头模式
  Utl_Http.Write_raw(http_req, UTL_RAW.cast_to_raw(request_env));--将请求参数重写成raw,防止请求参数里一些中文乱码或者特殊符号失效问题
  http_resp := utl_http.get_response(http_req);--获取接口响应报文

   if http_resp.status_code = 200  then--如果响应代码是200,表示接口正常
    begin
    loop
      utl_http.read_line(http_resp,vs_text);--读取响应报文至vs_text
      dbms_lob.writeappend(vs_clob,DBMS_LOB.GETLENGTH(vs_text),vs_text);--将vs_text逐行添加到大字段vs_clob中
    end loop;
  exception
    when utl_http.end_of_body then
      utl_http.end_response(http_resp);--结束请求
      end;
    else
      RAISE_APPLICATION_ERROR(-20005,'请求错误'); --异常处理

/**也可以用下面一段将异常信息插到数据库表中,方便查询和追踪历史异常信息记录**/
/**vs_ERROR_ERRM:=SQLERRM||dbms_utility.format_error_backtrace;
      vs_ERROR_codE:=SQLCODE||'--'||http_resp.status_code;
      INSERT INTO POST_DATE_ERROR (ERROR_TYPE,ERROR_SOURCE, ERROR_CODE, ERROR_ERRM, ERROR_PK_SOURCE, FLOGCBY, FLOGCDATE)
      VALUES ('PG','sp_yourname', VS_ERROR_CODE,VS_ERROR_ERRM,'', '', SYSDATE); **/

      utl_http.end_response(http_Resp);
      end if;

/** 调用of_get_drgval函数,解析xml响应文件,打开aCur_temp游标以表的方式将xml节点输出。**/
      begin
      open aCur_temp for 
      select a.* from table(of_get_drgval(vs_clob, 'data')) a;
      EXCEPTION
         when others then
         RAISE_APPLICATION_ERROR(-20005,'函数 of_get_drgval 错误');  
      end;

--dbms_output.put_line(dbms_lob.substr(vs_clob,32767));--打印响应报文,文本方式查看响应报文
--dbms_output.put_line(dbms_lob.substr(request_env,32767));--打印请求报文,查看请求报文
      dbms_lob.freetemporary(request_env);--释放大字段
      dbms_lob.freetemporary(vs_clob);--释放大字段
EXCEPTION
   WHEN utl_http.end_of_body THEN
     utl_http.end_response(http_Resp);
END;
end sp_yourname;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值