基於 AXIS2/C 的 C 語言庫實現對提供 REST API 的系統進行數據訪問

AXIS2/C 簡介和 REST 及 REST API 相關內容的簡介

AXIS2/C 簡介

Axis2/c 是基於 C 語言實現的 Web Service 引擎,基於 Axis2 架構,用於提供 Web 服務,並具備良好的可移植性,能夠做爲其餘軟件的一部分提供 Web 服務。Axis2/c 支持 SOAP1.1 及 SOAP1.2 協議,而且支持 RESTful 風格的 Web 服務。基於 Axis2/c 開發的 Web 服務能夠同時暴露爲 SOAP 和 RESTful 風格的服務。用戶能夠基於 Axis2/c,開發 Web 服務以供其餘客戶端進行調用,亦可經過調用 Axis2/c 提供的 C 語言庫,開發客戶端程序,去訪問其餘的 Web 服務。 瀏覽器

REST 及 REST API 簡介

REST(REpresentational State Transfer,表述性狀態轉移),指一組架構約束條件和原則,是當下 Web Service 領域流行的一種軟件架構風格。REST 強調從資源的角度觀察整個網絡,基於 REST 的架構是一種面向資源的架構(Resource-Oriented Architecture,ROA)。資源一般由惟一的 URI(Uniform Resource Identifier,統一資源標識符)來標識,例如:"http://10.11.12.13:8080/rest/resources/Server"。客戶端的程序經過訪問 URI 來獲取資源的表述。REST 一般使用 HTTP,URI,XML 以及 HTML 這些現有的普遍流行的協議和標準。 安全

基於 REST 的 Web 服務以其架構簡單、可擴展、安全有效,經過 HTTP 直接傳播數據等特性,成爲 Web 服務領域一個愈來愈流行的架構形式。 服務器

REST API 是一個系統提供給外部系統的數據訪問服務接口。基於 REST API 的服務,以資源的形式存在 ,客戶端經過對資源的操做實現相應的功能。在 RESTful 的 Web 服務中,對資源的操做經過 HTTP 的四個標準方法實現,且全部的業務需求都可映射成對資源的操做。例如,獲取資源的表述用 GET 方法,修改資源用 PUT 方法,添加一個資源用 POST 方法 或者 用 PUT 方法(POST 表示建立子資源,PUT 在目標資源不存在時建立資源自己),刪除資源用 DELETE 方法等。支持 HEAD 操做,但它只返回報頭,不返回表述,用於獲得資源的元數據時使用。 網絡

HTTP 通訊過程當中,身份認證 Authentication 的分析

在 HTTP 通訊過程當中,認證是一種用來容許 Web 瀏覽器,或者其餘客戶端程序在請求時提供以用戶名及密碼形式的憑證。HTTP 認證基於質詢 / 迴應 (challenge/response) 的認證模式,並主要支持兩種認證方式:基本認證 (basic authentication) 以及摘要認證 (digest authentication),下面主要分析基本認證的步驟。 架構

一般狀況下,客戶端第一次請求 URI 服務時,不知曉是否須要驗證,所以會發送不帶認證信息的 HTTP 請求,服務端因爲找不到認證信息,認證失敗,向客戶端發送一個 HTTP 響應,狀態碼爲 401(Unauthorized),幷包含 WWW-Authenticate 消息頭,客戶端收到 HTTP 響應後,從新發送 HTTP 請求,並在請求頭中添加 Authorization 消息頭,格式爲 Authorization:credentials,其中 credentials 是認證信息,具體認證信息根據不一樣的認證方案而不一樣,當服務器對認證信息進行判斷,經過後即響應客戶端請求,以下顯示了基本認證的步驟: 函數

  1. 客戶端訪問一個受 HTTP 基本認證保護的資源。
  2. 服務器返回 401 狀態,要求客戶端提供用戶名和密碼進行認證。
    401 UnauthorizedWWW-AuthenticateBasic realm="Secret World"
  3. 客戶端將輸入的用戶名密碼用 Base64 進行編碼後,採用非加密的明文方式傳送給服務器:Authorization:Basic xxxxxx(其中 xxxxxx 爲 username:password 經過 Base64 編碼的字符串),例如:Authorization:Basic dXNlcm5hbWU6cGFzc3dvcmQ=
  4. 若是認證成功,則返回相應的資源。若是認證失敗,則仍返回 401 狀態,要求從新進行認證。

