使用 PHP 实现 SOAP Web 服务时出错
Errors while implementing SOAP Web service with PHP
我必须使用 PHP 实现 SOAP Web 服务。
我使用 SoapServer
class 做到了,一切正常。
我需要为请求使用特定格式:它们必须包含一个 "Header"
标签和一个 "Authentication"
标签,其中有一个令牌,我必须使用它来验证客户端执行了请求。
我使用 "file_get_contents('php //input')"
获取我收到的整个请求,然后解析它以检索我需要的令牌。
如果我尝试使用 SoapUI 模拟 SOAP 请求,这会很好地工作。但是,如果我尝试通过使用 PHP SoapClient 来执行请求并使用函数 SoapHeader
来设置 header,仅在服务器端 "file_get_contents('php //input')"
returns整个请求的字段(包含在 XML 请求的 XML 标记中)合并到一个字符串中,而不是以字符串格式返回整个 XML。
我不明白为什么。
SoapServer
class 在 PHP 文档中没有详细记录。 SoapServer class 完全自动地完成您所想的一切。你必须使用装饰器class。什么是装饰器以及它的作用我将在下几行中解释。我正在努力推动您朝着正确的方向前进。
前段时间我不得不实施 WSSE 认证标准。我将从 WSSE 标准中提取一些部分用于此示例。
传入的请求有一个 header 看起来像这样...
<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>
密钥(标识符)标识授权用户执行 Web 服务的功能。从这个意义上讲,我们必须在执行任何功能之前检查密钥是否有效。为此,我们需要一个装饰器 class,它在实际函数执行之前执行。
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);
}
}
}
那就是装饰器 class。看起来很简单,嗯?装饰器包含一个 class,其名称类似于接收到的请求的 xml header 的第一个 child。每次我们收到 soap 服务器的请求时,都会自动执行此方法。除此之外,装饰器还会检查被调用的 soap 服务器函数是否可用。如果不是,则抛出消费者端的 soap 客户端接收到的 soap 故障。如果存在一种方法也很容易。我们在 class.
中放入的每个 Web 服务方法
class SimpleWebservice
{
public function doSomeCoolStuff($withCoolParams) : \SoapVar
{
// do some fancy stuff here and return a SoapVar object as response
}
}
为了便于说明,我们的网络服务只有这一项功能。
但是我们到底如何让装饰器与 soap 服务器一起工作?
放轻松,伙计。 SoapServer
class 有一些非常棘手的功能,没有记录。 class 有一个名为 setObject
的方法。这个方法可以解决问题。
$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
class,使用 setObject
方法添加装饰器,使用 handle
方法添加装饰器 运行。 soap 服务器接收所有请求,并且在调用 webservice 方法之前装饰器将检查标识符是否有效。只有标识符有效,调用的webservice方法才会被执行。
soap 客户端请求看起来怎么样?
另一方面,soap 客户端看起来像这样...
$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(...));
结论
在面向 object 的上下文中工作时,SoapServer
和 SoapClient
classes 非常酷。因为文档并没有真正给出关于这两个 classes 的太多信息,所以你必须测试和学习。当您知道如何操作时,您可以轻松地创建 SOAP Web 服务。不将任何 xml 写成字符串。
在您高效地使用此处看到的代码示例之前,请确保它们只是示例,并非用于生产用途。所示示例应将您推向正确的方向。 ;)
有问题吗?
我必须使用 PHP 实现 SOAP Web 服务。
我使用 SoapServer
class 做到了,一切正常。
我需要为请求使用特定格式:它们必须包含一个 "Header"
标签和一个 "Authentication"
标签,其中有一个令牌,我必须使用它来验证客户端执行了请求。
我使用 "file_get_contents('php //input')"
获取我收到的整个请求,然后解析它以检索我需要的令牌。
如果我尝试使用 SoapUI 模拟 SOAP 请求,这会很好地工作。但是,如果我尝试通过使用 PHP SoapClient 来执行请求并使用函数 SoapHeader
来设置 header,仅在服务器端 "file_get_contents('php //input')"
returns整个请求的字段(包含在 XML 请求的 XML 标记中)合并到一个字符串中,而不是以字符串格式返回整个 XML。
我不明白为什么。
SoapServer
class 在 PHP 文档中没有详细记录。 SoapServer class 完全自动地完成您所想的一切。你必须使用装饰器class。什么是装饰器以及它的作用我将在下几行中解释。我正在努力推动您朝着正确的方向前进。
前段时间我不得不实施 WSSE 认证标准。我将从 WSSE 标准中提取一些部分用于此示例。
传入的请求有一个 header 看起来像这样...
<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>
密钥(标识符)标识授权用户执行 Web 服务的功能。从这个意义上讲,我们必须在执行任何功能之前检查密钥是否有效。为此,我们需要一个装饰器 class,它在实际函数执行之前执行。
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);
}
}
}
那就是装饰器 class。看起来很简单,嗯?装饰器包含一个 class,其名称类似于接收到的请求的 xml header 的第一个 child。每次我们收到 soap 服务器的请求时,都会自动执行此方法。除此之外,装饰器还会检查被调用的 soap 服务器函数是否可用。如果不是,则抛出消费者端的 soap 客户端接收到的 soap 故障。如果存在一种方法也很容易。我们在 class.
中放入的每个 Web 服务方法class SimpleWebservice
{
public function doSomeCoolStuff($withCoolParams) : \SoapVar
{
// do some fancy stuff here and return a SoapVar object as response
}
}
为了便于说明,我们的网络服务只有这一项功能。
但是我们到底如何让装饰器与 soap 服务器一起工作?
放轻松,伙计。 SoapServer
class 有一些非常棘手的功能,没有记录。 class 有一个名为 setObject
的方法。这个方法可以解决问题。
$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
class,使用 setObject
方法添加装饰器,使用 handle
方法添加装饰器 运行。 soap 服务器接收所有请求,并且在调用 webservice 方法之前装饰器将检查标识符是否有效。只有标识符有效,调用的webservice方法才会被执行。
soap 客户端请求看起来怎么样?
另一方面,soap 客户端看起来像这样...
$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(...));
结论
在面向 object 的上下文中工作时,SoapServer
和 SoapClient
classes 非常酷。因为文档并没有真正给出关于这两个 classes 的太多信息,所以你必须测试和学习。当您知道如何操作时,您可以轻松地创建 SOAP Web 服务。不将任何 xml 写成字符串。
在您高效地使用此处看到的代码示例之前,请确保它们只是示例,并非用于生产用途。所示示例应将您推向正确的方向。 ;)
有问题吗?