1

我有一个设备定期向我在我的计算机上运行的 HTTP 服务器发送 SOAP 通知。通知如下所示:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <Notify>
            <DeviceNotification message= "
                    <NotificationEvent NotificationType="Location">
                            <ComputerLocation changedOn="1369757031051">
                            </ComputerLocation>
                    </NotificationEvent>
                "/>
        </Notify>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

它是一个 SOAP 信封,其中包含 DeviceNotification 类型实例的 XML 表示。

我真的不知道如何处理这些通知(好像我的程序是服务器?客户端?)。通知看起来更像是对命令的响应,但我实际上并没有调用任何远程服务(发送通知的机器有一个 Web 界面,我在其中输入“通知的目的地:此 ip,此端口”并开始发送他们)我也不确定通知中的 SOAP 是否完全正确。

我已经尝试了几个 SOAP 库等等......我认为我能够实现的最接近的事情是使用spyne(或者可能只是海市蜃楼,我认为这是因为它比其他库输出更多的东西)

class DeviceNotification(ComplexModel):
        ___namespace__ = ""
        pass

class HelloWorldService(ServiceBase):
    @rpc(DeviceNotification, _returns=Iterable(Unicode))
    def Notify(message):
        logging.warn("CHECKPOINT")
        print "message: %s" % message
        return ["foobarbaz"]

if __name__=='__main__':
    from wsgiref.simple_server import make_server
    PORT=7171
    application = Application([HelloWorldService],
                tns="",
                in_protocol=HttpRpc(validator='soft'),
                out_protocol=JsonDocument()
            )
    wsgi_application = WsgiApplication(application)
    server = make_server('', PORT, wsgi_application)
    server.serve_forever()

但是Notify函数永远不会被调用。我得到:

192.168.1.33 - - [28/May/2013 21:16:52] "POST / HTTP/1.1" 404 130
DEBUG:spyne.server.wsgi:Method name: '{}'
DEBUG:spyne.protocol.http:  header : {'soapaction': ['Notify'], 'host': ['niuyorker.jome:7171'], 'user_agent': ['Jakarta Commons-HttpClient/3.1']

这是我得到的最接近的东西。那个 SOAP 消息正确吗?如果是,以及这应该被视为对服务器(我的服务器)的请求还是作为响应(因此,我的应用程序将是一个客户端)。如果有人知道在哪里可以找到 Python 和 SOAP 的示例比添加两个数字或多次说“你好”更复杂一点,这也会有所帮助?

我想我总是可以使用lxml来解析整个 SOAP 消息,但如果可能的话,我想更“专业”地做一点。以防万一您没有注意到:我完全是 SOAP 服务的新手!:)

先感谢您!

更新:

看起来消息部分实际上应该是转义的 XML,它......嗯......这是我从一开始就得到的,但是(我是新手)我把它转义了,所以它在问题中看起来更漂亮。真实的是:

 <soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
 <soap-env:body>
  <notify>
   <notification message='&lt;NotificationEvent NotificationType="Location"&gt;&lt;DeviceLocation changedOn="1369074622065"&gt;&lt;/DeviceLocation&gt;&lt;/NotificationEvent&gt;'>
   </notification>
  </notify>
 </soap-env:body>
</soap-env:envelope>

我确实有 WSDL。使用suds我已经能够创建“ComputerLocation”类型的实例,例如:

>>> from suds.client import Client
>>> url="file:///tmp/service.wsdl"
>>> c=Client(url)
>>> c.factory.create('DeviceLocation')
(DeviceLocation){
   _changedOn = ""
 }

但这几乎是我所能做的。

老实说,我仍然不知道我需要服务器还是客户端......

4

2 回答 2

1

您提供的 XML 完全是一派胡言。我不相信您会找到任何能够处理它的 XML 解析器。message元素属性中的所有内容DeviceNotification都需要进行转义,以使 XML 格式正确。

首先,属性值中不能有左尖括号。这显然是违反格式约束的:http: //www.w3.org/TR/REC-xml/#CleanAttrVals

即使你已经过了这个,当属性值已经用双引号括起来时,你显然不能在属性中使用双引号。属性的开引号对NotificationType解析器来说是message属性的闭引号。

坦率地说,我认为您能做的最好的事情就是尝试使用某种正则表达式从中提取数据。它不是 XML,因此也不是 SOAP。

更新

随着问题中的更新,现在很清楚该 XML 的一些糟糕之处只是一个格式问题,所以我上面所说的一切都并不真正适用。不过,在属性中嵌入 XML 的概念仍然很荒谬。

在回答这个问题时,您是服务器还是客户端,从技术上讲,您是客户端。但是,因为这是一种通知模式,所以您是消息的接收者而不是发送者(这通常是客户端的情况)。

通常对于这种情况,我希望您向服务器发送一条消息,请求通知并提供接收这些通知的端点。在这种情况下,您已经通过单独的 Web 界面完成了所有这些操作。

所以剩下的就是让你接收消息,在这个角色中你更像是一个服务器。我可能对此有误,但我认为您应该使用 SOAP 库的服务器功能。

于 2013-05-29T00:32:12.460 回答
1

您正在使用in_protocol=HttpRpc而不是in_protocol=Soap11.

HttpRpc协议被简单地定义为:

所谓的 ReST-ish HttpRpc 协议实现。

虽然不完全清楚这意味着什么,但可以肯定的是,它不会尝试解析 SOAP 1.1 消息来查找命令,因为这与 REST-ish 相差无几。


已经过去了,并给出了您更新的(正确)示例:

它被解析为有效的 XML,并且位于有效的 SOAP 1.1 信封中。

但它仍然不是好的SOAP。正文的内容没有命名空间,也不是带有参数的结构化对象。事实上,整个内容是一棵通用 XML 树,而实际内容是另一棵通用 XML 树,它以转义形式存储为外部树中节点的属性。

从您的一个示例的外观来看,我猜 URL 路径始终是/,并且 SOAP 结构始终只是Notify直接位于 SOAP 信封内的单个结构。当然,如果您与您的框架进行了足够多的斗争,您可能可以让它在 上发送Notify,但是无论如何您的所有消息都会最终出现在同一个地方,所以……何必呢?如果有的话,您可能想要分派的是嵌入式 XML 中的某些内容,例如NotificationType.

如果这些猜测是正确的,请使用您知道如何使用的最熟悉的 Web 服务器——您甚至不需要 WSGI;如果需要,只需编写一个简单的处理程序http.server.HTTPServer——并将每条消息路由到相同的代码,该代码只进行足够的解析以获取NotificationEvent对象并从那里分派。像这样的东西:

from xml.etree import ElementTree as ET

def handle_location(nevent):
    clocation = nevent.find('ComputerLocation')
    changed_on = clocation.attrib['changedOn']
    location = clocation.text
    # do something with this info

def handle_other_thing(nevent):
    # whatever

handlers = {'Location': handle_location,
            'OtherThing': handle_other_thing }

et = ET.fromstring(body)
for notification in et.iter('notification'):
    message = ET.fromstring(notification.attrib['message'])
    for nevent in message.iter('NotificationEvent'):
        ntype = nevent.attrib['NotificationType']
        handlers[ntype](nevent)

听起来您可能对 . 中的最低级别字段具有 WSDL ,因此一旦到达该级别,NotificationEvent您可以使用它来代替手动解析 etree,如果它使您的生活更轻松或使您的代码更灵活。suds

当然,您需要添加一些错误处理。

于 2013-05-28T23:40:46.533 回答