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
相关产品推荐
相关产品推荐

