问题描述
我必须使用PHP来实现SOAP Web服务.
I have to implement a SOAP Web Service using PHP.
我通过使用SoapServer
类做到了,并且一切正常.
I did it by using the SoapServer
class and all works fine.
我需要为请求使用一种特定的格式:它们必须包含一个"Header"
标签和一个"Authentication"
标签,其中必须使用一个令牌来认证执行该请求的客户端.
I need to use a specific format for the request: they have to contain a "Header"
tag with an "Authentication"
tag in which there is a token that I have to use to authenticate the client that performed the request.
我使用"file_get_contents('php //input')"
来获取接收到的整个请求,然后将其解析以检索所需的令牌.
I used "file_get_contents('php //input')"
to get the entire request that I received and then parsed it to retrieve the token that I needed.
如果我尝试使用SoapUI模拟SOAP请求,则此方法很好.但是,如果我尝试使用PHP SoapClient进行请求并使用函数SoapHeader
设置标头,则在服务器端"file_get_contents('php //input')"
仅返回整个请求的字段(包含在XML请求的XML标记中) )合并为一个字符串,而不是以字符串格式返回整个XML.我不明白为什么.
This works fine if I try to simulate a SOAP request by using SoapUI. But, if I try to do the request by using PHP SoapClient and use the function SoapHeader
to set the header, on the server side "file_get_contents('php //input')"
returns only the fields of the entire request (contained in the XML tags of the XML request) merged together in a string, instead of returning the entire XML in a string format.I cannot understand why.
推荐答案
SoapServer
类在PHP文档中没有很好的记录. SoapServer类完全自动完成您要记住的所有事情.您必须使用装饰器类.装饰器是什么及其作用,我将在接下来的几行中进行解释.我正尝试向您提供正确的方向.
The SoapServer
class isn 't well documented in the PHP documentation. The SoapServer class does everything that you have in mind completely automatically. You have to use a decorator class. What a decorator is and what it does I 'll explain in the next lines. I 'm trying to give you a push in the right direction.
前一段时间,我必须实现WSSE认证标准.在本示例中,我将从WSSE标准中摘录一些内容.
A while ago I had to implement the WSSE authentication standard. I 'll take some parts from the WSSE standard for this example.
传入请求的标头看起来像这样...
The incoming request had a header that looked like this ...
<soapenv:Header>
<wsse:Security xmlns:wsc="http://schemas.xmlsoap.org/ws/2005/02/sc" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsc:SecurityContextToken>
<wsc:Identifier>identifier</wsc:Identifier>
</wsc:SecurityContextToken>
</wsse:Security>
</soapenv:Header>
键(标识符)标识执行网络服务功能的授权用户.从这个意义上说,我们必须在执行任何功能之前检查密钥是否有效.为此,我们需要一个装饰器类,该装饰器类在执行实际功能之前执行.
The key (identifier) identifies an authorized user to perform a function of the web service. In this sense, we must check that the key is valid before executing any function. For this purpose we need a decorator class, that is executed before the actual function is executed.
class AuthDecorator
{
/**
* Name of the class, which contains the webservice methods
* @var string
*/
protected $class;
/**
* Flag, if the recieved identifier is valid
* @var boolean
*/
protected $isValid = false;
public function getClass() : string
{
return $this->class;
}
public function setClass($class) : AuthDecorator
{
$this->class = $class;
return $this;
}
public function getIsValid() : bool
{
return $this->isValid;
}
public function setIsValid(bool $isValid) : AuthDecorator
{
$this->isValid = $isValid;
return $this;
}
public function __call(string $method, array $arguments)
{
if (!method_exists($this->class, $method)) {
throw new \SoapFault(
'Server',
sprintf(
'The method %s does not exist.',
$method
)
);
}
if (!$this->getIsValid()) {
// return a status object here, wenn identifier is invalid
}
return call_user_func_array(
[ $this->class, $method ],
$arguments
);
}
/**
* Here 's the magic! Method is called automatically with every recieved request
*
* @param object $security Security node form xml request header
*/
public function Security($security) : void
{
// auth against session or database or whatever here
$identifier = $this->getIdentifierFromSomewhereFunc();
if ($security->SecurityContextToken->Identifier == $identfier) {
$this->setIsValid(true);
}
}
}
这是装饰器类.看起来很简单,嗯?装饰器包含一个类,该类的名称类似于接收到的请求的xml标头的第一个孩子.每当我们通过soap服务器收到请求时,该方法将自动执行.装饰者除了检查被调用的soap服务器功能是否可用之外.如果不是,则抛出肥皂故障,表明消费者方的肥皂客户收到了.如果存在一种方法也很容易.我们将每个webservice方法放入一个类中.
That 's the decorator class. Looks easy, hm? The decorator contains a class named like the first child of the xml header of the recieved request. This method will be executed automatically every time we recieve a request with the soap server. Beside that the decorator checks, if the called soap server function is available. If not a soap fault is thrown that the soap client on the consumer side recieves. If a method exists is quite easy, too. Every webservice method we put in a class.
class SimpleWebservice
{
public function doSomeCoolStuff($withCoolParams) : \SoapVar
{
// do some fancy stuff here and return a SoapVar object as response
}
}
出于说明目的,我们的Web服务仅具有此功能.
For illustrative purposes, our web service just has this one function.
但是我们到底如何将装饰器与soap服务器一起使用?
容易,伙伴. SoapServer
类具有一些非常棘手的功能,未记录.该类具有称为setObject
的方法.这种方法可以解决问题.
Easy, mate. The SoapServer
class has some pretty tricky functionality, that is not documented. The class has a method called setObject
. This method will do the trick.
$server = new \SoapServer(
$path_to_wsdl_file,
[
'encoding' => 'UTF-8',
'send_errors' => true,
'soap_version' => SOAP_1_2,
]
);
$decorator = new AuthDecorator();
$decorator->setClass(SimpleWebservice::class);
$server->setObject($decorator);
$server->handle();
太棒了,对不对?只需初始化SoapServer
类,使用setObject
方法添加装饰器,然后使用handle
方法运行它.肥皂服务器接收所有请求,并且在调用webservice方法之前,装饰器将检查标识符是否有效.仅当标识符有效时,才会执行被调用的webservice方法.
That 's awesome, right? Just initializing the SoapServer
class, add the decorator with the setObject
method and run it with the handle
method. The soap server recieves all requests and before calling the webservice method the decorator will check, if the identifier is valid. Only if the identifier is valid, the called webservice method will be executed.
肥皂客户端请求的外观如何?
在另一方面,肥皂客户端看起来像这样...
On the other side the soap client can look like this ...
$client = new SoapClient(
$path_to_wsdl_file,
[
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
'exceptions' => true,
'trace' => true,
]
);
$securityContextToken = new \stdClass();
$securityContextToken->Identifier = 'identifier';
$securityContextToken = new \SoapVar(
$securityContextToken,
SOAP_ENC_OBJ,
null,
null,
'SecurityContextToken',
'http://schemas.xmlsoap.org/ws/2005/02/sc'
);
$security = new stdClass();
$security->SecurityContextToken = $securityContextToken;
$security = new \SoapVar(
$security,
SOAP_ENC_OBJ,
null,
null,
'Security',
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
);
$header = new \SoapHeader(
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
'Security',
$security
);
$client->__setSoapHeaders($header);
$result = $client->doSomeCoolStuff(new \SoapParam(...));
结论
在面向对象的上下文中工作时,SoapServer
和SoapClient
类非常酷.因为文档并没有真正提供有关这两个类的太多信息,所以您必须进行测试和学习.如果知道,您可以轻松创建SOAP Web服务.无需将任何xml编写为字符串.
When working in an object orientated context the SoapServer
and SoapClient
classes are pretty cool. Because the documentation doesn 't really give much about both classes, you have to test and learn. You can easily create a SOAP webservice when you know how. Without writing any xml as a string.
在有效率地使用此处显示的代码示例之前,请确保它们仅是示例,并非用于生产用途.所显示的示例应将您推向正确的方向. ;)
Before you productively use the code examples seen here, please make sure that they are only examples and not intended for productive use. The shown examples should push you in the right direction. ;)
问题?
这篇关于用PHP实现SOAP Web服务时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!