如何在 ActionScript 3 中将“Null”(一个真正的姓氏!)传递给 SOAP Web 服务

apache-flex actionscript soap coldfusion wsdl

我们有一位姓 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 服务作为对象调用时不会发生错误。

对于特定问题,它可能对您没有太大帮助,但 SOAP 1.2 允许可为空值,请参阅 w3.org/TR/2001/WD-soap12-20010709/#_Toc478383513

我有一种感觉,它涉及到 Dave Null。

员工是否考虑过改名?

BBC 引用:bbc.com/future/story/…

他真的应该考虑买一只Pointer狗并称他为 NullPointer。

C
Community

追踪它

起初我认为这是一个强制错误,其中 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 值。最大的原因是它保持了人类的可读性。

P
Peter Mortensen

xkcd note 上,Bobby Tables website 提供了很好的建议,可以避免在各种语言(包括 ColdFusion)的 SQL 查询中对用户数据(在本例中为字符串“Null”)进行不当解释。

从问题中不清楚这是问题的根源,并且鉴于在对第一个答案的评论中提到的解决方案(将参数嵌入结构中),它似乎可能是其他问题。

P
Peter Mortensen

问题可能出在 Flex 的 SOAP 编码器中。尝试在 Flex 应用程序中扩展 SOAP 编码器并调试程序以查看如何处理空值。

我的猜测是,它作为 NaN(不是数字)传递。这有时会弄乱 SOAP 消息解组过程(最明显的是在 JBoss 5 服务器中......)。我记得扩展了 SOAP 编码器并对如何处理 NaN 进行了显式检查。

name="Null" 当然有用,我看不出它应该与 NaN 有什么关系。

P
Peter Mortensen

@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>

跟踪输出为:

使用 != 条件的空字符串不等于空保留字 使用 == 条件的空字符串不等于空保留字 使用 === 条件的空字符串不等于空保留字

@Reboog711 员工的姓氏实际上是字符串“Null”,如“我的名字是 Pat Null” 您的回答未能传递员工的姓氏。您的回答只是隐藏了这样一个事实,即“Null”被 Ben Burns 描述的 appendChild() 方法不恰当地强制转换为 null 的语言概念。结果仍然是系统无法处理空先生或空女士。

@MaxxDaymon 我认为您误解了我的答案实际上是什么。它没有提供解决方案;而是解释为什么会出现问题;并引用 Flex 框架中的相关代码。我最近的编辑可能放错了位置;因为它讨论了一种替代方法,并且与原始问题没有直接关系。

您在某种程度上是在正确的轨道上,但是在代码 content 中的那一点是字符串 "null",并且“null” == null 返回 false,因此测试的行为符合预期。相反,我认为问题在于 XML.appendChild 如何处理字符串参数,以及如何将仅包含字符串“null”的根 XML 元素强制转换为文字 null

@Reboog711 看看我的小提琴。 "null" != 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); 添加到您的代码示例中。

d
doogle

将所有字符转换为其十六进制实体等价物。在这种情况下,Null 将转换为 &#4E;&#75;&#6C;&#6C;

请不要这样做。创建 CDATA 是为了在需要转义整个文本块的情况下使用。

我可能是错的,但我不认为仅仅因为它不是你的解决方案而拒绝投票是它应该如何工作的。此外,您必须记住问题需要启发式解决方案,因为没有一种明显的方法,正如发布的各种解决方案所证明的那样。最后,请记住我不知道 CF,解码器不会将 的内部文本等同于 NULL?如果是这样,那么 CDATA 真的是一个解决方案吗?

我投了反对票,因为这是一种反模式。这种情况下的错误不在 CF 中,而是在 ActionScript 中。但是,您仍然提出了一个很好的观点。我将在我的小提琴中添加一个 CDATA 编码测试。

为什么都是字符?一个还不够吗?

P
Peter Mortensen

ActionScript 中的 null 值字符串化将给出字符串 "NULL"。我怀疑有人已经决定将字符串 "NULL" 解码为 null 是一个好主意,这会导致您在此处看到的损坏 - 可能是因为他们传入 null 对象并获取字符串数据库,当他们不想要的时候(所以一定要检查那种错误,也是)。

是的,这里有很多可能性需要更多的调试才能缩小范围。 1) 此处使用的 WSDL 是否足以区分“NULL”作为字符串值和实际的空(或省略)值? 2) 如果是,客户端是否正确编码姓氏(作为字符串而不是空文字) 3) 如果是,服务是否正确地将“NULL”解释为字符串,或将其强制为空值?

P
Peter Mortensen

作为 hack,您可以考虑在客户端进行特殊处理,将“Null”字符串转换为永远不会发生的内容,例如 XXNULLXX 并在服务器上转换回。

它并不漂亮,但它可以解决这种边界情况的问题。

XXNULLXX 也可以是一个名字。你不知道。也许印度尼西亚的人没有姓氏,并在需要时使用 XXX 的变体作为他们的姓氏。

相同的概念,但更新数据库中的所有名称并使用一些字符(1Null,1Smith)作为前言。剥离客户端中的那个字符。当然,这可能比 Reboog 的解决方案更简单。

@BenBurns 是的,但是如果我想给我的孩子命名 &#78;&#117;&#108;&#108; 怎么办?

@Sirens 那不是问题。如果我的名字是“<”>,那么我希望它被正确地转义为“<&”>”,这是不言而喻的。真正的问题在于应用程序的行为就好像它对名称使用了黑名单。

P
Peter Mortensen

好吧,我猜想 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" 的方式。

D
Dhanuka

这是一个混乱,但假设 SEARCHSTRING 有一个最小长度,例如 2 个字符,substring 第二个字符的 SEARCHSTRING 参数并将其作为两个参数传递:SEARCHSTRING1 ("Nu")SEARCHSTRING2 ("ll"). Concatenate在执行对数据库的查询时将它们重新组合在一起。

CDATA 被添加到 XML 规范中以避免这些类型的混乱。

无需使用 CDATA 转义“Null”,XML 中不存在 null 关键字之类的东西。

同意@eckes。我不明白为什么会有这么多关于 CDATA 的讨论。 CDATA 仅用于转义 XML 中具有特殊含义的字符。没有:nul 在 XML 中具有特殊语义。 "NULL" 和 "<![CDATA[NULL]]>"与 XML 解析器相同。

@jasonkarns - 我同意 100% 应该 字符串/文本节点 NULL 没有什么特别之处,但要学究起来,<blah>null</blah><blah><![CDATA[null]]> 与 XML 解析器不同.它们应该产生相同的结果,但是处理它们的逻辑流程不同。我们正在利用这种效果来解决 flex XML 实现中的错误。我提倡这一点而不是其他方法,因为它保留了文本的可读性,并且对其他解析器没有副作用。