Python 解析SOAP通知

Python 解析SOAP通知,python,parsing,soap,Python,Parsing,Soap,我有一个设备,它定期向我的计算机上运行的HTTP服务器发送SOAP通知。通知如下所示: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <Notify> <DeviceNotification message= " <No

我有一个设备,它定期向我的计算机上运行的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>
但是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的示例,这也会有所帮助,这比添加两个数字或多次说“你好”要复杂一些

我想我总是可以用它来解析整个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>
但这几乎是我所能做的


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

您使用的是\u protocol=HttpRpc中的
,而不是\u protocol=Soap11
中的

简单地定义为:

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

虽然还不完全清楚这意味着什么,但可以肯定的是,它不会试图解析SOAP1.1消息来查找命令,因为这与REST无关


已经克服了这一点,并给出了更新的(正确的)示例:

它解析为有效的XML,并且位于有效的SOAP1.1信封中

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

从您的一个示例的外观来看,我猜URL路径始终是
/
,SOAP结构始终只是SOAP信封中的一个
Notify
。当然,如果你与你的框架有足够的斗争,你可能会让它在
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)
听起来您可能在
NotificationEvent
中为最低级别的字段设置了WSDL,因此您可以在达到该级别后使用
suds
来代替手动解析etree,前提是它可以使您的生活更轻松或使您的代码更灵活


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

您提供的XML完全是胡说八道。我不相信您会发现任何XML解析器能够处理这个问题。
DeviceNotification
元素的
message
属性中的所有内容都需要转义才能使XML格式良好

首先,属性值中不能有左尖括号。这显然违反了良好格式约束:

即使你已经过了这一关,当属性值已经包含在双引号中时,你显然不能在属性中有双引号。
NotificationType
属性的open引号将在解析器看来是
message
属性的close引号

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

更新

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

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

通常在这种情况下,我希望您向服务器发送一条消息,请求通知并提供接收这些通知的端点。在本例中,所有这些都是通过一个单独的web界面完成的


因此,剩下的就是让您接收消息,在这个角色中,您的行为更像一个服务器。我可能错了,但我认为您应该使用SOAP库的服务器功能

您是要按照您的说明设置原始TCP服务器,还是按照示例代码设置HTTP服务器?对不起,HTTP(以3,2,1…编辑)同时,我不确定bare
Notify
是否是正文的有效子级。我也不认为充满XML的多行字符串是有效的。但是…老实说,我在我的电脑或网上找不到任何SOAP1.1工具。(这是一项非常古老的技术,将在2013年作为一名新手学习!)因此,很难确定。我不认为仅仅“使用lxml”就有帮助。当我把它扔向domapi或etreeapi时,我得到了一个关于无效XML的异常。但是…同时使用lxml和BeautifulSoup可能会起作用。在<代码>中抛出您的示例
>>> from suds.client import Client
>>> url="file:///tmp/service.wsdl"
>>> c=Client(url)
>>> c.factory.create('DeviceLocation')
(DeviceLocation){
   _changedOn = ""
 }
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)