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(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 通讯过程当中,认证是一种用来容许 Web 浏览器,或者其余客户端程序在请求时提供以用户名及密码形式的凭证。HTTP 认证基于质询 / 回应 (challenge/response) 的认证模式,并主要支持两种认证方式:基本认证 (basic authentication) 以及摘要认证 (digest authentication),下面主要分析基本认证的步骤。 架构
一般状况下,客户端第一次请求 URI 服务时,不知晓是否须要验证,所以会发送不带认证信息的 HTTP 请求,服务端因为找不到认证信息,认证失败,向客户端发送一个 HTTP 响应,状态码为 401(Unauthorized),并包含 WWW-Authenticate 消息头,客户端收到 HTTP 响应后,从新发送 HTTP 请求,并在请求头中添加 Authorization 消息头,格式为 Authorization:credentials,其中 credentials 是认证信息,具体认证信息根据不一样的认证方案而不一样,当服务器对认证信息进行判断,经过后即响应客户端请求,以下显示了基本认证的步骤: 函数
401 UnauthorizedWWW-Authenticate:Basic realm="Secret World"
回页首 工具
这里咱们以 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
解压以后,编译源码进行安装。参数 prefix 用来指定安装目录,enable-openssl 用来指定是否须要访问具备 SSL 的 REST API 系统。以下图所示: 测试
Axis2/c 的安装包当中提供了不少用例,用例当中使用到了某些环境变量,因此这里咱们须要设定两个环境变量: ui
export AXIS2C_HOME=/zhaojian/axis2c // 指定 Axis2c 的安装目录
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AXIS2C_HOME}/lib/ // 指定 Axis2c 的 library 目录
用例代码也须要进行编译安装。以下图所示:
至此,基本的 Axis2/c 开发运行环境已经搭建完成,接下来,运行示例代码,并测试运行环境是否能够正常运行。用例代码是运行在 Axis2/c Server 之上的,因此必须先启动 Axis2/c Server,先看一下启动命令的帮助信息,以下图所示:
启动方法,以下图所示:
若是输入服务器启动命令”./axis2_http_server”以后,命令行一直处于 hang on 的状态,代表 Server 已经启动成功,用例代码能够运行了;若是命令行中断退出以下图所示,代表启动失败,能够经过日志文件 axis2.log 找出失败缘由。本实例中致使失败的缘由是 Axis2/c Server 服务器的默认端口 9090 被其它应用程序占用,致使启动失败。
咱们能够经过使用启动命令参数 -p 来修改端口号,以下图所示:
Axis2c Server 启动以后,就能够运行用例代码了:
这里要指出的是由于启动 Server 时,若是修改了默认端口 9090 为其它端口好比 8080 时,当运行用例好比上图中的 echo 命令时,也须要修改用例当中的端口为已修改的端口号 8080(能够经过命令 echo 的参数来修改,也能够修改用例源代码,不过须要从新编译安装)。
为了可以经过 Axis2/c 提供的 C 语言库函数来访问具备 REST API 接口的系统数据,咱们必须对 Axis2/c 的环境作一些参数配置,才可以让咱们的 Sample Code 顺利运行。在 Axis2/c 中,全部 Axis2/c 启动须要的配置都包括在配置文件 axis2.xml 当中。下面咱们来看看都有哪些参数能够修改来知足咱们的示例代码。
首先若是用户须要访问或编写 RESTful 风格的 Web Service,都须要将参数“enableREST”更改成“true”,默认值已经设为“true”以下图所示:
默认状况下,Axis2/c 使用 HTTP 协议进行服务访问,对于提供了安全层的服务访问,这里须要启用 HTTPS 协议的配置,首先确认安装部署了 Axis2/c 服务的机器上是否安装 openssl 以便咱们进行 HTTPS 的数据访问,确认方法以下图所示,确保 openssl 包已安装便可:
在 axis2.xml 当中启用以下几个参数:
另外,在访问具备 HTTPS 的服务时,服务端须要验证客户端的证书,因此咱们还须要提供访问证书的路径信息,好比:
查看服务端的证书的代码的命令是 openssl s_client – connect <servername>:<port>. 请看以下图示例所示:
能够经过下面这三条命令在默认目录下生成本地证书 ( 可参考下图所示 ):
echo |\ openssl s_client -connect <servername>:<port> 2>&1 |\ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem
其中 <servername> 和 <port> 是所要访问的提供 REST API 系统的 IP 地址和端口。
在 HTTP 通讯过程当中,部分 Web 服务须要验证访问该服务的用户身份,能够经过修改 axis2.xml 中的参数实现,也能够经过在代码中增长相应语句实现(后续章节会具体讲解),须要修改参数以下:
作完以上这些参数配置以后,若是一开始编译源码时,就添加了参数 enable-openssl 而且值为 yes,咱们这里就不须要从新编译源码了,只须要从新启动 Axis2 Server 就能够了,不然须要从新编译而且添加参数 enable-openssl 为 yes 值。
对于提供了 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 所示:
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 所示:
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 所示:
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 所示:
/***** 对 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"]}。
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"}}。
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 所示。
axutil_stream_t *request_body = NULL; axutil_stream_write(request_body,env,body_request,body_request_len);// 写入消息体
清单 8 详细写出了一个完整的请求发送,响应接收过程当中方法的调用,涉及到的方法均包含在文件 axis2_http_client.h 中。
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 服务,客户端主要经过标准方法对资源进行访问及处理,标准方法主要包括四种:GET、PUT、POST 以及 DELETE。下面将分别介绍这四种方法的使用。
GET 操做会列出该服务所提供的相关数据信息,实现 GET 操做的完整示例能够下载咱们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_get 方法。运行该方法后能够获得相似于以下图所示的运行结果:
POST 操做能够经过消息体中含有的信息,执行资源的建立。实现 POST 操做完整示例能够下载咱们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_post 方法。运行该方法后能够获得相似于以下图所示的运行结果:
PUT 操做主要用于对资源进行修改,须要修改的内容包含在 requestBody 中。实现 PUT 操做完整示例能够下载咱们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_put 方法。运行该方法后能够获得相似于以下图所示的运行结果:
DELETE 操做主要用于对资源进行删除。实现 DELETE 操做完整示例能够下载咱们的 Sample Code,参考文件 rest_sample.c 当中的 test_rest_delete 方法。运行该方法后能够获得相似于以下图所示的运行结果:
图表 19 DELETE 操做运行结果
另外,附上清单 9,经常使用状态码列表清楚的列出了各个返回码的含义,方便读者查阅:
200 :表示服务器成功执行了客户端的 HTTP/HTTPS 请求 201 :表示服务器按客户端的请求成功建立了一个新资源 202 :表示服务器成功接收客户端请求并进行处理 204 :表示服务器已经成功处理了客户端请求,可是没有可返回的内容 303:表示服务器将客户端的请求重定向到另外一个 URI,要完成请求必须进行进一步操做 304 :表示请求资源不能被更改 400 :表示客户端的请求参数不合法或没有表达足够的信息 401 :表示由于安全的缘由,致使对资源的操做没有完成 404 :表示客户端请求的资源不存在,即 URI 无效 405 :表示请求的资源不支持该操做 409 :表示 URI 指定的资源发生冲突 500 :表示服务器端发生非预期状况,致使请求没有完成 503 :表示处理请求的服务目前不可用
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 所示:
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 转换为字符串,结果以下图所示:
固然,你也可使用 cJSON.h 当中的方法来建立 JSON 格式的数据字符串。首先使用方法 cJSON_CreateObject 来建立根节点,而后使用 cJSON_AddItemToObject 添加你想建立的数据项。下面一个例子就是如何生成 JSON 字符串 {"entry ":{"name":"Andrew","phone":"555 123 456"} }。请看清单 11 所示:
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 转换为
字符串并打印出来,结果以下图所示:
在 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,这里就再也不一一叙述了。