基于 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 下,如下图所示:

图 1. 解压缩 AXIS2/C 安装包
图 1. 解压缩 AXIS2/C 安装包

解压之后,编译源码进行安装。参数 prefix 用来指定安装目录,enable-openssl 用来指定是否需要访问具有 SSL 的 REST API 系统。如下图所示:

图 2. 编译并安装
图 2. 编译并安装

Axis2/c 的安装包当中提供了很多用例,用例当中使用到了某些环境变量,所以这里我们需要设定两个环境变量:

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. 启动命令的帮助信息
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第1张图片

启动方法,如下图所示:

图 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. 运行用例代码
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第2张图片

这里要指出的是因为启动 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 支持
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第3张图片

在 axis2.xml 当中启用如下几个参数:

图 11. 启用 HTTPS 参数
图 11. 启用 HTTPS 参数

另外,在访问具有 HTTPS 的服务时,服务端需要验证客户端的证书,所以我们还需要提供访问证书的路径信息,比如:

图 12. 证书路径
图 12. 证书路径

查看服务端的证书的代码的命令是 openssl s_client – connect <servername>:<port>. 请看如下图示例所示:

图 13. 查看服务端证书
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第4张图片

可以通过下面这三条命令在默认目录下生成本地证书 ( 可参考下图所示 ):

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 操作运行结果
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第5张图片

POST 操作可以通过消息体中含有的信息,执行资源的创建。实现 POST 操作完整示例可以下载我们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_post 方法。运行该方法后可以得到类似于如下图所示的运行结果:

图 17. POST 操作运行结果
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第6张图片

PUT 操作主要用于对资源进行修改,需要修改的内容包含在 requestBody 中。实现 PUT 操作完整示例可以下载我们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_put 方法。运行该方法后可以得到类似于如下图所示的运行结果:

图 18. PUT 操作运行结果
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第7张图片

DELETE 操作主要用于对资源进行删除。实现 DELETE 操作完整示例可以下载我们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_delete 方法。运行该方法后可以得到类似于如下图所示的运行结果:

图表 19 DELETE 操作运行结果

Figure xxx. Requires a heading
基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问_第8张图片

另外,附上清单 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,这里就不再一一叙述了。

你可能感兴趣的:(基于 AXIS2/C 的 C 语言库实现对提供 REST API 的系统进行数据访问)