12.8.  SOAP 网络服务故障排除

是的,SOAP 网络服务的世界中也不总是欢乐和阳光。有时候也会有故障。

正如你在本章中看到的,SOAP 牵扯了很多层面。SOAP 向 HTTP 服务器发送 XML 文档并接收返回的 XML 文档时需要用到 HTTP 层。这样一来,你在 第 11 章 HTTP Web 服务 学到的调试技术在这里都有了用武之地。你可以 import httplib 并设置 httplib.HTTPConnection.debuglevel = 1 来查看潜在的 HTTP 传输。

在 HTTP 层之上,还有几个可能发生问题的地方。SOAPpy 隐藏 SOAP 语法的本领令你惊叹不已,但也意味着在发生问题时更难确定问题所在。

下面的这些例子是我在使用 SOAP 网络服务时犯过的一些常见错误以及所产生的错误信息。

例 12.15. 以错误的设置调用 Proxy 方法

>>> from SOAPpy import SOAPProxy
>>> url = 'http://www.bhlaab.com/181:80/soap/servlet/rpcrouter'
>>> server = SOAPProxy(url)                                        
>>> server.getTemp('27502')                                        
<Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?>
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: <Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?>
你看出错误了吗?你手工地创建了一个 SOAPProxy,你正确地指定了服务 URL,但是你没有指定命名空间。由于多个服务可能被路由到相同的服务 URL,命名空间是确定你所调用的服务和方法的重要内容。
服务器返回的是一个 SOAP 错误 (Fault),SOAPpy 把它转换为 Python 异常 SOAPpy.Types.faultType。从任何 SOAP 服务器返回的错误都是 SOAP 错误,因此你可以轻易地捕获这个异常。就此处而言,我们能从 SOAP 错误信息中看出端倪:由于源 SOAPProxy 对象没有设置服务命名空间,因此方法元素也就没有了命名空间。

错误配置 SOAP 服务的基本元素是 WSDL 着眼解决的问题。WSDL 文件包含服务 URL 和命名空间,所以你应该不会在这里犯错。但是,还有其他可能出错的地方。

例 12.16. 以错误参数调用方法

>>> wsdlFile = 'http://www.bhlaab.com/257/sd/2001/TemperatureService.wsdl'
>>> server = WSDL.Proxy(wsdlFile)
>>> temperature = server.getTemp(27502)                                
<Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match>   
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: <Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match>
你看出错误了吗?这是一个不易察觉的错误:你在使用整数而不是字符串来调用 server.getTemp 。自省 WSDL 文件不难发现,getTemp() 这个 SOAP 函数接受一个参数 zipcode,这是一个字符串参数。WSDL.Proxy 会为你强制转换数据类型;你需要根据服务器需要的数据类型传递数据。
又是这样,服务器传回一个 SOAP 错误,你能从 SOAP 错误信息中看出端倪:你在使用整数类型的参数调用 getTemp 函数,但却没有一个以此命名的函数接收整数参数。理论上讲,SOAP 允许你重载 (overload) 函数,也就是可以在同一个 SOAP 服务中存在同名函数,并且参数个数也相同,但是参数的数据类型不同。这就是数据类型必须匹配的原因,也说明了为什么 WSDL.Proxy 不强制地为你改变数据类型。如果真的强制改变了数据类型,发生这样的错误时,调用的可能是另外一个不相干的函数。看来产生这样的错误是件幸运的事。对于数据类型多加注意会让事情简单很多,一旦搞错了数据类型便立刻会发生错误。

Python 所期待的返回值个数与远程函数的实际返回值个数不同是另一种可能的错误。

例 12.17. 调用时方法所期待的返回值个数错误

>>> wsdlFile = 'http://www.bhlaab.com/064/sd/2001/TemperatureService.wsdl'
>>> server = WSDL.Proxy(wsdlFile)
>>> (city, temperature) = server.getTemp(27502)  
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unpack non-sequence
你看出错误了吗?server.getTemp 只返回一个浮点值,但你写的代码却期待着获得两个值,并把它们赋值给不同的两个变量。注意这不是一个 SOAP 错误。就远程服务器而言没有发生任何错误。错误发生在完成 SOAP 交割之后WSDL.Proxy 返回一个浮点数,你本地的 Python 解释器试图将这个浮点数分成两个变量。由于函数只返回了一个值,你在试图分割它时所获得的是一个 Python 异常,而不是 SOAP 错误。