回頁首 工具

AXIS2/C 環境搭建

AXIS2/C 環境搭建(Linux 環境)

這裏咱們以 Red Hat 5.7 環境爲例,介紹一下如何搭建 Axis2/c 的運行環境。首先須要下載 Axis2c 的安裝包,下載地址可見參考資料。這裏咱們以最新的版本 1.6 爲例。建立安裝目錄 axis2c,解壓縮 Axis2/c 的安裝包 axis2c-src-1.6.0.tar.gz 到文件夾 axis2c-src-1.6.0 下,以下圖所示: post

圖 1. 解壓縮 AXIS2/C 安裝包
圖 1. 解壓縮 AXIS2/C 安裝包

解壓以後,編譯源碼進行安裝。參數 prefix 用來指定安裝目錄,enable-openssl 用來指定是否須要訪問具備 SSL 的 REST API 系統。以下圖所示: 測試

圖 2. 編譯並安裝
圖 2. 編譯並安裝

Axis2/c 的安裝包當中提供了不少用例,用例當中使用到了某些環境變量,因此這裏咱們須要設定兩個環境變量: ui

export AXIS2C_HOME=/zhaojian/axis2c // 指定 Axis2c 的安裝目錄

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AXIS2C_HOME}/lib/ // 指定 Axis2c 的 library 目錄

用例代碼也須要進行編譯安裝。以下圖所示:

圖 3. 編譯用例代碼
圖 3. 編譯用例代碼

至此,基本的 Axis2/c 開發運行環境已經搭建完成,接下來,運行示例代碼,並測試運行環境是否能夠正常運行。用例代碼是運行在 Axis2/c Server 之上的,因此必須先啓動 Axis2/c Server,先看一下啓動命令的幫助信息,以下圖所示:

圖 4. 啓動命令的幫助信息
圖 4. 啓動命令的幫助信息

啓動方法,以下圖所示:

圖 5. 啓動 AXIS2/C Server 成功
圖 5. 啓動 AXIS2/C Server 成功

若是輸入服務器啓動命令」./axis2_http_server」以後,命令行一直處於 hang on 的狀態,代表 Server 已經啓動成功,用例代碼能夠運行了;若是命令行中斷退出以下圖所示,代表啓動失敗,能夠經過日誌文件 axis2.log 找出失敗緣由。本實例中致使失敗的緣由是 Axis2/c Server 服務器的默認端口 9090 被其它應用程序佔用,致使啓動失敗。

圖 6. 啓動 AXIS2/C Server 失敗
圖 6. 啓動 AXIS2/C Server 失敗

咱們能夠經過使用啓動命令參數 -p 來修改端口號,以下圖所示:

圖 7. 修改端口
圖 7. 修改端口

Axis2c Server 啓動以後,就能夠運行用例代碼了:

圖 8. 運行用例代碼
圖 8. 運行用例代碼

這裏要指出的是由於啓動 Server 時,若是修改了默認端口 9090 爲其它端口好比 8080 時,當運行用例好比上圖中的 echo 命令時,也須要修改用例當中的端口爲已修改的端口號 8080(能夠經過命令 echo 的參數來修改,也能夠修改用例源代碼,不過須要從新編譯安裝)。

AXIS2/C 的參數配置

爲了可以經過 Axis2/c 提供的 C 語言庫函數來訪問具備 REST API 接口的系統數據,咱們必須對 Axis2/c 的環境作一些參數配置,纔可以讓咱們的 Sample Code 順利運行。在 Axis2/c 中,全部 Axis2/c 啓動須要的配置都包括在配置文件 axis2.xml 當中。下面咱們來看看都有哪些參數能夠修改來知足咱們的示例代碼。

首先若是用戶須要訪問或編寫 RESTful 風格的 Web Service,都須要將參數「enableREST」更改成「true」,默認值已經設爲「true」以下圖所示:

圖 9. 啓用 REST 功能
圖 9. 啓用 REST 功能

