PHP Cookbook读书笔记 – 第14章使用Web服务
目前实现WEB服务比较流行的做法有二种
SOAP:(Simple Object Access Protocol,简单对象访问协议)传统的WEB服务解决方案,基于 XML的协议,它被设计成在WEB上交换结构化的和固化的信息。 SOAP可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。随着SOAP的发展已经越来越复杂,与其名称缩写相违背
REST:(Representational State Transfer,表示性状态转移)Roy Fielding 提出的一个描述互联系统架构风格的名词。REST是一种风格而不是一个标准。表现性状态传输试图描述一个设计优良的Web是如何运转的:用户在web网络(虚拟状态机)里通过点击链接处理应用(状态传输),结果就是传输并渲染下一个页面给用户(应用程序的下一个状态的表现性)。
除此之外,还有XML-RPC,类似SOAP的精简版,因为它也会把本地数据转换为一种中间性语言的格式,而你可以把转换后的数据传递到函数中并得到返回的结果。
下面的是2008年和2010年各种不同的API协议部署量的对比图
REST到底是什么?
SOAP其实已经很好理解了,因为是基于XML的(上一章节介绍过),而REST看了定义也不明白,其实REST是响应有关用户资源的HTTP POST/GET/PUT/DELETE 请求(这个基本 REST 设计原则建立了创建、读取、更新和删除操作与 HTTP 方法之间的一对一映射)。
- 若要在服务器上创建资源,应该使用 POST 方法。
- 若要检索某个资源,应该使用 GET 方法。
- 若要更改资源状态或对其进行更新,应该使用 PUT 方法。
- 若要删除某个资源,应该使用 DELETE 方法。
通过GET更新数据的案例:
GET /search.php?composer=beethoven&instrument=cello HTTP/1.1
上面是HTTP的请求内容,下面是PHP的实现代码
$base = 'http://music.example.org/search.php'; $params = array('composer' => 'beethoven', 'instrument' => 'cello'); $url = $base . '?' . http_build_query($params); $response = file_get_contents($url);
也可以通过cURL扩展来实现。关于REST的更多知识可以阅读《RESTful Web Services中文版》
通过PHP5的SOAP扩展来实现调用
PHP5的SOAP扩展不兼容PHP4,但优点却有很多:
- 用C编写的,它的执行速度快、效率高
- 从PHP5开始与PHP捆绑,从PHP5.1开始默认启用
- 与SOAP规范的许多部分保持一致
- 利用了PHP5的新特性,如异常处理
//通过PHP5的SOAP扩展来调用带WSDL的WEB服务 $wsdl_url = 'http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl'; $client = new SOAPClient($wsdl_url); $quote = $client->getQuote('EBAY'); // eBay, Inc. print $quote;
WSDL不是给程序员来看的,但对于计算机来说却是相当的友好,这一点表现在当你让SOAP扩展指向一个WSDL文件时,这个扩展会自动为响应的WEB服务创建一个对象,而你可以像操作PHP的类一样来操作这个对象。这个对象甚至能够知道每个方法都带什么参数,以及每个参数的类型。(SOAP与PHP不同,它是严格类型的)
如何设置SOAP类型
有些类型无法通过普通的PHP数据结构来实现,需要通过一些特殊的SOAP方法,如下面的例子
$ns = 'https://adwords.google.com/api/adwords/v2'; $job = new SOAPVar($data, SOAP_ENC_OBJECT, 'CustomReportJob', $ns); $response = $client->scheduleReportJob(array('job' => $job));
创建的XML像下面这样
...
这个NS1前缀会在SOAP-Envelope元素中映射
如何设置SOAP头部
在很多需要验证信息的web服务中经常会将验证的用户名、密码等通过SOAP头进行传递,使用SOAPHeader类创建投票信息如下代码所示:
$client = new SOAPClient('http://www.example.com/service.wsdl'); $username = new SOAPHeader('urn:service-namespace', 'Username', 'elvis'); $password = new SOAPHeader('urn:service-namespace', 'Password', 'the-king'); $headers = array($username, $password); //可以通过下面方式直接设置SOAPHeader $client->__setSoapHeaders($headers); //也可以在调用SOAP时将HEADER作为第4个参数添加进去 $client->__soapCall($function, $args, $options, $headers);
上面的代码实际创建的XML像下面这样:
elvis the-king
书中还提到了通过HTTP的基本验证方式来验证一个SOAP,我不知道真有哪个WEB服务会这么整(如果碰到我估计会满脸的黑线= = |||)
重新定义WSDL中WEB服务的接口地址
书中将(The WSDL file defines an endpoint for the service)这个翻译为“服务的终点”真叫人两眼一抹黑,在wikipedia上对endpoint有比较确切的定义:在面向服务架构中,endpoint是一个入口点,是一个服务、一个过程、一个队列或主题的目标。百度百科将其解释为绑定和网络地址的组合。
有过SOAP开发的同学可能都有用这个方法的经验,它可以解决实际开发中碰到的一种常见情形:对方提供的web服务的测试环境和生产环境开放的是一套接口但调用的地址不一样。这个时候通过下面的方法就可以解决从调用测试环境的服务转向生产环境的服务时只需要修改其接口地址。
$options = array('location' => 'http://www.example.com/testing-endpoint'); $client = new SOAPClient('http://www.example.com/service.wsdl', $options);
如果每个请求的endpoint都不一样:
$client = new SOAPClient('http://www.example.com/service.wsdl'); $method = 'getTemp'; $args = array('94114'); $options = array('location' => 'http://www.example.com/endpoint?method=getTemp'); $request = $client->__soapCall($method, $args, $options);
实践OO思想的一个好例子:
class TemperatureService extends SOAPClient { public function __call($method, $args) { // Modify endpoint to include method name // Assumes consistent naming convention $location = "http://www.example.com/endpoint?method={$method}"; $options = array('location' => $location); return $this->__soapCall($function, $args, array('location' => $location)); } } $client = new TemperatureService('http://www.example.com/service.wsdl'); $request = $client->getTemp('94114');
捕捉SOAP错误
当SOAP服务器产生错误时,会返回一个SOAP故障,可以通过try/catch结构检查SOAPFault类型的异常,或者是通过is_soap_fault()的返回值捕获
参考资料