作者: 吴俊杰

web service这项技术暂不说它有多落伍,但是项目中用到了,没法逃避!
    xml和json各有各的好处,但是JSON无疑是当今数据交互的主流了。客户soap服务器端用的是 java+xfire开发的,而我不懂java,可是我和客户的程序有数据交互,起初我推荐用json数据格式,但是客户执意要用web service。拗不过,只有研究 php的soap了。
    事实证明:这可不是一件容易的事情,web service虽号称 “跨平台与开发语言无关” ,实则是机关重重,陷阱遍布!网上的参考资料凌乱不堪,去糟存精也绝非易事,调通这个确实是走了不少弯路,在没有人指导的情况下,一直在网上search,一路之艰辛实属不易!下面就就记录一下php和java+xfire互为 web service服务器之间的一些细节,防止再次用到的时候,又走弯路,浪费时间。

1)普及知识:php的soap类库
参考:http://www.cnblogs.com/chance1/archive/2009/04/08/1431949.html

php有两个扩展可以实现web service,一个是NuSoap,另一个是php官方的soap扩展,由于soap是官方的,所以我们这里以soap来实现web service.由于默认是没有打开soap扩展的,所以自己先看一下soap扩展有没有打开。

在soap编写web service的过程中主要用到了SoapClient,SoapServer,SoapFault三个类。

SoapClient类
这个类用来使用Web services。SoapClient类可以作为给定Web services的客户端。
它有两种操作形式:
* WSDL 模式

* Non-WSDL 模式

在WSDL模式中,构造器可以使用WSDL文件名作为参数,并从WSDL中提取服务所使用的信息。

non-WSDL模式中使用参数来传递要使用的信息。

SoapServer类
这个类可以用来提供Web services。与SoapClient类似,SoapServer也有两种操作模式:WSDL模式和non-WSDL模式。这两种模式的意义跟 SoapClient的两种模式一样。在WSDL模式中,服务实现了WSDL提供的接口;在non-WSDL模式中,参数被用来管理服务的行为。
在SoapServer类的众多方法中,有三个方法比较重要。它们是SoapServer::setClass(),SoapServer::addFunction()和SoapServer::handle()。

下面给出实例
定义一个提供服务的php类,这个类所提供的函数就是web service对外提供的服务
(比较简单,例子略)

注意事项(非常重要)
php自带的soap扩展提供的类或构造函数是:SoapClient::SoapClient,详见《php5参考手册》 ;而nusoap提供的类库是:nusoap_client(有下划线) ,详见nusoap安装包源文件“lib/class.soapclient.php”文件。这里你会觉得很奇怪,为什么文件名不是带下划线"class.soap_client.php"?原来是这样的,nusoap为了兼容php自带的扩展soap的代码,在此类库文件最后面一行有如下代码:

if (!extension_loaded('soap')) {
class soapclient extends nusoap_client {
}
}
所以网上就出现了乱七八糟的 用第三方nusoap类库初始化soap客户端对象的例子,但是运行的时候程序都没有报错!例如:有的代码是 $client=new soapclient(); 有的是 $client = new nusoap_client(); 就是上面的原因!不过这里要说明的是,php扩展自带的soap类,已经能够很好的处理soap客户端调用,但是它的弊端就是如果它作为soap server要生成web service的WSDL,就显得无能为力了。而第三方的nusoap却恰好很容易生成WSDL。所以我这个项目就把两者都利用上了。【重要】为了不至于造成类库冲突,我还是修改了nusoap的 class.soapclient.php 文件的代码,把最后面的:
if (!extension_loaded('soap')) {
class soapclient extends nusoap_client {
}
}
删掉了。那么我打算这样,事实上也是尝试了很多方法都不行而被逼成这样的: php自带的soap扩展nusoap第三方类库必须在我的业务系统上共存兼容;我的业务系统(php代码实现)既是soap服务器端,也是soap客户端。我用nusoap作为我的soap server,因为客户的java机器要用web service调用我提供给它的函数,而且我要生成WDSL;而我的php脚本也要作为soap客户端调用客户java服务器上的函数的时候,不得不用php扩展自带soapclient这个类,至少我测试nusoap做soap client就是没法实现,soapclient这个自带的类库,也是侥幸可以调用。

==============================================================================

2) 同种语言的 web service 服务器端和客户端的实现
使用php soap扩展 建立 soap server和用php soap扩展的soapclient调用soap server 提供的函数非常简单,不用举例了。同理如果用java实现soap也应该很容易,不过我不懂。

3)使用php作为soap client去调用java xfire做的web service
调用wsdl的web service,从《php5参考手册》上看到的例子:
第一:调用成功

SoapClient::SoapClient
<?php
## 第一类方法初始化soapclient