默認狀況下,Axis2/c 使用 HTTP 協議進行服務訪問,對於提供了安全層的服務訪問,這裏須要啓用 HTTPS 協議的配置,首先確認安裝部署了 Axis2/c 服務的機器上是否安裝 openssl 以便咱們進行 HTTPS 的數據訪問,確認方法以下圖所示,確保 openssl 包已安裝便可:

圖 10. 爲 Axis2/c 添加 SSL 支持
圖 10. 爲 Axis2/c 添加 SSL 支持

在 axis2.xml 當中啓用以下幾個參數:

圖 11. 啓用 HTTPS 參數
圖 11. 啓用 HTTPS 參數

另外,在訪問具備 HTTPS 的服務時,服務端須要驗證客戶端的證書,因此咱們還須要提供訪問證書的路徑信息,好比:

圖 12. 證書路徑
圖 12. 證書路徑

查看服務端的證書的代碼的命令是 openssl s_client – connect <servername>:<port>. 請看以下圖示例所示:

圖 13. 查看服務端證書
圖 13. 查看服務端證書

能夠經過下面這三條命令在默認目錄下生成本地證書 ( 可參考下圖所示 ):

echo |\ 
 openssl s_client -connect <servername>:<port> 2>&1 |\ 
 sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem

其中 <servername> 和 <port> 是所要訪問的提供 REST API 系統的 IP 地址和端口。

圖 14. 製做本地證書
圖 14. 製做本地證書

在 HTTP 通訊過程當中,部分 Web 服務須要驗證訪問該服務的用戶身份,能夠經過修改 axis2.xml 中的參數實現,也能夠經過在代碼中增長相應語句實現(後續章節會具體講解),須要修改參數以下:

圖 15. 添加 Authentication 信息
圖 15. 添加 Authentication 信息

作完以上這些參數配置以後,若是一開始編譯源碼時,就添加了參數 enable-openssl 而且值爲 yes,咱們這裏就不須要從新編譯源碼了,只須要從新啓動 Axis2 Server 就能夠了,不然須要從新編譯而且添加參數 enable-openssl 爲 yes 值。

回頁首

標準 REST API 方法的訪問

REST API 訪問過程解析

對於提供了 REST API 服務的系統進行數據請求調用是基於 HTTP 協議進行通訊的,而且使用了標準的 HTTP 方法 GET,POST,PUT,DELETE 等。本節經過實例代碼,詳細介紹如何經過 Axis2/c 提供的庫函數,使用 C 語言代碼,完成 REST 請求消息的建立,訪問 REST API 服務的標準方法實現以及對於響應消息的接收等內容。

在接下來的實例清單和 Sample Code 當中,咱們都以 IBM Systems Director 產品提供的 REST API 服務爲例,所涉及到的 URI 信息也會以該服務提供的內容爲例。該產品提供的 RES API 詳情可見參考資料。

建立一個完整的請求消息,包括三個部分:請求行、消息頭以及請求體。請求行能夠經過兩種方法實現。第一種是經過符合要求的請求行字符串,調用 axis2_http_request_line.h 中 axis2_http_request_line_parse_line 方法,生成請求行,如清單 1 所示:

清單 1. 請求行建立方式一
axis2_http_request_line_t *request_line = NULL; 
 const char *request_line_str = "GET /ibm/director/rest/resources HTTP/1.1\r\n"; 
 request_line = axis2_http_request_line_parse_line(env, request_line_str);

第二種是調用 axis2_http_request_line.h 中 axis2_http_request_line_create 方法,經過傳遞必要參數,建立請求行,如清單 2 所示:

清單 2. 請求行建立方式二
axis2_http_request_line_t *request_line = NULL; 
 request_line = 
 axis2_http_request_line_create(env, "GET", "/ibm/director/rest/resources", "HTTP/1.1");

消息頭的建立能夠經過調用 axis2_http_header.h 中的 axis2_http_header_create 方法進行建立,具體實例如清單 3 所示:

