You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在纯PHP/Laravel中配置WSA与WS-Addressing实现SOAP调用

解决VakifBank GetirDekont SOAP接口调用的WS-Addressing配置问题

问题背景

使用原生PHP SoapClient调用VakifBank的GetirDekont接口时,返回以下错误:

looks like we got no xml document or response
The requested URL was rejected. Please consult with your administrator.

但该WSDL在SoapUI中可正常调用,返回包含WS-Addressing头部的XML响应。核心原因是该接口强制要求请求携带WS-Addressing(WSA)头部信息,而PHP原生SoapClient默认不会自动生成这些头部。

纯PHP解决方案

通过自定义SOAP头部添加WSA必需字段,同时确保请求参数符合接口命名空间要求:

<?php
// 接口参数
$params = [
    'user_name' => '****',
    'password' => '****',
    'customer_num' => '***',
    'process_num' => 000
];

// WSDL地址
$wsdl = "https://vbtestservice.vakifbank.com.tr/HesapHareketleri.OnlineEkstre/SOnlineEkstreServis.svc?singleWsdl";

// 初始化SoapClient
$client = new SoapClient($wsdl, [
    'trace' => true,
    'exceptions' => 0,
    'soap_version' => SOAP_1_2 // 需根据WSDL确认SOAP版本,部分WCF服务要求1.2
]);

// 构建WS-Addressing头部
$wsaNs = 'http://www.w3.org/2005/08/addressing';
$action = 'Peak.Integration.ExternalInbound.Ekstre/ISOnlineEkstreServis/GetirDekont';

// 创建WSA头部元素
$actionHeader = new SoapHeader(
    $wsaNs,
    'Action',
    $action,
    true // 必须设置mustUnderstand为true
);

$messageId = new SoapHeader(
    $wsaNs,
    'MessageID',
    'urn:uuid:' . uuid_create(UUID_TYPE_RANDOM)
);

$toHeader = new SoapHeader(
    $wsaNs,
    'To',
    $wsdl
);

// 设置SOAP头部
$client->__setSoapHeaders([$actionHeader, $messageId, $toHeader]);

// 将参数包装到对应命名空间的请求节点下
$requestParams = [
    'GetirDekontRequest' => $params
];

// 调用接口
$result = $client->__soapCall('GetirDekont', [$requestParams]);

// 打印请求和响应日志用于调试
echo "Request:\n" . $client->__getLastRequest() . "\n";
echo "Response:\n" . $client->__getLastResponse() . "\n";
?>

关键说明

  • 确认soap_version匹配接口要求:多数WCF服务默认使用SOAP 1.2,需根据WSDL定义调整。
  • WSA的Action值必须与接口定义完全一致,可从SoapUI的请求中直接复制(注意是请求的Action,而非响应的)。
  • 参数必须包裹在对应命名空间的请求节点下,避免因参数结构不匹配被服务端拒绝。

Laravel框架解决方案

方案1:使用Guzzle手动构造SOAP请求

通过Guzzle直接发送带WSA头部的XML请求,灵活可控:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GuzzleHttp\Client;

class VakifBankController extends Controller
{
    public function getDekont()
    {
        $wsdl = "https://vbtestservice.vakifbank.com.tr/HesapHareketleri.OnlineEkstre/SOnlineEkstreServis.svc?singleWsdl";
        $client = new Client();

        // 构造符合要求的SOAP请求XML
        $xml = <<<XML
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
    <s:Header>
        <a:Action s:mustUnderstand="1">Peak.Integration.ExternalInbound.Ekstre/ISOnlineEkstreServis/GetirDekont</a:Action>
        <a:MessageID>urn:uuid:{$this->generateUuid()}</a:MessageID>
        <a:To s:mustUnderstand="1">$wsdl</a:To>
    </s:Header>
    <s:Body xmlns="Peak.Integration.ExternalInbound.Ekstre">
        <GetirDekont>
            <user_name>****</user_name>
            <password>****</password>
            <customer_num>***</customer_num>
            <process_num>000</process_num>
        </GetirDekont>
    </s:Body>
</s:Envelope>
XML;

        try {
            $response = $client->post($wsdl, [
                'headers' => [
                    'Content-Type' => 'application/soap+xml; charset=utf-8',
                    'SOAPAction' => 'Peak.Integration.ExternalInbound.Ekstre/ISOnlineEkstreServis/GetirDekont'
                ],
                'body' => $xml
            ]);

            $responseBody = $response->getBody()->getContents();
            // 解析XML响应
            $xmlResponse = simplexml_load_string($responseBody);
            dd($xmlResponse);
        } catch (\Exception $e) {
            dd($e->getMessage());
        }
    }

    // 生成UUID方法
    private function generateUuid()
    {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff)
        );
    }
}

方案2:使用laravel-soap扩展

安装扩展后配置WSA头部:

  1. 安装扩展
composer require artisaninweb/laravel-soap
  1. 在config/soap.php中添加客户端配置:
return [
    'clients' => [
        'vakifbank' => [
            'wsdl' => 'https://vbtestservice.vakifbank.com.tr/HesapHareketleri.OnlineEkstre/SOnlineEkstreServis.svc?singleWsdl',
            'options' => [
                'trace' => true,
                'exceptions' => false,
                'soap_version' => SOAP_1_2
            ]
        ]
    ]
];
  1. 在代码中调用接口并添加WSA头部:
<?php
use Artisaninweb\SoapWrapper\SoapWrapper;

class VakifBankController extends Controller
{
    public function getDekont(SoapWrapper $soapWrapper)
    {
        $soapWrapper->add('vakifbank', function ($service) {
            $service->wsdl(config('soap.clients.vakifbank.wsdl'))
                    ->options(config('soap.clients.vakifbank.options'));
        });

        // 构建WSA头部
        $wsaNs = 'http://www.w3.org/2005/08/addressing';
        $action = new SoapHeader($wsaNs, 'Action', 'Peak.Integration.ExternalInbound.Ekstre/ISOnlineEkstreServis/GetirDekont', true);
        $messageId = new SoapHeader($wsaNs, 'MessageID', 'urn:uuid:' . $this->generateUuid());
        $to = new SoapHeader($wsaNs, 'To', config('soap.clients.vakifbank.wsdl'), true);

        $soapWrapper->client('vakifbank')->__setSoapHeaders([$action, $messageId, $to]);

        $params = [
            'user_name' => '****',
            'password' => '****',
            'customer_num' => '***',
            'process_num' => 000
        ];

        $response = $soapWrapper->client('vakifbank')->call('GetirDekont', [$params]);
        dd($response);
    }

    private function generateUuid()
    {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff)
        );
    }
}

调试验证技巧

  • 开启trace参数后,使用__getLastRequest()输出请求XML,与SoapUI的请求对比,确保WSA头部和参数结构完全一致。
  • 检查请求的Content-Type:SOAP 1.2要求application/soap+xml,SOAP 1.1要求text/xml。

内容的提问来源于stack exchange,提问作者Mert 2973

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:40:31