我们有一位姓 Null 的员工。当姓氏用作搜索词时,我们的员工查找应用程序将被终止(现在这种情况经常发生)。收到的错误(感谢 Fiddler!)是:
<soapenv:Fault>
<faultcode>soapenv:Server.userException</faultcode>
<faultstring>coldfusion.xml.rpc.CFCInvocationException: [coldfusion.runtime.MissingArgumentException : The SEARCHSTRING parameter to the getFacultyNames function is required but was not passed in.]</faultstring>
可爱吧?
参数类型为 string
。
我在用:
WSDL (SOAP)
弹性 3.5
动作脚本 3
冷融合 8
请注意,从 ColdFusion 页面将 Web 服务作为对象调用时不会发生错误。
追踪它
起初我认为这是一个强制错误,其中 null
被强制为 "null"
并且 "null" == null
的测试通过了。它不是。 我很接近,但非常非常错误。很抱歉!
我已经完成了很多 fiddling on wonderfl.net 并跟踪了 mx.rpc.xml.*
中的代码。在 XMLEncoder
的第 1795 行(在 3.5 源代码中),在 setValue
中,所有 XMLEncoding 归结为
currentChild.appendChild(xmlSpecialCharsFilter(Object(value)));
这基本上与以下内容相同:
currentChild.appendChild("null");
根据我原来的小提琴,这段代码返回一个空的 XML 元素。但为什么?
原因
根据关于错误报告 FLEX-33664 的评论者 Justin Mclean 的说法,以下是罪魁祸首(请参阅我的 fiddle 中的最后两个测试来验证这一点):
var thisIsNotNull:XML = <root>null</root>;
if(thisIsNotNull == null){
// always branches here, as (thisIsNotNull == null) strangely returns true
// despite the fact that thisIsNotNull is a valid instance of type XML
}
当 currentChild.appendChild
传递字符串 "null"
时,它首先将其转换为带有文本 null
的根 XML 元素,然后针对 null 文字测试该元素。这是一个弱相等测试,所以要么包含 null 的 XML 被强制为 null 类型,要么 null 类型被强制为包含字符串“null”的根 xml 元素,并且测试通过了它可能失败的地方。一种解决方法可能是在检查 XML(或任何东西,真的)时始终使用 strict equality 测试是否为“nullness”。
解决方案
将它们作为 CDATA 值转义。
CDATA 值是改变整个文本值的最合适方法,否则会导致编码/解码问题。例如,十六进制编码用于单个字符。当您转义元素的整个文本时,首选 CDATA 值。最大的原因是它保持了人类的可读性。
在 xkcd note 上,Bobby Tables website 提供了很好的建议,可以避免在各种语言(包括 ColdFusion)的 SQL 查询中对用户数据(在本例中为字符串“Null”)进行不当解释。
从问题中不清楚这是问题的根源,并且鉴于在对第一个答案的评论中提到的解决方案(将参数嵌入结构中),它似乎可能是其他问题。
@doc_180 有正确的概念,除了他专注于数字,而原始海报有字符串问题。
解决方案是更改 mx.rpc.xml.XMLEncoder
文件。这是第 121 行:
if (content != null)
result += content;
(我查看了 Flex 4.5.1 SDK;其他版本的行号可能不同。)
基本上,验证失败是因为“内容为空”,因此您的参数没有添加到传出的 SOAP 数据包中;从而导致缺少参数错误。
您必须扩展此类以删除验证。然后是一个大雪球,修改 SOAPEncoder 以使用您修改后的 XMLEncoder,然后修改 Operation 以使用您修改后的 SOAPEncoder,然后修改 WebService 以使用您的备用 Operation 类。
我花了几个小时在上面,但我需要继续前进。大概需要一两天的时间。
您可以只修复 XMLEncoder 行并执行一些 monkey patching 以使用您自己的类。
我还要补充一点,如果您切换到将 RemoteObject/AMF 与 ColdFusion 结合使用,则可以毫无问题地传递 null。
2013 年 11 月 16 日更新:
我在上一篇关于 RemoteObject/AMF 的评论中添加了一个最新的内容。如果您使用的是 ColdFusion 10;然后从服务器端对象中删除对象上具有空值的属性。因此,您必须在访问之前检查属性是否存在,否则您将收到运行时错误。
像这样检查:
<cfif (structKeyExists(arguments.myObject,'propertyName')>
<!--- no property code --->
<cfelse>
<!--- handle property normally --->
</cfif>
这是 ColdFusion 9 的行为变化;其中 null 属性将变成空字符串。
编辑 2013 年 12 月 6 日
由于存在关于如何处理空值的问题,这里有一个快速示例应用程序来演示字符串“null”如何与保留字 null 相关。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" initialize="application1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function application1_initializeHandler(event:FlexEvent):void
{
var s :String = "null";
if(s != null){
trace('null string is not equal to null reserved word using the != condition');
} else {
trace('null string is equal to null reserved word using the != condition');
}
if(s == null){
trace('null string is equal to null reserved word using the == condition');
} else {
trace('null string is not equal to null reserved word using the == condition');
}
if(s === null){
trace('null string is equal to null reserved word using the === condition');
} else {
trace('null string is not equal to null reserved word using the === condition');
}
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Application>
跟踪输出为:
使用 != 条件的空字符串不等于空保留字 使用 == 条件的空字符串不等于空保留字 使用 === 条件的空字符串不等于空保留字
content
中的那一点是字符串 "null"
,并且“null” == null 返回 false,因此测试的行为符合预期。相反,我认为问题在于 XML.appendChild 如何处理字符串参数,以及如何将仅包含字符串“null”的根 XML 元素强制转换为文字 null
。
true
是此处所需的行为。如果发生相反的情况,这将从编码过程中丢弃字符串“null”,这实际上是问题的原因。但是,由于此测试成功,编码器继续运行,直到 XML.appendChild 由于强制错误而丢弃它。
var xml:XML = <root>null</root>; var s:String = (xml == null) ? "wtf? xml coerced to null?!!" : "xml not coerced to null."; trace(s);
添加到您的代码示例中。
将所有字符转换为其十六进制实体等价物。在这种情况下,Null
将转换为 E;KC;C;
将 ActionScript 中的 null
值字符串化将给出字符串 "NULL"
。我怀疑有人已经决定将字符串 "NULL"
解码为 null
是一个好主意,这会导致您在此处看到的损坏 - 可能是因为他们传入 null
对象并获取字符串数据库,当他们不想要的时候(所以一定要检查那种错误,也是)。
作为 hack,您可以考虑在客户端进行特殊处理,将“Null”字符串转换为永远不会发生的内容,例如 XXNULLXX 并在服务器上转换回。
它并不漂亮,但它可以解决这种边界情况的问题。
Null
怎么办?
好吧,我猜想 Flex 的 SOAP 编码器实现似乎错误地序列化了空值。将它们序列化为 String Null 似乎不是一个好的解决方案。正式正确的版本似乎是将空值传递为:
<childtag2 xsi:nil="true" />
所以“Null”的值只不过是一个有效的字符串,这正是你要找的。
我猜想在 Apache Flex 中解决这个问题应该不难完成。我建议打开一个 Jira 问题或联系 apache-flex 邮件列表的人。但是,这只会修复客户端。我不能说 ColdFusion 是否能够处理以这种方式编码的空值。
另请参阅 Radu Cotescu 的博文How to send null values in soapUI requests。
xsi:nil="true"
,XMLEncoder.as 实际上会正确编码一个真正的 null
值。问题实际上似乎在于 ActionScript XML
类型本身(而不是编码器)处理字符串 "null"
的方式。
这是一个混乱,但假设 SEARCHSTRING
有一个最小长度,例如 2 个字符,substring
第二个字符的 SEARCHSTRING
参数并将其作为两个参数传递:SEARCHSTRING1 ("Nu")
和 SEARCHSTRING2 ("ll").
Concatenate
在执行对数据库的查询时将它们重新组合在一起。
n
、u
、l
在 XML 中具有特殊语义。 "NULL" 和 "<![CDATA[NULL]]>"与 XML 解析器相同。
NULL
没有什么特别之处,但要学究起来,<blah>null</blah>
和 <blah><![CDATA[null]]>
与 XML 解析器不同.它们应该产生相同的结果,但是处理它们的逻辑流程不同。我们正在利用这种效果来解决 flex XML 实现中的错误。我提倡这一点而不是其他方法,因为它保留了文本的可读性,并且对其他解析器没有副作用。