清單 3. 消息頭的建立
axis2_http_simple_request_t *request = NULL; 
 axutil_url_t *url = NULL; 
 axis2_http_header_t *header = NULL; 
 request = axis2_http_simple_request_create(env,request_line,NULL,0 ,request_body); 
 url = 
 axutil_url_create(env, "https", "10.11.12.13",8422,"/ibm/director/rest/resources");
 header = axis2_http_header_create(env, "Host", axutil_url_get_host(url, env)); 
 axis2_http_simple_request_add_header(request, env, header);

在調用 REST API 過程當中,部分通訊須要驗證請求客戶端的身份。客戶端能夠經過在請求消息的消息頭中,添加屬性 Authentication,向服務端傳送身份認證信息。本例實現的認證爲 HTTP 的基本認證,Authentication 屬性值的形式爲 Basic xxxxxx 形式,xxxxxx 表明 username:password 經過 base64 編碼後的字符串,形如:Authorization:Basic dXNlcm5hbWU6cGFzc3dvcmQ=,能夠經過調用 axutil_base64.h 中的 axutil_base64_encode 完成對字符串的編碼,請參閱清單 4 所示:

清單 4. Base64 編碼實現及 Authentication 屬性添加
/***** 對 username:password 字符串進行編碼 *********/ 
 int str_size = 0; 
 int encoded_len = 0; 
 axis2_char_t *str_src = "username:password";//「用戶名 : 密碼」字符串
 axis2_char_t *encoded_str = NULL;// 對「用戶名 : 密碼」字符串進行 base64 編碼後字符串
 axis2_char_t *firstStr = "Basic";//Authentication 值的頭部分
 str_size = strlen(str_src); 
 encoded_len = axutil_base64_encode_len(str_size); 
 encoded_str = AXIS2_MALLOC(env->allocator, encoded_len + 2); 
 encoded_len = axutil_base64_encode(encoded_str,str_src,str_size); 
 /*****Authentication 值的合成 *********/ 
 int size = 0; 
 int firstStrSize = strlen(firstStr); 
 int encoded_strSize = strlen(encoded_str); 
 size = firstStrSize + encoded_strSize; 
 if(firstStrSize > encoded_strSize){ 
 size = ((int)strlen(firstStr)) * 2; 
 }else{ 
 size = ((int)strlen(encoded_str)) * 2; 
 } 
 char *str_return = (char *) malloc((size + 2) * sizeof(char)); 
 strcpy(str_return, firstStr); 
 strcat(str_return," "); 
 strcat(str_return, encoded_str); 
 /***** 在消息頭中添加屬性 Authentication*********/ 
 header = axis2_http_header_create(env, "Authorization", str_return); 
 axis2_http_simple_request_add_header(request, env, header);

在調用 REST API 過程當中,若是使用 POST 或 PUT 方法,須要向服務端傳遞相應信息,這些信息能夠添加至請求消息中的消息體中。本例中,建立的消息體是基於 JSON 格式的,建立消息體的方式有兩種:

第一種方式是按照標準的 JSON 格式,直接書寫字符串,具體實例參照清單 5 所示,示例 JSON 格式消息體爲:{"IPAddress":["192.168.1.1"]}。

清單 5. 消息體建立方式一
char *body_request = NULL; 
 int body_request_len = 0; 
 body_request = "{\"IPAddress\":[\"192.168.1.1\"]}"; 
 body_request_len = strlen(body_request);

第二種方式是經過調用 cJSON.h 中的方法,生成消息體,具體實例參照清單 6 所示,示例 JSON 格式消息體爲:{"entry",{"name":"Andrew","phone":"555 123 456"}}。

清單 6. 消息體建立方式二
char *body_request = NULL; 
 int body_request_len = 0; 
 cJSON *root; 
 cJSON *value; 
 cJSON *entry; 
 root=cJSON_CreateObject();// 定義根節點
 entry = cJSON_CreateObject(); 
 cJSON_AddItemToObject(root,"entry",entry);// 建立 JSON 數據:name 爲 entry ,value 儲存在 entry 中
 value = cJSON_CreateString("Andew");// 將字符串建立爲 cJSON 數據
 cJSON_AddItemToObject(entry,"name",value); 
 value = cJSON_CreateString("555 123 456"); 
 cJSON_AddItemToObject(entry,"phone",value); 
 body_request = cJSON_Print(root);// 將 JSON 數據轉化爲字符串;
 body_request_len = strlen(body_request);

