viewflex/zoap

用于快速部署Laravel和Lumen中的SOAP服务,具有WSDL定义的自动发现功能。

1.0.5 2021-02-24 20:46 UTC

This package is auto-updated.

Last update: 2024-09-06 20:50:17 UTC


README

GitHub license

为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请求,并按照以下内容提供主体。将使用硬编码的值对userpasswordtoken参数进行验证,因此如果您更改它们,可以看到失败的结果。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许可证

变更日志

版本发布记录在变更日志中。