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

Laravel 7基于Zend Soap构建SOAP服务及移动端调用故障排查

解决Laravel 7 + Zend Soap SOAP服务的WSDL加载、500错误及标签/Action格式问题

我来帮你一步步排查和解决这些问题,从WSDL加载失败、移动端HTTP 500错误,到WSDL标签顺序和SOAP Action格式的要求,逐个击破:

一、修复WSDL加载失败与移动端HTTP 500错误

1. 修正Laravel路由与CSRF配置

SOAP服务默认使用POST请求,首先确保你的路由配置正确并排除CSRF拦截:

  • 在routes/api.php中添加POST路由:
    Route::post('/server/account-action', [YourSoapController::class, 'handle']);
    
  • 在app/Http/Middleware/VerifyCsrfToken.php中排除该路由,避免Laravel拦截请求:
    protected $except = [
        '/api/server/account-action',
    ];
    

2. 调整后端SOAP Server初始化代码

你的代码中注释了正确的原生SoapServer配置,改用Zend Server时需要确保包装器和参数正确。修正后的Server逻辑:

else {
    // 初始化Zend Soap Server,明确指定document/literal风格
    $server = new Server($this->endpoint . '?wsdl', [
        'style' => SOAP_DOCUMENT,
        'use' => SOAP_LITERAL,
    ]);
    // 使用Zend的DocumentLiteralWrapped包装器,适配document/literal wrapped规范
    $server->setObject(new DocumentLiteralWrapped(new $this->service()));
    $server->registerFaultException($this->exceptions);
    $server->setOptions($this->options);
    
    // 处理请求并返回正确格式的响应
    $response = $server->handle();
    // 必须设置正确的Content-Type头,否则客户端无法解析
    return response($response)->header('Content-Type', 'text/xml; charset=utf-8');
}

注意:原代码中的DocumentLiteralWrapper是拼写错误,正确类名是DocumentLiteralWrapped。

3. 修复移动端请求的错误配置

你的Kotlin代码存在重复头、SOAP Action值错误等问题,修正后:

private val NAMESPACE = "http://10.2.0.114:8001/api/server/account-action";
private val METHODNAME = "withdrawDetail"
private val WSDL = "http://10.2.0.114:8001/api/server/account-action?wsdl";
private val SOAP_ACTION = "$NAMESPACE#$METHODNAME"
private val TAG = "soap"
var responseDump = ""

try {
    val envelope = SoapSerializationEnvelope(SoapEnvelope.VER11)
    // 适配document/literal风格,开启SOAP对象输出
    envelope.setOutputSoapObject(true)
    envelope.dotNet = false // 非.NET服务,禁用.NET兼容模式
    
    val request = SoapObject(NAMESPACE, METHODNAME)
    request.addProperty("withdrawId", "1")
    request.addProperty("token", "695E8784AE45B219F62C4EBE21E3E")
    
    val headerList: MutableList<HeaderProperty> = ArrayList()
    headerList.add(HeaderProperty("Authorization", "Bearer 695E8784AE45B219F62C4EBE21E3E"))
    headerList.add(HeaderProperty("soapAction", SOAP_ACTION)) // 使用带#的SOAP Action
    headerList.add(HeaderProperty("Content-Type", "application/xml; charset=UTF-8")) // 仅保留一个Content-Type
    
    envelope.bodyOut = request
    val transport = HttpTransportSE(WSDL)
    transport.debug = true
    
    try {
        transport.call(SOAP_ACTION, envelope, headerList)
        val requestDump = transport.requestDump
        responseDump = transport.responseDump
        Log.e(TAG, "响应内容: $responseDump")
    } catch (e: IOException) {
        e.printStackTrace()
        Log.e(TAG, "HTTP请求失败: ${e.message}")
    }
} catch (e: Exception) {
    e.printStackTrace()
    Log.e(TAG, "SOAP调用异常: ${e.message}")
}
return responseDump

二、调整WSDL标签顺序与SOAP Action格式

1. 自定义Wsdl类修改标签顺序

Zend Soap默认生成的WSDL标签顺序不符合要求,我们需要继承Zend的Wsdl类重写XML生成逻辑:

use Zend\Soap\Wsdl as ZendWsdl;

class CustomWsdl extends ZendWsdl
{
    /**
     * 重写XML生成方法,按指定顺序拼接标签
     */
    public function toXml()
    {
        // 分别生成各部分XML片段
        $types = $this->generateTypes();
        $portType = $this->generatePortType();
        $service = $this->generateService();
        $messages = $this->generateMessages();
        
        // 按要求的顺序拼接完整XML
        $xml = $this->getXmlDeclaration()
             . '<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"'
             . $this->getNamespacesDeclaration()
             . '>'
             . $types
             . $portType
             . $service
             . $messages
             . '</definitions>';
        
        return $xml;
    }
}

2. 替换AutoDiscover的Wsdl类并设置SOAP Action格式

在生成WSDL的代码中,使用自定义Wsdl类,并确保SOAP Action带#方法名:

if(isset($_GET['wsdl'])) {
    // 使用自定义Wsdl类
    $wsdl = new CustomWsdl('wsdl', $this->endpoint);
    foreach($this->types as $key => $class) {
        $wsdl->addType($class, $key);
    }
    
    $this->strategy->setContext($wsdl);
    foreach($this->types as $key => $class) {
        $this->strategy->addComplexType($class);
    }
    
    $discover = new AutoDiscover($this->strategy);
    $discover->setBindingStyle(['style' => 'document']);
    $discover->setOperationBodyStyle(['use' => 'literal']);
    $discover->setClass($this->service);
    $discover->setUri($this->endpoint);
    $discover->setServiceName($this->name);
    
    // 替换为自定义Wsdl类
    $discover->setWsdlClass(CustomWsdl::class);
    
    // 手动设置每个操作的SOAP Action为「命名空间#方法名」格式
    $generatedWsdl = $discover->getWsdl();
    foreach ($generatedWsdl->getOperations() as $operation) {
        $operation->setSoapAction($this->endpoint . '#' . $operation->getName());
    }
    
    // 输出WSDL并设置正确的Content-Type
    return response($generatedWsdl->toXml())->header('Content-Type', 'text/xml; charset=utf-8');
}

三、调试建议

  • 直接在浏览器访问WSDL地址,检查XML是否正常加载、标签顺序是否符合要求;
  • 查看Laravel日志(storage/logs/laravel.log),定位500错误的具体原因;
  • 使用SoapUI或Postman先测试后端服务,确认正常后再调试移动端代码。

内容的提问来源于stack exchange,提问作者Cat Fight

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:04:23