最後經過調用 axutil_stream.h 中的 axutil_stream_write 方法將須要添加的消息體字符串寫入消息體中,如清單 7 所示。

清單 7. 將字符串寫入消息體
axutil_stream_t *request_body = NULL; 
 axutil_stream_write(request_body,env,body_request,body_request_len);// 寫入消息體

清單 8 詳細寫出了一個完整的請求發送,響應接收過程當中方法的調用,涉及到的方法均包含在文件 axis2_http_client.h 中。

清單 8. REST API 服務的請求與響應過程
axis2_http_simple_request_t *request = NULL; 
 request = axis2_http_simple_request_create(env, request_line,NULL,0 , request_body); 
 int status = 0; 
 axis2_http_client_set_server_cert(client, env, "/home/cert.pem");
 // 對於 https,須要添加服務器端的證書,即以前製做的證書 cert.pem 
 axis2_http_client_send(client, env, request, NULL); 
 axis2_http_simple_response_t *response = NULL; 
 char *body_bytes = NULL; 
 int body_bytes_len = 0; 
 response = axis2_http_client_get_response(client, env); 
 body_bytes_len = 
 axis2_http_simple_response_get_body_bytes(response, env, &body_bytes);
 // 其中 body_bytes 即爲返回響應的結果字符串

四種方法對 REST API 的訪問實例

對於 REST API 服務,客戶端主要經過標準方法對資源進行訪問及處理,標準方法主要包括四種:GET、PUT、POST 以及 DELETE。下面將分別介紹這四種方法的使用。

GET 操做會列出該服務所提供的相關數據信息,實現 GET 操做的完整示例能夠下載咱們的 Sample Code,參考文件 rest_sample.c 當中的 test_rest_get 方法。運行該方法後能夠獲得相似於以下圖所示的運行結果:

圖 16. GET 操做運行結果
圖 16. GET 操做運行結果

POST 操做能夠經過消息體中含有的信息,執行資源的建立。實現 POST 操做完整示例能夠下載咱們的 Sample Code,參考文件 rest_sample.c 當中的 test_rest_post 方法。運行該方法後能夠獲得相似於以下圖所示的運行結果:

圖 17. POST 操做運行結果
圖 17. POST 操做運行結果

PUT 操做主要用於對資源進行修改,須要修改的內容包含在 requestBody 中。實現 PUT 操做完整示例能夠下載咱們的 Sample Code,參考文件 rest_sample.c 當中的 test_rest_put 方法。運行該方法後能夠獲得相似於以下圖所示的運行結果:

圖 18. PUT 操做運行結果
圖 18. PUT 操做運行結果

DELETE 操做主要用於對資源進行刪除。實現 DELETE 操做完整示例能夠下載咱們的 Sample Code,參考文件 rest_sample.c 當中的 test_rest_delete 方法。運行該方法後能夠獲得相似於以下圖所示的運行結果:

圖表 19 DELETE 操做運行結果

Figure xxx. Requires a heading
Figure xxx. Requires a heading

另外,附上清單 9,經常使用狀態碼列表清楚的列出了各個返回碼的含義,方便讀者查閱:

清單 9. 經常使用狀態碼列表
200 :表示服務器成功執行了客戶端的 HTTP/HTTPS 請求
 201 :表示服務器按客戶端的請求成功建立了一個新資源
 202 :表示服務器成功接收客戶端請求並進行處理
 204 :表示服務器已經成功處理了客戶端請求,可是沒有可返回的內容
 303:表示服務器將客戶端的請求重定向到另外一個 URI,要完成請求必須進行進一步操做
 304 :表示請求資源不能被更改
 400 :表示客戶端的請求參數不合法或沒有表達足夠的信息
 401 :表示由於安全的緣由,致使對資源的操做沒有完成
 404 :表示客戶端請求的資源不存在,即 URI 無效
 405 :表示請求的資源不支持該操做
 409 :表示 URI 指定的資源發生衝突
 500 :表示服務器端發生非預期狀況,致使請求沒有完成
 503 :表示處理請求的服務目前不可用

