PHP调用SOAP API遇WSDL策略扩展错误的技术问询
问题背景
我之前用过SOAP API,但长期专注于REST API开发,现在重新对接SOAP API时遇到了问题。使用PHP的SoapClient调用API时,出现如下错误:
SOAP-ERROR: Parsing WSDL: Unknown required WSDL extension 'http://schemas.xmlsoap.org/ws/2004/09/policy'
查看受登录密码保护的WSDL地址的XML输出,发现包含以下标签:
<wsdl:definitions xmlns:p1="urn:haixin:all2crm" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="SI_USBPC_REQUEST" targetNamespace="urn:haixin:all2crm"> <wsdl:documentation/> <wsp:UsingPolicy wsdl:required="true"/> <wsp:Policy wsu:Id="OP_SI_USBPC_REQUEST"/>
以及下方的:
<wsp:Policy> <wsp:PolicyReference URI="#OP_SI_USBPC_REQUEST"/> </wsp:Policy>
搜索后得知,常见解决方案是将<wsp:UsingPolicy wsdl:required="true"/>改为false,但我无法修改该API的远程WSDL。且策略名称OP_SI_USBPC_REQUEST无搜索结果,推测是API自定义名称。
针对以上情况,我有以下问题:
问题1:是否需要生成策略并附加到SOAP客户端请求中?
不需要。从你提供的WSDL内容来看,OP_SI_USBPC_REQUEST这个策略标签是空的,没有定义任何实际的安全规则或服务约束。这种情况下,生成策略并附加到请求完全是多余的,服务端大概率也没有针对该策略做校验逻辑。
问题2:策略与扩展的关联是什么?为何关闭策略要求可消除扩展缺失错误?
http://schemas.xmlsoap.org/ws/2004/09/policy是WS-Policy标准的命名空间,属于WSDL的扩展规范,用来定义服务的约束要求(比如安全、可靠性规则)。PHP的SoapClient原生不支持这个扩展,当WSDL中标记wsdl:required="true"时,客户端会强制要求解析该扩展,无法识别就会抛出错误。将该属性改为false后,客户端会忽略这个扩展的必填要求,自然就不会报错了。
问题3:该扩展是否可下载?格式是什么?是否需要附加到SOAP客户端请求?
这是一套标准的WS-Policy规范,没有单独的“扩展文件”可以下载。它是基于XML的规则定义,PHPSoapClient原生不支持,就算你获取到规范文档,也无法直接附加到请求中解决当前错误。
问题4:该扩展是否需在服务器安装?添加扩展后错误是否会消除?当前已附加用户名密码调用API,是否需额外处理策略要求?
PHP官方没有针对WS-Policy的原生扩展,第三方可用扩展也极少,就算找到并安装,也不一定能适配这个API的自定义空策略。你已经通过login和password参数完成了认证,且WSDL中的策略没有实际内容,不需要额外处理策略相关的要求。
问题5:若下载WSDL到本地并关闭策略要求,能否用本地文件对接在线API?(本人认为WSDL仅为接口定义,无法用于对接在线API,是否正确?)
你的想法错误,完全可以这么做。WSDL本身就是接口定义文件,只要本地修改后的WSDL中,服务地址(通常是<wsdl:address>标签的location属性)指向在线API的真实地址,使用本地WSDL创建SoapClient就能正常对接在线服务。这是绕开远程WSDL扩展错误的常用方法,具体步骤:
- 下载远程WSDL到本地
- 将
<wsp:UsingPolicy wsdl:required="true"/>修改为wsdl:required="false" - 代码中
SoapClient的WSDL路径替换为本地文件路径
参考代码
原调用代码
$options = [ 'login' => 'mlogin', 'password' => 'mypassword', ]; $client = new \SoapClient("http://domain:port/dir/wsdl/?p=sa/92130e1ffa97338ba2d1fc026567031d" , $options );
修改后(使用本地WSDL)
$options = [ 'login' => 'mlogin', 'password' => 'mypassword', ]; // 替换为你的本地WSDL文件路径 $client = new \SoapClient("./local.wsdl" , $options );
内容的提问来源于stack exchange,提问作者max

