动易SW中的一个严重但不影响使用的bug(一)

BUG描述:整个动易API接口的响应功能,除了操作状态码正常,其它部分完全失效,不会返回错误提示,也不会返回用户信息。

BUG重现:在启用了整合的站点,直接访问/API/API_Response.asp,将会看到<status>1</status>(表示请求不正确或者操作失败),以及<message>操作已成功完成</message>(操作成功返回的默认值)。就表示该BUG存在。

BUG影响:大部分情况下,各个应用程序并不会向动易请求用户信息,因此除了在登陆失败、注册失败等失败操作会提示用户“操作已成功”的假象以外,暂时不会造成其它影响。


  动易SW里有这样一个问题,只有启用了整合接口并且曾经在整合的其它系统中遇到过错误提示的朋友才会发现:

  每当整合出现问题或者是登陆、注册等一系列操作出错的时候,我们知道以前是返回错误的具体提示,包括服务器缺乏相关组件,或者接口配置错误,或者用户信息错误等,都会有不同的提示。到了SW里,这些操作同样也会返回错误,不会让你操作成功。但是错误的提示是什么呢?全部都是“操作已成功完成”,这个搞笑的问题出在哪里呢?

  以前设计API的时候,考虑到最常见的情况是用户注册、登陆、注销这些日常操作,而这些操作里又是成功的居多,所以我在用于返回信息的Response.xml模板里,设置了默认返回状态为0(表示成功),默认返回信息为“操作已成功完成”。这就是这个bug的缘起了。

  那么,以前老的版本反而没有这个问题,为什么新版本里出现这个问题呢?第一反应:当出现错误的时候,没有修改message,也就是错误提示信息。好,我们打开API_Response.asp检查一下,发现当操作错误的时候,代码是这样的:

If FoundErr Then
    sPE_Items(conStatus, 1) = "1"
    sPE_Items(conMessage, 1) = ErrMsg
    prepareXML (False)
    WriteXml
Else
    sPE_Items(conStatus, 1) = "0"
    prepareXML (False)
    WriteXml
End If

  我用黄色背景标记的这行,翻译过来就是“设置提示信息的内容为ErrMsg(具体的错误信息)”,说明不是没有修改,于是我们继续往下查,sPE_Items这是一个API使用的全局数组,二维的,sPE_Items(a, b),a代表xml里的不同元素,b为0标识我们要访问的是元素名称,1标识我们要访问的是元素里的内容;而conMessage是一个常数,实际上它等于4。在全局数组里sPE_Items(4, 1)代表的就是xml中message这个元素的内容。

  接下来是prepareXML(False)这个函数。这个函数的作用是加载xml模板,然后用数组里的内容去替换默认值。经检查,在prepareXML这个函数里,当返回出错信息的时候的代码如下:

ASP/Visual Basic代码
  1. If intIndex <> conAction And intIndex <> conSyskey And intIndex <> conUsername Then
  2. setNodeText sPE_Items(intIndex,0),sPE_Items(intIndex,1)
  3. End If

  这里不会有什么问题,看来问题就出在setNodeText这个函数上了。新版本中的这个函数是:

ASP/Visual Basic代码
  1. ‘**************************************************
  2. ‘函数名:setNodeText
  3. ‘作 用:设置XML文件中指定节点的文本
  4. ‘参 数:strNodeName —-节点名称
  5. ‘    strNodeText —-要设置的文本
  6. ‘返回值:0 = 设置成功; 否则返回Err.Description
  7. ‘**************************************************
  8. Function setNodeText(strNodeName, strNodeText)
  9. If IsNull(strNodeText) Or IsEmpty(strNodeText) or strNodeText = “” Then Exit Function
  10. If IsNull(strNodeName) Or IsEmpty(strNodeName) or strNodeName = “” Then Exit Function
  11. If IsNode(strNodeName) Then sMyXmlDoc.selectSingleNode(strNodeName).text = strNodeText
  12. End Function

  以前的版本中代码是:

