viewflex / zoap
用于快速部署Laravel和Lumen中的SOAP服务,具有WSDL定义的自动发现功能。
Requires
- laminas/laminas-soap: ^2.9
This package is auto-updated.
Last update: 2024-09-06 20:50:17 UTC
README
为Laravel和Lumen提供即时SOAP服务器,将任何类转换为WS-I兼容的SOAP服务,具有WSDL定义的自动发现功能。通过Laminas SOAP组件提供服务的简单声明性配置,无需额外编码。
概述
系统要求
Laravel或Lumen框架,版本5.2或更高。
基本步骤
设置服务快速且无痛苦
- 在您的Laravel或Lumen应用程序中安装此包。
- 发布配置文件以进行自定义。
- 定义服务的配置。
示例
已经配置并准备测试的演示服务。这可以作为从现有类创建您自己的服务的模板。WSDL的自动发现和生成取决于您是否已正确注释了服务类的属性和方法,如DemoService
类中所示并如下解释。
架构
此包在SOAP通信和WSDL生成中使用document/literal wrapped
模式,但在必要时,可以扩展ZoapController
类以部署不同的模式。
安装
从您的Laravel或Lumen应用程序的根目录,通过Composer进行安装
composer require viewflex/zoap
安装后,将ZoapServiceProvider
添加到服务提供商列表
Laravel
在config/app.php
中添加此行。如果您使用的是Laravel版本5.5或更高,此步骤不是必需的。
Viewflex\Zoap\ZoapServiceProvider::class,
Lumen
在bootstrap/app.php
中添加此行
$app->register(Viewflex\Zoap\ZoapServiceProvider::class);
您还可以安装irazasyed/larasupport包,该包为Lumen添加了一些基本的Laravel功能,包括发布包文件的支持。这将允许您根据以下说明发布Zoap配置和视图文件进行自定义;否则,您可以只需将这些文件手动从包复制到其发布位置。
配置
zoap.php
配置文件包含通用设置和单个服务的配置。
运行此命令以将zoap.php
配置文件发布到项目的config
目录进行自定义
php artisan vendor:publish --tag='zoap'
这还将发布SoapFault响应模板到您的项目resources/views/vendor/zoap
目录。
日志记录
启用时,将记录异常的完整错误信息,包括跟踪堆栈。
服务
ZoapController
类配置了与key
路由参数匹配的服务服务器(见下文路由)。这样,您可以通过在这里定义它们来通过SOAP服务器提供任何数量的类。
提供的示例服务是演示服务;其配置如下所示
'services' => [ 'demo' => [ 'name' => 'Demo', 'class' => 'Viewflex\Zoap\Demo\DemoService', 'exceptions' => [ 'Exception' ], 'types' => [ 'keyValue' => 'Viewflex\Zoap\Demo\Types\KeyValue', 'product' => 'Viewflex\Zoap\Demo\Types\Product' ], 'strategy' => 'ArrayOfTypeComplex', 'headers' => [ 'Cache-Control' => 'no-cache, no-store' ], 'options' => [] ] ],
名称
指定服务在生成的WSDL文件中显示的名称。
类
指定您想要提供服务的类。此类的公共属性和方法将由SOAP服务器提供。
异常
列出您希望捕获并转换为SoapFault
的异常。使用Exception
可以捕获所有异常,或者您可以更具体地列出单个子异常。不在白名单上的任何异常都可能返回不可预测的结果,包括没有结果。我们不希望服务器直接返回SoapFault
,因为这可能会暴露堆栈跟踪;相反,异常被捕获,然后以带有适当信息的SoapFault
返回。
类型
根据需要添加复杂数据类型——通常自动发现会找到它们,但如果找不到,可以在此处指定——自动发现不会重复添加相同的数据类型。
策略
指定要用于自动发现的这些ComplexTypeStrategyInterface
实现之一
- AnyType
- ArrayOfTypeComplex
- ArrayOfTypeSequence
- DefaultComplexType
如果没有指定,将使用ArrayOfTypeComplex
策略。
头部
如果没有在此处指定,将自动设置一个Content-Type
头部为'application/xml; charset=utf-8'。指定任何其他所需的HTTP响应头部。
选项
指定此服务的服务器选项数组(可选)。
路由
包路由文件路由Demo服务
app()->router->get('zoap/{key}/server', [ 'as' => 'zoap.server.wsdl', 'uses' => '\Viewflex\Zoap\ZoapController@server' ]); app()->router->post('zoap/{key}/server', [ 'as' => 'zoap.server', 'uses' => '\Viewflex\Zoap\ZoapController@server' ]);
使用此路由或根据需要创建新路由以访问ZoapController
类上的SOAP服务,使用相同的URL参数{key}
表示服务配置的键。键'demo'用于查找Demo服务配置。
用法
SOAP是一个复杂的规范,有各种实现,并且由于许多原因可能难以工作。此包将大部分实现细节从开发者那里抽象出来。
您需要使用PHP DocBlock注解在所有公共类属性和方法上定义您的SOAP API;这被自动发现过程用于定义您的服务。请参阅下面的示例部分,以了解提供的真实实现示例,以帮助您入门。
示例
此包中提供的Demo SOAP服务是一个简单的实现示例,具有常用的配置值。DemoService
类引用了一个虚构的提供者(DemoProvider
类),它返回一些硬编码的结果,仅用于说明将应用程序功能作为SOAP服务公开的概念。
下面提供了对Demo服务方法的请求示例,以及预期的响应。将请求中的'http://example.com'替换为您Laravel应用程序的实际域名。
Demo服务类提供了一个示例,说明服务器如何自动将方法参数和返回值转换为适当的数据格式。下面是DemoService
类中getProducts
方法的DocBlock
/** * Returns an array of products by search criteria. * * @param \Viewflex\Zoap\Types\KeyValue[] $criteria * @param string $token * @param string $user * @param string $password * @return \Viewflex\Zoap\Types\Product[] * @throws SoapFault */ public function getProducts($criteria = [], $token = '', $user = '', $password = '')
此方法返回一个Product
对象的数组,包装并格式化为XML字符串,如下所示。
WSDL生成
SOAP服务器必须提供一个WSDL文件才能识别它预期处理的方法和数据模型,但考虑到规范的复杂性和缺乏良好的文档,手动编写一个是很困难且容易出错的。此包使用自动发现来自动生成WSDL文件。
可以通过向Demo路由发出带有空URL参数'wsdl'
的GET请求来获取Demo服务的WSDL定义
http://example.com/zoap/demo/server?wsdl
它应返回一个完整的WSDL文件,描述Demo服务。
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://example.com/zoap/demo/server" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" name="Demo" targetNamespace="http://example.com/zoap/demo/server"> <types> <xsd:schema targetNamespace="http://example.com/zoap/demo/server"> <xsd:element name="auth"> <xsd:complexType> <xsd:sequence> <xsd:element name="user" type="xsd:string"/> <xsd:element name="password" type="xsd:string"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="authResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="authResult" type="soap-enc:Array"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="ping"> <xsd:complexType> <xsd:sequence> <xsd:element name="token" type="xsd:string" nillable="true"/> <xsd:element name="user" type="xsd:string" nillable="true"/> <xsd:element name="password" type="xsd:string" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="pingResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="pingResult" type="xsd:boolean"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="getProduct"> <xsd:complexType> <xsd:sequence> <xsd:element name="productId" type="xsd:int"/> <xsd:element name="token" type="xsd:string" nillable="true"/> <xsd:element name="user" type="xsd:string" nillable="true"/> <xsd:element name="password" type="xsd:string" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="Product"> <xsd:all> <xsd:element name="id" type="xsd:int"/> <xsd:element name="name" type="xsd:string" nillable="true"/> <xsd:element name="category" type="xsd:string" nillable="true"/> <xsd:element name="subcategory" type="xsd:string" nillable="true"/> <xsd:element name="price" type="xsd:float" nillable="true"/> </xsd:all> </xsd:complexType> <xsd:element name="getProductResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="getProductResult" type="tns:Product"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="KeyValue"> <xsd:all> <xsd:element name="key" type="xsd:string"/> <xsd:element name="value" type="xsd:string"/> </xsd:all> </xsd:complexType> <xsd:complexType name="ArrayOfKeyValue"> <xsd:complexContent> <xsd:restriction base="soap-enc:Array"> <xsd:attribute ref="soap-enc:arrayType" wsdl:arrayType="tns:KeyValue[]"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType> <xsd:element name="getProducts"> <xsd:complexType> <xsd:sequence> <xsd:element name="criteria" type="tns:ArrayOfKeyValue" nillable="true"/> <xsd:element name="token" type="xsd:string" nillable="true"/> <xsd:element name="user" type="xsd:string" nillable="true"/> <xsd:element name="password" type="xsd:string" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="ArrayOfProduct"> <xsd:complexContent> <xsd:restriction base="soap-enc:Array"> <xsd:attribute ref="soap-enc:arrayType" wsdl:arrayType="tns:Product[]"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType> <xsd:element name="getProductsResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="getProductsResult" type="tns:ArrayOfProduct"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </types> <portType name="DemoPort"> <operation name="auth"> <documentation>Authenticates user/password, returning status of true with token, or throws SoapFault.</documentation> <input message="tns:authIn"/> <output message="tns:authOut"/> </operation> <operation name="ping"> <documentation>Returns boolean authentication result using given token or user/password.</documentation> <input message="tns:pingIn"/> <output message="tns:pingOut"/> </operation> <operation name="getProduct"> <documentation>Returns a product by id.</documentation> <input message="tns:getProductIn"/> <output message="tns:getProductOut"/> </operation> <operation name="getProducts"> <documentation>Returns an array of products by search criteria.</documentation> <input message="tns:getProductsIn"/> <output message="tns:getProductsOut"/> </operation> </portType> <binding name="DemoBinding" type="tns:DemoPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="auth"> <soap:operation soapAction="http://example.com/zoap/demo/server#auth"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="ping"> <soap:operation soapAction="http://example.com/zoap/demo/server#ping"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="getProduct"> <soap:operation soapAction="http://example.com/zoap/demo/server#getProduct"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> <operation name="getProducts"> <soap:operation soapAction="http://example.com/zoap/demo/server#getProducts"/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="DemoService"> <port name="DemoPort" binding="tns:DemoBinding"> <soap:address location="http://example.com/zoap/demo/server"/> </port> </service> <message name="authIn"> <part name="parameters" element="tns:auth"/> </message> <message name="authOut"> <part name="parameters" element="tns:authResponse"/> </message> <message name="pingIn"> <part name="parameters" element="tns:ping"/> </message> <message name="pingOut"> <part name="parameters" element="tns:pingResponse"/> </message> <message name="getProductIn"> <part name="parameters" element="tns:getProduct"/> </message> <message name="getProductOut"> <part name="parameters" element="tns:getProductResponse"/> </message> <message name="getProductsIn"> <part name="parameters" element="tns:getProducts"/> </message> <message name="getProductsOut"> <part name="parameters" element="tns:getProductsResponse"/> </message> </definitions>
服务方法
要访问服务方法,请使用具有“Content-Type”头部为“application/xml”或“text/xml”的POST请求,并按照以下内容提供主体。将使用硬编码的值对user
、password
和token
参数进行验证,因此如果您更改它们,可以看到失败的结果。Demo服务还包括获取单个产品或产品数组的函数,以展示返回复杂对象的函数的结果格式。
身份验证
请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://example.com/zoap/demo/server"> <soapenv:Header/> <soapenv:Body> <oper:auth> <oper:user>test@test.com</oper:user> <oper:password>tester</oper:password> </oper:auth> </soapenv:Body> </soapenv:Envelope>
响应
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/zoap/demo/server"> <SOAP-ENV:Body> <ns1:authResponse> <authResult> <item> <key>status</key> <value>true</value> </item> <item> <key>token</key> <value>tGSGYv8al1Ce6Rui8oa4Kjo8ADhYvR9x8KFZOeEGWgU1iscF7N2tUnI3t9bX</value> </item> </authResult> </ns1:authResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
ping
请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://example.com/zoap/demo/server"> <soapenv:Header/> <soapenv:Body> <oper:ping> <oper:token>tGSGYv8al1Ce6Rui8oa4Kjo8ADhYvR9x8KFZOeEGWgU1iscF7N2tUnI3t9bX</oper:token> </oper:ping> </soapenv:Body> </soapenv:Envelope>
响应
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/zoap/demo/server"> <SOAP-ENV:Body> <ns1:pingResponse> <pingResult>true</pingResult> </ns1:pingResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
getProduct
请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://example.com/zoap/demo/server"> <soapenv:Header/> <soapenv:Body> <oper:getProduct> <oper:productId>456</oper:productId> <oper:token>tGSGYv8al1Ce6Rui8oa4Kjo8ADhYvR9x8KFZOeEGWgU1iscF7N2tUnI3t9bX </oper:token> </oper:getProduct> </soapenv:Body> </soapenv:Envelope>
响应
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/zoap/demo/server"> <SOAP-ENV:Body> <ns1:getProductResponse> <getProductResult> <id>456</id> <name>North Face Summit Ski Jacket</name> <category>Outerwear</category> <subcategory>Women</subcategory> <price>249.98</price> </getProductResult> </ns1:getProductResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
getProducts
请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://example.com/zoap/demo/server"> <soapenv:Header/> <soapenv:Body> <oper:getProducts> <oper:criteria> <oper:keyValue> <oper:key>category</oper:key> <oper:value>Outerwear</oper:value> </oper:keyValue> </oper:criteria> <oper:token>tGSGYv8al1Ce6Rui8oa4Kjo8ADhYvR9x8KFZOeEGWgU1iscF7N2tUnI3t9bX</oper:token> </oper:getProducts> </soapenv:Body> </soapenv:Envelope>
响应
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://example.com/zoap/demo/server"> <SOAP-ENV:Body> <ns1:getProductsResponse> <getProductsResult> <ns1:Product> <id>456</id> <name>North Face Summit Ski Jacket</name> <category>Outerwear</category> <subcategory>Women</subcategory> <price>249.98</price> </ns1:Product> <ns1:Product> <id>789</id> <name>Marmot Crew Neck Base Layer</name> <category>Outerwear</category> <subcategory>Men</subcategory> <price>95.29</price> </ns1:Product> </getProductsResult> </ns1:getProductsResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
测试
您可以使用例如Postman的HTTP客户端直接使用XML请求测试您的服务。Demo服务的Postman收集文件包含在此软件包的tests
目录中;您可以从Postman内部或通过Newman(参见Postman文档)运行收集的测试套件。将收集变量domain
设置为您的Laravel应用程序的实际域名(而不是http://example.com)。
许可证
本软件提供使用MIT许可证。
变更日志
版本发布记录在变更日志中。