zoukankan      html  css  js  c++  java
  • SOAP: java+xfire(web service) + php客户端

    作者: 吴俊杰

    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

  • 相关阅读:
    Python基础-time and datetime
    Python基础-包
    Python基础-常用模块
    第四十七天Python学习记录
    第四十四天Python学习记录
    如何教你在NIPS会议上批量下载历年的pdf文档(另附04~14年NIPS论文下载链接)
    如何用pdfbox-app-1.8.10.jar批处理将pdf文档转换成text文档
    如何在Win10下设置图片的浏览方式为windows照片查看器
    如何不通过系统升级来安装window10正式版?(特别针对Xp用户)
    Mysql统计信息处理及binlog解释
  • 原文地址:https://www.cnblogs.com/voiphudong/p/3580351.html
Copyright © 2011-2022 走看看