那么 Google 网络服务方面又如何呢?我曾经犯过的最常见的错误是忘记正确设置应用许可证。

例 12.18. 调用方法返回一个应用特定的错误

>>> from SOAPpy import WSDL
>>> server = WSDL.Proxy(r'/path/to/local/GoogleSearch.wsdl')
>>> results = server.doGoogleSearch('foo', 'mark', 0, 10, False, "", 
...     False, "", "utf-8", "utf-8")
<Fault SOAP-ENV:Server:                                              
 Exception from service object: Invalid authorization key: foo:
 <SOAPpy.Types.structType detail at 14164616>:
 {'stackTrace':
  'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:220)
   at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
   at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
     GoogleSearchService.java:825)
   at com.google.soap.search.GoogleSearchService.doGoogleSearch(
     GoogleSearchService.java:121)
   at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
   at org.apache.soap.providers.RPCJavaProvider.invoke(
     RPCJavaProvider.java:129)
   at org.apache.soap.server.http.RPCRouterServlet.doPost(
     RPCRouterServlet.java:288)
   at javax.servlet.http.httpervlet.service(httpervlet.java:760)
   at javax.servlet.http.httpervlet.service(httpervlet.java:853)
   at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
   at com.google.gse.HttpConnection.run(HttpConnection.java:195)
   at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
   at com.google.soap.search.UserKey.<init>(UserKey.java:59)
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:217)
   ... 14 more
'}>
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
    return self.__r_call(*args, **kw)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
    self.__hd, self.__ma)
  File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
    raise p
SOAPpy.Types.faultType: <Fault SOAP-ENV:Server: Exception from service object:
Invalid authorization key: foo:
<SOAPpy.Types.structType detail at 14164616>:
{'stackTrace':
  'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:220)
   at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
   at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
     GoogleSearchService.java:825)
   at com.google.soap.search.GoogleSearchService.doGoogleSearch(
     GoogleSearchService.java:121)
   at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
   at java.lang.reflect.Method.invoke(Unknown Source)
   at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
   at org.apache.soap.providers.RPCJavaProvider.invoke(
     RPCJavaProvider.java:129)
   at org.apache.soap.server.http.RPCRouterServlet.doPost(
     RPCRouterServlet.java:288)
   at javax.servlet.http.httpervlet.service(httpervlet.java:760)
   at javax.servlet.http.httpervlet.service(httpervlet.java:853)
   at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
   at com.google.gse.HttpConnection.run(HttpConnection.java:195)
   at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
   at com.google.soap.search.UserKey.<init>(UserKey.java:59)
   at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
     QueryLimits.java:217)
   ... 14 more
'}>
你看出错误了吗?调用的语法,时时彩计划软件公式:参数个数以及数据类型都没有错误。这个问题是应用特定的:第一个参数应该是我的应用许可证,但 foo 不是一个有效的 Google 许可证。
Google 服务器返回的是一个 SOAP 错误和一大串特别长的错误信息,其中包含了完整的 Java 堆栈跟踪。记住所有SOAP 错误都被标示为 SOAP Faults: errors in configuration (设置错误), errors in function arguments (函数参数错误),或者是应用特定的错误 (这里就是) 等等。在其中埋藏的至关重要信息是:Invalid authorization key: foo (非有效授权许可证:foo)。

进一步阅读

凤凰时时彩平台靠谱吗 钻石时时彩计划软件手机版下载 江西时时彩宝典 皇冠时时彩网址 凤凰时时彩代理
新疆时时彩三星走势图 时时彩五星一码不定位 红树林时时彩平台网址 时时彩官网皇恩娱乐 领航时时彩软件后二lm0
多宝时时彩源码 大玩家 黄金城时时彩平台 17年重庆时时彩骗局 多宝时时彩平台
时时彩跟计划挂了就跟 易购时时彩登陆 重庆时时彩的分析软件 我玩乐彩提现被冻结了 巨轮黄金胆码软件下载
江西十一选五计划软件手机版 山东11选5开奖 贵州十一选五历史开奖查询结果 山东十一选五软件 广东11选5开奖信息查询
福建22选5走势图表 澳洲快乐8平台 北京赛车pk10官方直播 码报白小姐 贵州快3助手下载安装
河南22选5走势图 末世之掌上七星 幸运赛车直播 湖北快3号码单号遗漏 湖南快乐十分电视走势
真钱图片二十万 重庆时时彩软件版 福建11选五开奖结果 快乐十分走势 江西11选5开奖结果最快最准