技術交流

HCP 快速拋轉資料的秘訣:Web Service

HCP(Human Capital Planner;人力資源規劃系統)的 ESS(Employee Self-Service;員工自助服務平台)本身有提供 Web Services 及 API 給外部系統使用與資料拋轉。在與其他系統做資料交換時,若同是 Oracle DB 資料庫可以簡單使用 DB link 來傳遞資料,而對於異質系統則可以使用許多工具或方式來傳遞資料,在這就介紹以 Web Services 來拋轉資料的做法。

Web Services 包含四個核心元件

Web Services 是一種服務導向架構的軟體服務技術,透過標準的 Web 協議提供服務以確保不同平台的應用服務可以互相操作或進行資料交換。其中最基本的是 HTTP 與 XML,核心定義主要包括四個元件:

  • UDDI:Universal Description, Discovery and Integration
    一個用來發布和搜索 WEB Service 的協議,應用程式可藉由此協議在設計或運行時找到目標 WEB Service。
  • WSDL:Web Services Description Language
    一個 XML 格式文檔,用以描述服務埠訪問方式和使用協議的細節。使用者如何與 Web Service 溝通及互動,哪些方法可以呼叫所需要使用的參數、參數格式等。
  • SOAP:Simple Object Access Protocol
    基於 XML 協議的可擴展消息信封格式,是一種標準化的通訊規範。
  • XML & HTTP
    Internet 的標準協定與通信基礎。

HCP 是以 Oracle 的 UTL_HTTP 這個 package 為介面經由 SOAP 進行傳遞 XML 格式的資料與訊息來呼叫 Web Services 並取得回應。

呼叫 Web Service

設定 ACL 存取控制串列

ACL 為 Access Control List 存取控制串列的簡寫,在 Oracle Database 11g 版本或以上由於針對連線到外部網絡服務有作進一步的安全控管,故在執行 UTL_HTTP 呼叫 Web Services 前須先作存取控制清單 ACL 的設定。Oracle 提供 DBMS_NETWORK_ACL_ADMIN package來管理設定 ACL。須留意執行 UTL_HTTP 程式時,若連線的服務不在或是沒設定存取控制清單會有下列錯誤訊息:ORA-24247: network access denied by access control list (ACL)。

使用 DBMS_NETWORK_ACL_ADMIN 來設定 ACL 的主要步驟為:

  1. 建立一個存取控制清單:建立存取控制清單給定名稱與描述。
  2. 指定連線的主機給存取控制清單:指定網址與連接埠,一個存取控制清單只能連到一個網址,如果重複指定新的設定會蓋過舊的設定。
  3. 給定權限:設定 Oracle 帳號的存取控制清單權限。

建立 ACL 範例:新增一名稱為 webservice_permissions.xml 連線到 IP:10.0.0.156 port: 5566 的存取控制清單並將連線權限給予 SCOTT


-- Create an ACL
  dbms_network_acl_admin.create_acl
    (acl => 'webservice_permissions.xml',
     description => 'Enables network permissions for the Web service',
     principal => 'SCOTT',
     is_grant => TRUE,
     privilege => 'connect',
     start_date => null,
     end_date => null );

  -- Assign Host to ACL
  dbms_network_acl_admin.assign_acl
    (acl => 'webservice_permissions.xml',
     host => '10.0.0.156',
     lower_port => 5566,
     upper_port => null);

  -- Adding privilege to ACL
  dbms_network_acl_admin.add_privilege
    (acl => 'webservice_permissions.xml',
     principal => ' SCOTT ',
     is_grant => true,
     privilege => 'connect');

  commit;

end;

ACL 相關的 view

  • DBA_NETWORK_ACLS:查詢存取控制清單
  • DBA_NETWORK_ACL_PRIVILEGES:查詢存取控制清單權限列表

執行 UTL_HTTP 呼叫 Web Services

