目前我发现不少的项目都需要使用到调用服务器的接口获取相应的业务数据,实现客户端和服务器间的通信交互,特别在App应用和网站开发时尤其明显。但是就PHP程序员而言,大多数情况下都是使用CURL进行请求,甚至用fopen来操作。对于这种开发方式,就个人而言,不是说不好,只是不够好。也许不是因为性能上,也不是安全上,而是对于Web Services这一领域的规范上,毕竟我们要朝着专业的方向要求自己。
而且现在Web Services的思想和应用都已日渐成熟,还有REST这样的接口开发规范,以及SOAP、RPC等各种协议,和各种开源的开发框架。为什么不使用这些规范、协议和开发框架更好地进行接口请求呢?
这里的目的不是发明一种新的思想,或者是新的协议,而是把现在主流的Web Services各方面整合起来,并提供一个轻量级的开发构架,以应对服务器接口轻易编写和客户端的快速调用,让前后端的开发人员可以更加关注业务上的构建,进行无绪开发,同时又不丢失Web Services的好处。
所以,就有了zenphpWS3。
zenphpWS3是自主开发、利用PHP实现并基于phprpc的Web Services轻量级开发框架,融合了开源社区的优秀框架精神和相关开发框架、应用框架(如Yii、ThinkPHP、phpcms、RedBean 等),支持远程调用协调RPC、简单对象接入协议SOAP和HTTP协议,具有 便于开发、便于使用、便于扩展三大特点。
目前提供的主要有:
用于服务器接口开发的PHP开发框架
实现接口调用的JAVA客户端
zenWS3完全开发手册 - 初稿 - 20130317.pdf
希望通过这个框架、这些文档能给您带来的,不仅仅是减少开发量、实现项目需求,更希望得到的是知道如何更好地设计、开发和单元测试、重构、敏捷开发等实践。
温馨提示:此框架会努力保持更新和维护。
在部署好zenphpWS3开发框架后,添加新的接口类如:./services/Controllers/ExamplesController.class.php ,并实现了获取欢迎语的getWelcome接口,同时为该接口定义了非必须的类型为字符串的名字参数,默认值为nobody,然后返回结果。也就是服务器接口开发主要工作在于:参数规则定义+接口业务实现。
class ExamplesController extends Controller { public function getRules() { return array( 'getWelcome' => array( 'name' => array('type' => 'string', 'default' => 'nobody', 'require' => false, ), ), ); } public function getWelcome() { $this->succeed(); return array('content' => 'Hello Wolrd', 'name' => $this->name); } }
在实现了服务器接口后,就可以对其进行访问。如简单的直接使用浏览器输入接口链接(http方式):http://localhost/index.php?c=examples&a=getWelcome&p=http&f=json,得到的结果如下:
{"status":"OK","data":{"content":"Hello Wolrd","name":"nobody"},"error":"","debug":[]}
这种方式的请求或应用于基本全部的开发语言,稍后会有相关的调用示例。
如果使用JAVA语言开发客户端,则可以使用提供的JAVA包,进行快速请求,对上面的示例接口使用如下:
package com.dogstar.shakenext.test; import com.dogstar.shakenext.apiclient.*; import com.dogstar.shakenext.apiclient.SN_Response.SN_Response_Status; import junit.framework.Assert; import junit.framework.TestCase; public class SN_ApiClient_Test extends TestCase { public void testHelloWorld() { SN_ApiClient client = SN_ApiClient.getInstance(); SN_Params params = SN_Params.newInsance() .withHost("http://dogstar.api.shakenext.com") .withController("Examples") .withAction("getWelcome") .set("name", "dogstar"); SN_Response response = client.request(params); Assert.assertEquals(SN_Response_Status.OK, response.getStatus()); Assert.assertEquals("{\"content\":\"Hello Wolrd\",\"name\":\"dogstar\"}", response.getData()); } }
这里使用了单元测试进行演示,以便更好的说明验证的内容。同时在开发过程中,应该坚持单元测试,除了可以进行意图导向开发,还可为自己编写的代码提供更多的安全保证。
以下为php客户端使用RPC协议进行接口请求的示例:
<?php require (dirname(__FILE__)."phprpc/phprpc_client.php"); $host = "http://localhost/index.php?"; //服务器域名 $sysParam = "c=examples&a=getWelcome&p=http&f=json"; //系统参数 $appParam = "&name=phper"; //应用参数 $client = new PHPRPC_Client(); $client->useService($host.$sysParam.$appParam); $data = $client->response(); //请求响应 if($data instanceof PHPRPC_Error) { //异常处理。。。 echo $data->toString(); } //处理返回的数据 var_dump($data); ?>
由于对服务端接口开发框架已经有编写了文档进行详细说明,这里不再重复。但会挑一点值得关注和了解的点来进行说明。毕竟,当你决定使用第三方的框架或者SDK包时,应该了解其底层和原理,以便更好地利用和完善。同时不要绝对相信第三方,因为代码是人写,我们连自己的代码都不能完全相信,更不应该完全相信和依赖别人的代码。
在开发框架部署好后,可以看到统一入口文件index.php。这里演示了加载和使用开发框架是如此的方便,如同很多其他开源框架一样。
<?php // 加载框架公共入口类文件 require(dirname(__FILE__).'/ZenWebService.class.php'); //实例化一个Web Server应用实例 ZenWebService::run(); ?>
这里的参数规则参考自很多框架(如Yii)对参数的处理方式。同样,为了减少后端开发人员对参数获取的关注,且加强对参数的过滤、检测和处理,这里引入了参数规则。这样的话,可以做到一条规则,多处使用。开发人员可以说不用任何开发量,只需要简单配置参数的规则,便可通过$this->name这样的方式获取客户端提供的参数。
当需要定义参数规则时,只需要在Controller类下重定义getRules()即可。如下:
protected function getRules() { return array( '*' => array( 'name' => array('type' => 'string', 'default' => 'nobody', 'require' => false, ), ), 'getInfo' => array( 'sex' => array('type' => 'string', 'default' => 'unkonw', 'require' => false, ), ), ); }
上面定义了两条参数规则,分别从上到下是姓名和性别。并且姓名name应用于该Controller下的全部接口,但性别sex只应用于getInfo接口。这两个参数都是非必须参数,且有默认值。
我想,参数规则应该是这个开发框架中比较有意思的一个地方。
对于接口身份验证这一块,由于各项目的需求和约定不同,所以对于接口验证,各项目可以根据需求去实现。或者配置是否需要使用appKey和token。
为了展示JAVA客户端的简单调用,可以用一行代码来做示例:
SN_Response response = SN_ApiClient.getInstance().request(SN_Params.newInsance("http://localhost"));
但实际开发更应该使用更明确的写法,并且配合严格的单元测试,即使这里的一行调用代码很吸引人,但对睦读它的人理想成本很高。更理想的调用应该使用三行代码。
SN_Params params = SN_Params.newInsance("http://localhost/"); SN_ApiClient client = SN_ApiClient.getInstance(); SN_Response response = client.request(params);
很明显,上面的三行代码很好地说明了请求过程:
构建请求参数
构建请求实例
获取接口结果
之所以这样设计,是为了提高客户端开发人员的关注:我只需要关心给什么参数和获取返回结果就可以了,而不需要关心如何去请求。
为了轻松设置请求参数,以及区分业务参数和系统参数,这里的参数类SN_Params可使用连贯的设置方式。如下示例:
SN_Params params = SN_Params.newInsance(); //业务参数设置 params.set("name", "dogstar").set("year", 2014 + "").set("sex", "male"); //系统参数设置 params.withHost("http://localhost/").withAction("actionName").withController("controllerName");
当然,这里使用了工厂模式,而且是渐进式的工厂模式。从而可以以任意的顺序和方式来组装所需要的请求参数。
为了方便后期的扩展,和为开发人员提供更大的自由空间,并遵循开放-关闭原则,这里将接口请求的方式以及对接口结果的解析都利用代理模式和工厂模式进行了封装。后期如果需要进行扩展,可以在实现SN_Request或SN_Formatter接口,并对相应的工厂类进行扩展即可以外部调用。
并且当在工厂类里尝试创建一个非法的实例时,将会返回一个None对象,避免外部对null的判断或者对异常的处理。当然,在外部尝试使用这个不存在的实例进行操作时,还会抛出一个异常,以便告知开发人员。
请参考:[Web Services轻量级开发框架]zenphpWS3:PHP服务器/JAVA&PHP客户端(文档&源码&设计&测试)
1、JAVA客户端完善:补充SOAP调用、XML解析。
2、服务器开发框架重构和进一步优化。
3、文档完善
4、一个完整的示例项目并提供源代码