我正在设置一个 SOAP 网络服务,它应该返回一个复合消息。
此消息的有效实例如下:
<dl190Response xmlns="http://pse/">
<cdhead cisprik="5563167"/>
<mvts>
<mvts_S att="a1">
<x>x1</x>
<w>w1</w>
</mvts_S>
<mvts_S>
<x>x2</x>
<w>w2</w>
</mvts_S>
</mvts>
</dl190Response>
所有这些都在 wsdl 中明确定义:
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://pse/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
name="PSE"
targetNamespace="http://pse/">
<types>
<xs:schema xmlns="http://pse/" targetNamespace="http://pse/">
<xs:complexType name="cdhead_T">
<xs:attribute name="cisprik" type="xs:long"/>
</xs:complexType>
<xs:complexType name="mvts_T">
<xs:sequence>
<xs:element name="mvts_S" type="mvts_S_T" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="mvts_S_T">
<xs:sequence>
<xs:element name="x" type="xs:string"/>
<xs:element name="w" type="xs:string"/>
</xs:sequence>
<xs:attribute name="att" type="xs:string" use="optional"/>
</xs:complexType>
</xs:schema>
</types>
<message name="DL190Req">
<part name="cdhead" type="tns:cdhead_T"/>
</message>
<message name="DL190Res">
<part name="cdhead" type="tns:cdhead_T"/>
<part name="mvts" type="tns:mvts_T"/>
</message>
<portType name="DLPortType">
<operation name="dl190">
<input message="tns:DL190Req"/>
<output message="tns:DL190Res"/>
</operation>
</portType>
<binding name="DLBinding" type="tns:DLPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="dl190">
<soap:operation soapAction="http://www.testServer.com/test_soap.php#dl190"/>
<input>
<soap:body use="literal" namespace="http://pse/"/>
</input>
<output>
<soap:body use="literal" namespace="http://pse/"/>
</output>
</operation>
</binding>
<service name="PSE">
<port name="DLPortType" binding="tns:DLBinding">
<soap:address location="http://www.testServer.com/test_soap.php"/>
</port>
</service>
</definitions>
我一直在服务器端 test_soap.php 上无休止地工作以使其正确,但我没有成功。 在返回 XML 之前正常工作的部分内容如下:
<?php
class PSE {
function dl190 ($arg) {
//I don't need to extract the input data just now
mysql_connect('127.0.0.1:3306', 'user', 'password');
mysql_select_db('myDatabase');
$xml = new SimpleXMLElement('<dl190Res/>');
$xml -> addChild('cdhead');
$mvts = $xml -> addChild('mvts');
$rows = mysql_query('select * from trx');
while($data = mysql_fetch_assoc($rows)) {
$mvts_S = $mvts -> addChild('mvts_S');
foreach($data as $key => $value) {
if ($key == 'att') { $mvts_S -> addAttribute($key, $value);}
else {$mvts_S -> addChild($key, $value);}
}
};
$dom = dom_import_simplexml ($xml) -> ownerDocument;
// now respond to the request and return the XML
}
};
ini_set( "soap.wsdl_cache_enabled", "0");
$server = new SoapServer ("test.wsdl");
$server -> setClass ('PSE');
$server -> setObject (new PSE());
$server -> handle();
?>
我几乎尝试了所有我能想到的方法来获得正确的响应,但我没有成功。我能够对之前仅包含一个部分的消息执行相同的操作(请参阅我最近的问题+答案)。
但是在这里,有两个消息部分,我没有成功。
当然,在让 soap 服务器将其包装到 Envelope+Body 之后,$xml 内容的调试表明这正是我希望看到的返回内容。
实际上情况与只有一个消息部分的情况不同:只要我先剥离 XML 声明并返回它,我就可以从一个部分创建一个新的 SoapVar。在这里我不能这样做,因为返回值由两部分组成。
所以我想知道我现在应该做什么:
我感谢所有帮助,所以 SOAP 专家们,请不要犹豫,尝试回答这个问题!
添加
作为临时解决方法,我编辑了 WSDL,将响应消息更改为只有一个部分。这允许我将预期的消息作为预期的两个部分的串联传递(或与此相关的任何其他消息,因为 SoapVar 没有对返回值进行消息定义结构 WSDL 检查):
$xml1 = new SimpleXMLElement('<cdhead/>');
$xml1 -> addAttribute ('xmlns', 'http://pse/');
$xml1 -> addAttribute ('cisprik', $newCisprik);
$xml2 = new SimpleXMLElement('<mvts/>');
$rows = mysql_query('select * from trx');
while($data = mysql_fetch_assoc($rows)) {
$mvts_S = $xml2 -> addChild('mvts_S');
foreach($data as $key => $value) {
if ($key == 'att') { $mvts_S -> addAttribute($key, $value);}
else {$mvts_S -> addChild($key, $value);}
}
};
$dom1 = dom_import_simplexml ($xml1) -> ownerDocument;
$dom2 = dom_import_simplexml ($xml2) -> ownerDocument;
$part1 = $dom1 -> saveXML($dom1 -> documentElement);
$part2 = $dom2 -> saveXML($dom2 -> documentElement);
$result = new SoapVar ($part1 . $part2, XSD_ANYXML);
特别之处在于连接当然不是有效的 XML,缺少周围的根元素,但 SoapVar 无论如何都能够解析它。
就是这样:对 SoapVar 和 SoapParam/SoapServer 有详细了解的任何人都可以解释是否完全有可能返回两个消息部分?
并说明如何操作?
或者,提供有关如何在其他 SOAP 设置中执行此操作的详细信息?
最佳答案
我尝试并设置了最小的 SoapServer,这是我所做的:
这是我的调用请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pse="http://pse/">
<soapenv:Header/>
<soapenv:Body>
<pse:dl190>
<cdhead cisprik="0"/>
</pse:dl190>
</soapenv:Body>
</soapenv:Envelope>
由于您对数据库的调用,它最初没有工作,但我知道您真的只需要一个解决方案来解决如何在 soap 层上正确响应,其余的您会弄清楚。
这是一个简单的解决方案:
<?php
class PSE
{
public function dl190($arg)
{
//var_dump($arg) is:
//object(stdClass)#3 (1) {
// ["cisprik"]=> int(0)
//}
$fakeResult = array();
$fakeResult[0] = new stdClass();
$fakeResult[0]->cisprik = 23;
$fakeResult[1] = array();
$fakeResult[1][0] = new stdClass();
$fakeResult[1][0]->att = "a1";
$fakeResult[1][0]->x = "x1";
$fakeResult[1][0]->w = "w1";
$fakeResult[1][1] = new stdClass();
//$fakeResult[1][1]->att = "a1";
$fakeResult[1][1]->x = "x2";
$fakeResult[1][1]->w = "w2";
return $fakeResult;
}
}
//ini_set("soap.wsdl_cache_enabled", "0");
$server = new SoapServer ("wsdl.xml");
$server->setObject(new PSE());
$server->handle();
请注意,PHP 基本上只在请求参数中发出 stdClass 和数组的混合(我将您得到的内容作为评论转储在顶部)。这是一件令人难过的事情,但我相信在同一级别上做出回应而不是通过使用 XML 作为返回方式使事情变得更糟是一件公平的事情。
如果您针对此代码执行上述请求,您将获得此 soap 响应:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://pse/">
<SOAP-ENV:Body>
<ns1:dl190Response>
<cdhead cisprik="23"/>
<mvts>
<mvts_S att="a1">
<x>x1</x>
<w>w1</w>
</mvts_S>
<mvts_S>
<x>x2</x>
<w>w2</w>
</mvts_S>
</mvts>
</ns1:dl190Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
但是还有改进的余地。 PHP SoapServer(以及 SoapClient)有一个称为类映射的特性,我强烈建议您使用它。如果您的 IDE 支持任何类型的 PHPDoc 自动完成,您几乎可以在任何处理正确设置值的地方利用它。
这是我的带有类映射定义的版本。请注意,我在它们前面都加上了“PSE”前缀,以强调类名不需要以 WSDL 中的复杂类型命名这一事实。
<?php
class PSE
{
public function dl190(PSE_cdhead_T $arg)
{
// var_dump($arg) is:
// object(PSE_cdhead_T)#3 (1) {
// ["cisprik"]=> int(0)
// }
$fakeResult = array();
$fakeResult[0] = new PSE_cdhead_T();
$fakeResult[0]->cisprik = 23;
$fakeResult[1] = array();
$fakeResult[1][0] = new PSE_mvts_S_T();
$fakeResult[1][0]->att = "a1";
$fakeResult[1][0]->x = "x1";
$fakeResult[1][0]->w = "w1";
$fakeResult[1][1] = new PSE_mvts_S_T();
//$fakeResult[1][1]->att = "a1";
$fakeResult[1][1]->x = "x2";
$fakeResult[1][1]->w = "w2";
return $fakeResult;
}
}
class PSE_cdhead_T {
/**
* @var int
*/
public $cisprik;
}
class PSE_mvts_S_T {
/**
* @var string
*/
public $att;
/**
* @var string
*/
public $x;
/**
* @var string
*/
public $w;
}
//ini_set("soap.wsdl_cache_enabled", "0");
$classmap = array(
'cdhead_T' => 'PSE_cdhead_T',
'mvts_S_T' => 'PSE_mvts_S_T',
);
$serverOptions = array(
'encoding' => 'utf-8',
'classmap' => $classmap,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
);
$server = new SoapServer ("wsdl.xml", $serverOptions);
$server->setObject(new PSE());
$server->handle();
不幸的是,一个烦人的问题没有解决:在您的响应中,您不能使用类,而必须使用一个数组,而没有任何关于哪个索引参数映射到哪个 xml 结果的提示。这真的很糟糕,但要更改它,您必须更改 WSDL。
我很不高兴地报告说我不是创建 WSDL 文件的专家。我试图添加一个复杂类型作为响应中的唯一元素。如果您查看我的第二个版本中的转储,您会发现您得到了一个类 PSE_cdhead_T,它是请求消息的唯一部分的映射复杂类型。
因为响应消息有两部分,所以 SoapServer 必须将它们放在一个数组中。没有可能的命名引用。我建议您在此处添加一个新的 complexType 并在 map 中相应地创建一个新类,如下所示:
class PSE_DL190_Response
{
/**
* @var PSE_cdhead_T
*/
public $cdhead;
/**
* @var PSE_mvts_S_T[]
*/
public $mvts;
}
然后您可以更轻松地准备响应:
$fakeResult = new PSE_DL190_Response();
$fakeResult->cdhead = new PSE_cdhead_T(); // Set the one cdhead structure
$fakeResult->mvts[] = new PSE_mvts_S_T(); // Add one mvts structure;
这很可能会导致您的 XML 响应发生变化 - 但我无法评估其影响。
最后一个想法:有一些适用于 PHP 的 WSDL 代码生成器,您可以尝试一下。他们将自动生成类映射所需的类。上次我尝试它们时,它们似乎可以工作,但不适用于我测试的所有 WSDL 文件。 Soap 的定义似乎太复杂了,无法做到这一点。但如果它有效,那么与其手动创建它们是非常值得的。
关于php - 如何填充复合消息并作为 SoapServer 响应 XML 返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12772307/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返