tumihub / loap
用于快速部署Laravel和Lumen中的SOAP服务,自动发现WSDL定义。
Requires
- laminas/laminas-soap: ^2.8
This package is auto-updated.
Last update: 2024-09-27 13:24:42 UTC
README
Laravel和Lumen的即时SOAP服务器,将任何类转换为WS-I兼容的SOAP服务,并自动发现WSDL定义。通过包装Laminas SOAP组件,提供服务的简单声明性配置,无需额外编码。
概述
系统要求
Laravel或Lumen框架,版本5.2或更高。
基本步骤
设置服务快速且简便
- 将此包安装到您的Laravel或Lumen应用程序中。
- 发布配置文件以进行自定义。
- 定义服务的配置。
示例
已配置并准备测试的演示服务。这可以用作从现有类创建自己的服务的模板。WSDL自动发现和生成取决于您是否已正确注释了服务类的属性和方法,如DemoService
类中所示,并在下文解释。
架构
此包在SOAP通信和WSDL生成中使用document/literal wrapped
模式,但如果有必要,可以扩展LoapController
类以部署其他模式。
安装
从您的Laravel或Lumen应用程序的根目录,通过Composer安装
composer require tumihub/loap
安装后,将LoapServiceProvider
添加到服务提供者列表中
对于Laravel
在config/app.php
中添加此行。如果您使用的是Laravel 5.5或更高版本,此步骤不是必需的。
Tumihub\Loap\LoapServiceProvider::class,
对于Lumen
在bootstrap/app.php
中添加此行
$app->register(Tumihub\Loap\LoapServiceProvider::class);
您还可以安装irazasyed/larasupport包,该包为Lumen添加了一些基本Laravel功能,包括发布包文件的支持。这将允许您发布Loap配置和视图文件以进行自定义,如以下下文所述;否则,您可以只需将文件从包手动复制到其发布位置。
配置
loap.php
配置文件包含通用设置和单个服务的配置。
运行此命令将loap.php
配置文件发布到项目的config
目录以进行自定义
php artisan vendor:publish --tag='loap'
这还将SOAPFault响应模板发布到您的项目的resources/views/vendor/loap
目录。
日志记录
启用时,异常的完整错误信息,包括跟踪堆栈,将被记录。
服务
LoapController
类配置与key
路由参数匹配的服务的服务器(见下文路由)。这样,您可以通过简单地在这里定义它们来通过SOAP服务器提供服务。
提供的示例服务是演示服务;其配置如下所示
'services' => [ 'demo' => [ 'name' => 'Demo', 'class' => 'Tumihub\Loap\Demo\DemoService', 'exceptions' => [ 'Exception' ], 'types' => [ 'keyValue' => 'Tumihub\Loap\Demo\Types\KeyValue', 'product' => 'Tumihub\Loap\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('loap/{key}/server', [ 'as' => 'loap.server.wsdl', 'uses' => '\Tumihub\Loap\LoapController@server' ]); app()->router->post('loap/{key}/server', [ 'as' => 'loap.server', 'uses' => '\Tumihub\Loap\LoapController@server' ]);
使用此路由或根据需要创建新路由以通过 LoapController
类访问您的 SOAP 服务,使用相同的 URL 参数 {key}
指示服务配置的密钥。密钥 'demo' 用于查找 Demo 服务配置。
用法
SOAP 是一个复杂的规范,具有各种实现,并且由于许多原因可能难以处理。此包将许多实现细节从开发人员那里抽象出来。
您需要定义您的 SOAP API,使用 PHP DocBlock 注释在所有公共类属性和方法上;这被自动发现过程用于定义您的服务。请参阅下面的 Demo 部分,以了解一个真实实现的概述,该实现作为示例提供,以帮助您入门。
示例
与此包一起提供的 Demo SOAP 服务是一个简单的实现示例,具有常用的配置值。 DemoService
类引用了一个虚构的提供程序(DemoProvider
类),它返回一些硬编码的结果,仅用于说明将应用程序功能作为 SOAP 服务公开的概念。
下面提供了对 Demo 服务方法的请求示例,以及预期的响应。将请求中的 'http://example.com' 替换为您的 Laravel 应用的实际域名。
Demo 服务类提供了一个示例,说明服务器如何自动将方法参数和返回值转换为适当的数据格式。下面显示了 DemoService
类中 getProducts
方法的 DocBlock。
/** * Returns an array of products by search criteria. * * @param \Tumihub\Loap\Types\KeyValue[] $criteria * @param string $token * @param string $user * @param string $password * @return \Tumihub\Loap\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/loap/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/loap/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/loap/demo/server"> <types> <xsd:schema targetNamespace="http://example.com/loap/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/loap/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/loap/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/loap/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/loap/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/loap/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 请求,并使用如下所示的正文内容。用户名、密码和 token 参数将与硬编码值进行验证,因此您可以更改它们以查看失败结果。Demo 服务还包括获取单个产品或产品数组的函数,以演示返回复杂对象的方法的结果格式。
认证
请求
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oper="http://example.com/loap/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/loap/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/loap/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/loap/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/loap/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/loap/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/loap/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/loap/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 客户端(https://www.getpostman.com/)直接使用 XML 请求测试您的服务。此包的 tests
目录中包含 Demo 服务的Postman 集合文件;您可以在 Postman 内部或通过 Newman 在命令行中运行测试套件(请参阅 Postman 文档)。将集合变量 domain
设置为您的 Laravel 应用程序的域名(而不是 'http://example.com')。
许可
本软件在 MIT 许可证 下提供使用。
变更日志
版本发布情况在 变更日志 中跟踪。