tumihub/loap

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

dev-master 2020-08-27 03:41 UTC

This package is auto-updated.

Last update: 2024-09-27 13:24:42 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模式,但如果有必要,可以扩展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 许可证 下提供使用。

变更日志

版本发布情况在 变更日志 中跟踪。