$client = new SoapClient("some.wsdl"); ##方法一
$client = new SoapClient("some.wsdl", array('soap_version' => SOAP_1_2)); ##方法一
$client = new SoapClient("some.wsdl", array('login' => "some_name", ##方法一
'password' => "some_password"));
$client = new SoapClient("some.wsdl", array('proxy_host' => "localhost", ##方法一
'proxy_port' => 8080));
$client = new SoapClient("some.wsdl", array('proxy_host' => "localhost", ##方法一
'proxy_port' => 8080,
'proxy_login' => "some_name",
'proxy_password' => "some_password"));
## 下面是第二类方法初始化soapclient
$client = new SoapClient(null, array('location' => "http://localhost/soap.php",  ##方法二,注意 不可以带上"?wsdl"
'uri' => "http://test-uri/"));
$client = new SoapClient(null, array('location' => "http://localhost/soap.php",
'uri' => "http://test-uri/",
'style' => SOAP_DOCUMENT,
'use' => SOAP_LITERAL));
?>

据我测试 ,方法一(只要是明确指定了.wsdl文档的)完全行不通,如果使用方法一,那么总是无法与web service交互函数参数!总是报错,例如代码为:

$SoapClient = new SoapClient("http://192.168.100.187:8080/GBMP/services/CTIService?wsdl");
var_dump( $SoapClient->setICTCompanyInfo("中文字符")) ;
那么运行后,报错如下:
Fatal error: Uncaught SoapFault exception: [soap:Client] Not enough message parts were received for the operation
如果把上面的 wsdl文件下载下来后保存在本地,发现也不行!我猜测这可能与java有关,尽管soap是跨平台的。

【重要】后来发现只有方法二才能实现,代码如下:

$arrOptions=array(
//注意: 这个location指定的是server端代码在服务器中的具体位置, 而且不能在最后加上"?wdsl"字符串
'location'=>'http://192.168.100.187:8080/GBMP/services/CTIService',
#'location'=>'http://192.168.100.187:8080/GBMP/services/CTIService?wsdl', ##错误
'uri'=>'http://192.168.100.187:8080/',
);
$SoapClient = new SoapClient(null,$arrOptions); //实例化客户端对象
var_dump( $SoapClient->setICTCompanyInfo("中文字符"));
或者用:
$SoapClient->__call('setICTCompanyInfo',array("中文字符"));  ##如果用__call的方法调用,那么参数必须用一维索引数组!
第二:多参数的传递方法
如果要传递多个参数,可以直接在后面加参数列表就行,例如:
$SoapClient->setICTCompanyInfo("中文字符",$arg2,$arg3,......) ##这里有个陷阱,就是参数个数必须严格和server端函数一样,不可多传,否则报错
或者:
$SoapClient->__call('setICTCompanyInfo',array("中文字符",$arg2,$arg3,......))

提示:至于上面的soapclient功能 如何用 第三方类库(nusoap)实现,尝试过几次,发现都失败!暂时没有时间继续测试下去...

3) 使用php第三方类库nusoap输出wsdl文档
成功的源代码如下:

<?php
require('/lib/nusoap.php');
$nusoap_server = new nusoap_server();
$nusoap_server->soap_defencoding = 'UTF-8';  ##必须指定编码,否则如果参数有中文,那么对端会乱码
$nusoap_server->decode_utf8 = false;  ##必须指定编码,否则如果参数有中文,那么对端会乱码
$nusoap_server->configureWSDL('wsdl_test'); ##这里随便写吧,暂不知道有何用
$nusoap_server->register('getBccd',array('user'=>'xsd:string','pass'=>'xsd:string'),array('return'=>'xsd:string')); //正确

function getBccd($user,$pass){
return "Succeed! $user $pass 服务器 is ok!";
}
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA)?$HTTP_RAW_POST_DATA : '';
$nusoap_server->service($GLOBALS['HTTP_RAW_POST_DATA']);
?>

关于多参数的指定,在register()函数的第二个参数,必须是一个“关联数组”,上面“绿色”的对应关系,不多解释了!
其它:参考url:   http://blog.csdn.net/china_skag/article/details/7284227

$_POST:通过 HTTP POST 方法传递的变量组成的数组。是自动全局变量。
$GLOBALS['HTTP_RAW_POST_DATA'] :总是产生 $HTTP_RAW_POST_DATA 变量包含有原始的 POST 数据。此变量仅在碰到未识别 MIME 类型的数据时产生。$HTTP_RAW_POST_DATA 对于 enctype="multipart/form-data" 表单数据不可用。
也就是说基本上$GLOBALS['HTTP_RAW_POST_DATA'] 和 $_POST是一样的。
但是如果post过来的数据不是PHP能够识别的,你可以用 $GLOBALS['HTTP_RAW_POST_DATA']来接收,比如 text/xml 或者 soap 等等。
补充说明:PHP默认识别的数据类型是application/x-www.form-urlencoded标准的数据类型。
【好例子】