回頁首

C 語言對於 JSON 格式數據的處理

JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式,採用徹底獨立於語言的文本格式,易於人的閱讀和編寫,也易於機器的解析和生成,這些特性使 JSON 成爲理想的數據交換語言。

在咱們的 Sample Code 當中,使用基於 Axis2/c 的 C 語言庫來訪問 REST API 時,經過 GET 方法獲取到的數據就是 JSON 格式的數據。因爲 Axis2/c 的庫函數中,目前尚未對 JSON 格式數據處理的方法,因此下面咱們將簡要介紹如何使用開源工具 cJSON 來完成對 JSON 格式數據的處理。

下面一個例子介紹了使用方法 cJSON_Parse 來解析 JSON 數據字符串並轉成數據對象 cJSON,經過 cJSON_GetObjectItem 方法獲得你想要的數據項。請看清單 10 所示:

清單 10. 將 JSON 字符串轉換爲數據對象 cJSON
char *document = 
 "{\"entry\":{\"name\":\"Andew\",\"phone\":\"555 123 456\"}}"; 
 char *result = NULL; 
 cJSON *root; 
 cJSON *label; 
 root = cJSON_Parse(document);// 將字符型數據轉換爲 cJSON 型
 label = cJSON_GetObjectItem(root,"entry");// 經過已知的名稱,獲得對應值
 result = cJSON_Print(label);// 將 cJSON 數據轉換成 char 型
 printf("the result is %s\n",result); // 打印輸出結果

使用 cJSON_Print 方法把 cJSON 數據對象 label 轉換爲字符串,結果以下圖所示:

圖 . 20 JSON 字符串轉化
圖 . 20 JSON 字符串轉化

固然,你也可使用 cJSON.h 當中的方法來建立 JSON 格式的數據字符串。首先使用方法 cJSON_CreateObject 來建立根節點,而後使用 cJSON_AddItemToObject 添加你想建立的數據項。下面一個例子就是如何生成 JSON 字符串 {"entry ":{"name":"Andrew","phone":"555 123 456"} }。請看清單 11 所示:

清單 11. 使用 cJSON 當中的方法建立 JSON 數據
cJSON *root = cJSON_CreateObject();// 定義根節點
 cJSON *entry = cJSON_CreateObject();// 定義 entry 節點
 cJSON *value = cJSON_CreateString("Andew");// 將字符串建立爲 cJSON 數據
 cJSON_AddItemToObject(root, "entry",entry);// 建立 JSON 數據:name 爲 entry ,value 儲存在 entry 中,
 cJSON_AddItemToObject(entry,"name", value); 
 value = cJSON_CreateString("555 123 456"); 
 cJSON_AddItemToObject(entry, "phone", value); 
 char *result = cJSON_Print(root);// 將 JSON 數據轉化爲字符串;
 printf("the result is :%s\n",result); // 打印輸出結果

使用 cJSON_Print 方法把 cJSON 數據對象 root 轉換爲

字符串並打印出來,結果以下圖所示:

圖 21. JSON 數據建立
圖 21. JSON 數據建立

在 Sample Code 當中,咱們列出來一段完整的代碼包括使用

cJSON和實例代碼的編譯:

Sample Code 當中使用了開源工具 cJSON,爲了可以運行 Sample Code,必須對 Sample Code 和 cJSON 的源碼進行編譯。編譯過程當中會出現錯誤,主要因爲如下兩個緣由:1)cJSON 代碼中使用了 math.h 當中的函數所致使的,能夠經過修改 Makefile 當中的」 CC = gcc」爲」 CC = gcc -lm」便可;2)cJSON 代碼中的註釋格式使用不當所致使,能夠經過修改 cJSON.c 文件,將」 // 「格式的註釋改成」/* */」便可。

Axis2/c 的 C 語言庫函數來訪問具備 REST API 系統獲取數據,

並使用 cJSON.h 當中的方法來處理返回的結果等 ( 可參考方法 test_rest_get 當中處理返回結果的代碼 )。詳細內容請參考咱們

的 Sample Code,這裏就再也不一一敘述了。

相關文章
相關標籤/搜索