ASP/Visual Basic代码
  1. ‘**************************************************
  2. ‘函数名:setNodeText
  3. ‘作 用:设置XML文件中指定节点的文本
  4. ‘参 数:strNodeName —-节点名称
  5. ‘    strNodeText —-要设置的文本
  6. ‘返回值:0 = 设置成功; 否则返回Err.Description
  7. ‘**************************************************
  8. Function setNodeText(strNodeName, strNodeText)
  9. If IsNull(strNodeText) Or IsEmpty(strNodeText) or strNodeText = “” Then Exit Function
  10. If IsNull(strNodeName) Or IsEmpty(strNodeName) or strNodeName = “” Then Exit Function
  11. If IsNode(strNodeName) Then sMyXmlDoc.getElementsByTagName(strNodeName).Item(0).text = strNodeText
  12. End Function

  区别就在于,新版本用selectSingleNode这个方法获取节点,以前版本中我用getElementsByTagName().Item(0)这个方法来获取节点。两个方法都是返回xml文档树中的一个元素对象。新版本的代码简洁,直接从树中读取一个元素,而我的方法是读取文档树中所有标签为strNodeName(本例中指的是message)的元素列表,然后取第一个。

  我排错的时候看到这里就已经明白问题所在了。因为当初我第一次写到这里的时候就犯过新版中这个错误。selectSingleNode这个方法的参数类型是XPath,而getElementsByTagName这个方法的参数类型是string。我们这里给的“message”,不是一个XPath,如果在我们的文档中用XPath来标识这个元素,正确的应该是“/root/body/message”,但是由于sMyXmlDoc这个对象取得是xml文档树的根元素,也就是root,所以其实也可以用“/body/message”来访问到。文档中只有一个message元素,因此也可以用“//message”来访问。不管用哪一种都行,但是用“message”是不对的。

  所以这里给xml中所有body的子元素赋值都会失败。这就是问题所在了。

  同理可知,在新版本的接口中,如果是整合的程序向动易发送请求,那么不管什么请求,返回的xml包中,都只有两个有效数据,一个是告诉对方响应者是动易系统,另一个就是告诉对方操作状态是成功还是失败。至于错误的具体提示,只有默认值,而其它的数据,比如检索用户信息,返回的则全部是空值。还好目前的整合中基本上不会用到这些元素,所以也就成了一个“严重”但不影响使用的bug了。关于这个bug为什么没有引发500错误以及如何修正,下次我再继续说。

  除此之外,动易的新版本接口还增加了一个新元素(sex),可以理解为符合接口规范的自定义元素,但是这个元素表示的是用户性别。这是在标准中定义的必备扩展元素,元素名称是gendar(英文单词意思是性别)。等于放弃了对标准元素gendar的支持,增加了一个自定义元素sex。

  不知道三方是否在这个新增元素上做过沟通取得了共识。如果没有的话,那么这里又是一个新bug,就是通用注册的时候,其它程序将不能通知动易记录用户的性别,动易也没法通知其它用户。

  我第一次看到这个新增元素的时候百思不得其解。如果是gendar命名不恰当,要改的话,那应该会发公告通知PDO接口规范更新到1.1或者2.0,新增sex元素,gendar元素状态变为“不推荐”,这是w3c在xhtml规范里喜欢用的方式。先不推荐但是还兼容,让你有时间慢慢改,以后时机恰当再取消。

  但是我奇怪的是gendar无论从语意上讲还是从哪方面讲,都不算是命名不规范的元素啊,甚至是比sex更规范的元素。sex只不过是在部分web应用里习惯的用法而已。其英文单词意思是“性”,而不是“性别”。

  莫非做这个修改的技术员在修改接口的时候不看接口规范?不得而知了。但是对于selectSingleNode不能根据元素名称选择元素这个问题,的确是不应该犯的。

  发现我写的代码被更改并不是因为我写错了,本来应该是高兴的吧,但是我却高兴不起来。我依然是动易的忠实FANS,我希望它更好。