<?php
require_once("lib/nusoap.php");
$soap = new soap_server;
function savaNetData($moduleID,$pageArticleNumber,$webserverUrl){
$filename = 'config.txt';
$fp = fopen($filename, 'w+');

$content = "<config><moduleID>".$moduleID."</moduleID><number>".$pageArticleNumber
."</number><webserverUrl>".$webserverUrl."</webserverUrl></config>";
if ($fp) {
fwrite($fp, $content);
fclose($fp);
return 1;
} else {
die("Create File $filename error");
return -1;
}
}
$soap->configureWSDL("ServerPHPService",""); //初始化对WSDL的支持
// 注册服务
$soap->register('savaNetData',
array("moduleID"=>"xsd:int","pageArticleNumber"=>"xsd:int","webserverUrl"=>"xsd:string"), // 输入参数的定义
array("return"=>"xsd:int") // 返回值的定义
);
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA)?$HTTP_RAW_POST_DATA : '';
$soap->service($HTTP_RAW_POST_DATA);
?>

其它参考例子:
http://space.yoka.com/blog/923799/4665589.html

SOAP: java+xfire(web service) + php客户端的更多相关文章

  1. java axis web service

    编写 java调用web service的客户端比较简单,其中webservice为上一篇gsoap创建的server. package clientTest; import java.rmi.Rem ...

  2. <<Java RESTful Web Service实战>> 读书笔记

    <<Java RESTful Web Service实战>> 读书笔记 第一章   JAX-RS2.0入门 REST (Representational State ransf ...

  3. 【转】基于CXF Java 搭建Web Service (Restful Web Service与基于SOAP的Web Service混合方案)

    转载:http://www.cnblogs.com/windwithlife/archive/2013/03/03/2942157.html 一,选择一个合适的,Web开发环境: 我选择的是Eclip ...

  4. XFire Web Service客户端开发

    一.项目创建: 创建一个Maven的web工程 Maven包导入pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0&qu ...

  5. (转)Java实现Web Service过程中处理SOAP Header的问题

    网上有篇文章,大致这么说的(如下文),最后我采用的wsimport  -XadditionalHeaders的方式. StrikeIron offers two authentication meth ...

  6. JAVA开发Web Service几种框架介绍

    郑重声明:此文为转载来的,出处已不知了,侵告删. 在讲Web Service开发服务时,需要介绍一个目前开发Web Service的几个框架,分别为Axis,axis2,Xfire,CXF以及JWS( ...

  7. (转)JAVA 调用Web Service的三种方法

    1.使用HttpClient用到的jar文件:commons-httpclient-3.1.jar方法:预先定义好Soap请求数据,可以借助于XMLSpy Professional软件来做这一步生成. ...

  8. XFire Web Service

    Web Service 创建HelloWorldService项目 首先要启动Web Service Project 向导.该向导由三个页面组成,第一页设置Web项目配置的详细信息:第二页设置XFir ...

  9. Java 调用Web service 加入认证头(soapenv:Header)

    前言 有时候调用web service 会出现 Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ...

随机推荐

  1. k临近法的实现:kd树

    # coding:utf-8 import numpy as np import matplotlib.pyplot as plt T = [[2, 3], [5, 4], [9, 6], [4, 7 ...

  2. java 将长度很长的字符串(巨大字符串超过4000字节)插入oracle的clob字段时会报错的解决方案

    直接很长的字符串插入到clob字段中会报字符过长的异常,相信大家都会碰到这种情况 String sql = "insert into table(request_id,table_name, ...

  3. SSRS入门相关笔记

    1.SSRS Server 的地址的查看及设置:打开 开始->程序-> Microsoft SQL Server 2012/2014 -> Configuration Tools - ...

  4. 伪类选择器:root的妙用

    css3的元素旋转功能非常强大,也非常吸引人,但是很多时候因为浏览器使用率的问题,我们必需要想办法兼容一些低版本的浏览器,特别是ie这朵奇葩. 想要实现元素旋转本来很简单的一个属性就能实现,那就是tr ...

  5. SLA了解

    许多企业正要求服务品质协议(SLA),SLA 可以保证企业为之付费的 IT 服务的可靠性.随着 Web 服务成为主流,客户将要求保证服务质量的 SLA.在本文中,Judith M. Myerson 说 ...

  6. ajax方法完整的事件流

  7. adb failed to start daemon 的解决办法

    很多人遇到下面这个问题 * daemon not running. starting it now on port 5037 * ADB server didn't ACK<br>* fa ...

  8. javascript中的变量、执行环境、作用域

  9. 轻量级开源内存数据库SQLite性能测试

    [IT168 专稿]SQLite是一款轻型的数据库,它占用资源非常的低,同时能够跟很多程序语言相结合,但是支持的SQL语句不会逊色于其他开源数据库.它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品 ...

  10. tespeed-测试网速的Python工具

    1.安装(环境CentOS7) #pip install lxml #wget wget http://sourceforge.net/projects/socksipy/files/socksipy ...