Oracle DB 可以使用 utl_http package 在 HTTP 協定下來存取 internet 上的資料。資料拋轉主要透過 request body 中設定的參數。大致作業方式是開啟 request 建立連線,設定語系與傳送 SOAP 格式的 request 內容,接收並處理 response 內容,最後關閉連線。以 UTL_HTTP 呼叫 Web Services 的 function 與步驟如下:

  1. begin_request:開啟 request 建立連線
  2. set_header:設定 HTTP request header
  3. set_body_charset:設定語系(Optional)
  4. write_text:在 request body 輸入資料
  5. get_response:讀取 response
  6. read_text:讀取 HTTP response body
  7. end_response:完成 HTTP request and response

開啟 request 建立連線:傳入 url 網址,method 參數使用 POST。


begin
  -- Get web service url
  v_url := 'http://10.10.41.107:5566/WebService/RenewSalary.asmx';
  v_method := 'POST';

  -- send HTTP request
  utl_http.set_transfer_timeout(1200);
  v_req := utl_http.begin_request(v_url,
                                  v_method,
                                  utl_http.http_version_1_1);

設定 HTTP request header:主要是設定 SOAPAction,其他 request header 屬性大部分皆為固定設定值,設定 Content-Length 屬性時使用lengthb function 以配合 utl_http.set_body_charset 的設定值 UTF-8。


-- SOAP action
  v_soap_action := 'http://tempuri.org/CallByJson';

  -- Set request header
  utl_http.set_header(v_req, 'Content-Type', 'text/xml; charset=utf-8');
  utl_http.set_header(v_req,
                      'Content-Length',
                      lengthb(v_envelope));
  utl_http.set_header(v_req, 'User-Agent', 'Mozilla/4.0');
  utl_http.set_header(v_req, 'SOAPAction', v_soap_action);

在 request body 輸入資料,request body 採用 SOAP 格式並包含需傳入的參數,範例中傳遞參數 json 的值是 p_data,由於 HCP 資料庫使用多國語言,資料除英文外可能包含不同語系故加上語系設定並設為 UTF-8。

PS:由於 11g 尚未原生支援 json 格式,故將預先轉成 json 格式的 p_data 資料以字串方式傳遞,再於目標端接收後轉回 json 來處理來達成傳遞 json 資料。


-- request body
  -- for soap 1.1
  v_envelope := '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <CallByJson xmlns="http://tempuri.org/">
      <json>' || p_data || '</json>
    </CallByJson>
  </soap:Body>
</soap:Envelope>' || utl_tcp.crlf;

-- Set request body
  utl_http.set_body_charset(v_req, 'UTF-8');
  utl_http.write_text(v_req, v_envelope);

讀取 response,讀取 HTTP response body 與完成 HTTP request and response


-- Read response
  v_resp := utl_http.get_response(v_req);
  -- Read response body
  utl_http.read_text(v_resp, v_reponse_body);
  -- Complete HTTP request and response
  utl_http.end_response(v_resp);

這裡介紹了透過使用 UTL_HTTP來呼叫 Web Services 達到拋轉資料到其他系統,算是屬於比較基本的使用方式,在 HCP 或一般安裝的 Oracle DB 上原生就有支援。於較新版本的 Oracle DB 有提供其他新的套件可額外安裝,如 Oracle Application Express (APEX),其中包含了一些方便的 API 可以由 PL/SQL 呼叫使用,應用上還是基於同概念來運作,在有了相關的基礎觀念後就可以更容易理解跟運用今後新的工具。

下列是 WSDL 描述的 SOAP 1.1 Request 與 Response 範例,可以跟上述程式呼叫的範例比較。

Request


POST /WebService/RenewSalary.asmx HTTP/1.1
Host: 10.0.0.156
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/CallByJson"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <CallByJson xmlns="http://tempuri.org/">
      <json>string</json>
    </CallByJson>
  </soap:Body>
</soap:Envelope>

Response


HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <CallByJsonResponse xmlns="http://tempuri.org/">
      <CallByJsonResult>string</CallByJsonResult>
    </CallByJsonResponse>
  </soap:Body>
</soap:Envelope>