Como passar "Nulo" (um sobrenome real!) Para um serviço Web SOAP no ActionScript 3

4636

Temos um funcionário cujo sobrenome é Nulo. Nosso aplicativo de pesquisa de funcionários é eliminado quando esse sobrenome é usado como o termo de pesquisa (que acontece com bastante frequência agora). O erro recebido (obrigado 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>

Bonitinho, hein?

O tipo de parâmetro é string.

Estou usando:

  • WSDL ( SOAP )
  • Flex 3.5
  • ActionScript 3
  • ColdFusion 8

Observe que o erro não ocorre ao chamar o serviço da web como um objeto de uma página do ColdFusion.

conta
fonte
6
Pode não ajudar muito com o problema específico, mas o SOAP 1.2 permite valores anuláveis, consulte w3.org/TR/2001/WD-soap12-20010709/#_Toc478383513
JensG
6
Tenho a sensação de que envolve Dave Null.
George Gibson
2
Pelo menos, não envolve Chuck Norris. Aqui está o porquê de ficar longe dele no código: codesqueeze.com/…
SDsolar 3/17/17
42
O funcionário considerou mudar de nome?
usar o seguinte
11
Ele realmente deveria considerar comprar um cachorro Pointer e chamá-lo de NullPointer.
Antonio Alvarez

Respostas:

1109

Rastreando-o

No começo, pensei que era um bug de coerção para o qual nullestava sendo coagido "null"e um teste "null" == nullestava passando. Não é. Eu estava perto, mas muito, muito errado. Me desculpe por isso!

Desde então, fiz muitas brincadeiras no wonderfl.net e rastreio o código em mx.rpc.xml.*. Na linha 1795 de XMLEncoder(na fonte 3.5), em setValue, todo o XMLEncoding se resume a

currentChild.appendChild(xmlSpecialCharsFilter(Object(value)));

que é essencialmente o mesmo que:

currentChild.appendChild("null");

Este código, de acordo com o meu violino original, retorna um elemento XML vazio. Mas por que?

Causa

De acordo com o comentarista Justin Mclean no relatório de bug FLEX-33664 , o culpado a seguir (veja os dois últimos testes no meu violino que verificam isso):

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
}

Quando currentChild.appendChilda cadeia é passada "null", ela primeiro a converte em um elemento XML raiz com texto nulle depois testa esse elemento em relação ao literal nulo. Este é um teste de igualdade fraco, portanto, o XML que contém nulo é coagido para o tipo nulo ou o tipo nulo é coagido para um elemento xml raiz que contém a cadeia "nula" e o teste passa onde provavelmente deve falhar. Uma correção pode ser sempre usar testes rigorosos de igualdade ao verificar XML (ou qualquer coisa, na verdade) para "nulidade".

Solução

A única solução razoável que posso pensar, além de corrigir esse bug em todas as malditas versões do ActionScript, é testar campos "nulos" e escapar deles como valores CDATA .

Os valores CDATA são a maneira mais apropriada de alterar um valor de texto inteiro que, de outra forma, causaria problemas de codificação / decodificação. A codificação hexadecimal, por exemplo, destina-se a caracteres individuais. Os valores CDATA são preferidos quando você está escapando do texto inteiro de um elemento. A maior razão para isso é que mantém a legibilidade humana.

Ben Burns
fonte
298

Na nota xkcd , o site Bobby Tables oferece bons conselhos para evitar a interpretação incorreta dos dados do usuário (neste caso, a cadeia "Nulo") nas consultas SQL em vários idiomas, incluindo o ColdFusion .

Não está claro pela pergunta que essa é a fonte do problema e, dada a solução mencionada em um comentário à primeira resposta (incorporando os parâmetros em uma estrutura), parece provável que fosse outra coisa.

Alex Dupuy
fonte
239

O problema pode estar no codificador SOAP do Flex. Tente estender o codificador SOAP em seu aplicativo Flex e depure o programa para ver como o valor nulo é tratado.

Meu palpite é que passou como NaN (não é um número). Isso atrapalhará o processo de remoção da mensagem SOAP em algum momento (principalmente no servidor JBoss 5 ...). Lembro-me de estender o codificador SOAP e executar uma verificação explícita de como o NaN é tratado.

uncaught_exceptions
fonte
12
name = "Null" é obviamente útil, e eu não vejo como isso deve estar relacionado ao NaN.
Eckes
129

@ doc_180 tinha o conceito certo, exceto que ele se concentra nos números, enquanto o pôster original teve problemas com as strings.

A solução é alterar o mx.rpc.xml.XMLEncoderarquivo. Esta é a linha 121:

    if (content != null)
        result += content;

(Consultei o Flex 4.5.1 SDK; os números de linha podem diferir em outras versões.)

Basicamente, a validação falha porque 'o conteúdo é nulo' e, portanto, seu argumento não é adicionado ao pacote SOAP de saída; causando assim o erro de parâmetro ausente.

Você precisa estender essa classe para remover a validação. Em seguida, há uma grande bola de neve na cadeia, modificando o SOAPEncoder para usar o XMLEncoder modificado e, em seguida, a Operation para usar o SOAPEncoder modificado e, em seguida, instalando o WebService para usar sua classe de operação alternativa.

Passei algumas horas nisso, mas preciso seguir em frente. Provavelmente vai demorar um dia ou dois.

Você pode consertar apenas a linha XMLEncoder e fazer algumas correções de macacos para usar sua própria classe.

Também acrescentarei que, se você mudar para o RemoteObject / AMF com ColdFusion, o nulo será passado sem problemas.


16/11/2013 atualização :

Tenho uma adição mais recente ao meu último comentário sobre o RemoteObject / AMF. Se você estiver usando o ColdFusion 10; as propriedades com um valor nulo em um objeto são removidas do objeto do lado do servidor. Portanto, você deve verificar a existência das propriedades antes de acessá-las ou obterá um erro de tempo de execução.

Verifique assim:

<cfif (structKeyExists(arguments.myObject,'propertyName')>
 <!--- no property code --->
<cfelse>
 <!--- handle property  normally --->
</cfif>

Esta é uma mudança de comportamento do ColdFusion 9; onde as propriedades nulas se transformariam em cadeias vazias.


Editar 06/12/2013

Como houve uma pergunta sobre como os nulos são tratados, aqui está um aplicativo de amostra rápida para demonstrar como uma sequência "nula" se relacionará com a palavra reservada nula.

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

A saída de rastreio é:

cadeia nula não é igual a palavra reservada nula usando a condição! =

cadeia nula não é igual a palavra reservada nula usando a condição ==

cadeia nula não é igual a palavra reservada nula usando a condição ===

JeffryHouser
fonte
8
@ Reboog711 O sobrenome do funcionário é literalmente a string "Nulo", como em "Meu nome é Pat Null" Sua resposta não passa o sobrenome do funcionário. A sua resposta apenas oculta o fato de que "Nulo" está sendo inapropriadamente coagido ao conceito de linguagem null pelo método appendChild (), conforme descrito por Ben Burns. O resultado ainda é a falha do sistema em lidar com o Sr. ou a Sra. Null.
Maxx Daymon
2
@ MaxxDaymon Eu acho que você interpreta mal qual é a minha resposta. Não apresenta uma solução; mas sim uma explicação de por que o problema ocorre; e cita o código relevante do Flex Framework. Minha edição mais recente talvez esteja fora de lugar; pois discute uma abordagem alternativa e não está diretamente relacionada à pergunta original.
precisa saber é o seguinte
1
Você está no caminho certo, mas nesse ponto do código contentestá a string "null"e "null" == null retorna false, para que o teste se comporte conforme o pretendido. Em vez disso, acredito que o problema é uma mistura de como o XML.appendChild lida com um argumento de string e como um elemento XML raiz contendo apenas a string "null" pode ser coagido a um literal null.
Ben Burns,
@ Reboog711 Dê uma olhada no meu violino. "null"! = null` retornando trueé o comportamento desejado aqui. Se o contrário acontecesse, isso descartaria a string "null" do processo de codificação, que de fato seria a causa do problema. No entanto, como esse teste é bem-sucedido, o codificador continua, até que XML.appendChild o descarte devido a um bug de coerção.
Ben Burns,
4
Não se preocupe. Se você deseja ver o problema real, adicione var xml:XML = <root>null</root>; var s:String = (xml == null) ? "wtf? xml coerced to null?!!" : "xml not coerced to null."; trace(s);ao seu exemplo de código.
Ben Burns,
65

Converta todos os caracteres em seus equivalentes de entidade hexadecimal. Nesse caso, Nullseria convertido em&#4E;&#75;&#6C;&#6C;

doogle
fonte
41
Por favor, não faça isso. O CDATA foi criado para uso nos casos em que você precisa escapar de um bloco inteiro de texto.
Ben Burns,
4
Eu posso estar errado, mas não acho que a votação seja baixa apenas porque não foi sua solução, é como deveria funcionar. Além disso, você deve ter em mente que o problema exige uma solução heurística, pois não há uma maneira óbvia, como ficou evidente pela variedade de soluções postadas. Por fim, tendo em mente que eu não conheço CF, um decodificador não equivale apenas ao texto interno de <mensage><![CDATA[NULLuffyGirl> </message> ao texto interno de <mensage> NULL </ mensagem>? Se sim, então o CDATA é realmente uma solução?
dec
7
Eu votei mal porque esse é um anti-padrão. O bug neste caso não está no CF, está no ActionScript. No entanto, você levanta uma boa questão. Vou adicionar um teste ao meu violino para codificação CDATA.
perfil completo de Ben Burns
51

A string de um nullvalor no ActionScript fornecerá a string "NULL". Minha suspeita é que alguém decidiu que ele é, portanto, uma boa idéia para decodificar a seqüência "NULL"como null, causando a ruptura que você vê aqui - provavelmente porque eles estavam passando em nullobjetos e recebendo cordas no banco de dados, quando eles não queriam isso (portanto, verifique também esse tipo de bug).

Andrew Aylett
fonte
Sim, existem várias possibilidades aqui que exigirão mais depuração para diminuir. 1) O WSDL usado aqui é expressivo o suficiente para distinguir entre "NULL" como um valor de sequência e um valor nulo (ou omitido) real? 2) Em caso afirmativo, o cliente está codificando o sobrenome corretamente (como uma sequência e não como um literal nulo) 3) Nesse caso, o serviço está interpretando corretamente "NULL" como uma sequência ou coagindo-o a um valor nulo?
Pimlottc 17/11
39

Como um hack, você pode considerar um tratamento especial no lado do cliente, convertendo a string 'Null' em algo que nunca ocorrerá, por exemplo, XXNULLXX e convertendo novamente no servidor.

Não é bonito, mas pode resolver o problema de um caso de fronteira.

Marca
fonte
32
XXNULLXX também poderia ser um nome. Você não sabe. Talvez as pessoas na Indonésia não tenham sobrenome e usem uma variante de XXX como sobrenome, quando necessário.
gb.
3
Mesmo conceito, mas atualize todos os nomes no banco de dados e preface com algum caractere (1Null, 1Smith). Retire esse caractere no cliente. É claro que isso pode ser um trabalho menos do que a solução de Reboog.
bobpaul
14
@BenBurns Sim, mas e se eu quiser nomear meu filho &#78;&#117;&#108;&#108;?
Sirenes
@ Sirens Esse não é o problema. Se meu nome é "<">, então espero que seja escapado adequadamente como "" & "", isso é óbvio. O verdadeiro problema é que um aplicativo se comporta como se ele usasse uma lista negra de nomes.
Sr. Lister
30

Bem, acho que a implementação do codificador SOAP pelo Flex parece serializar valores nulos incorretamente. Serializá-los como um String Null não parece ser uma boa solução. A versão formalmente correta parece passar um valor nulo como:

<childtag2 xsi:nil="true" />

Portanto, o valor de "Null" não seria nada além de uma sequência válida, que é exatamente o que você está procurando.

Eu acho que consertar isso no Apache Flex não deve ser tão difícil de fazer. Eu recomendaria abrir um problema com o Jira ou entrar em contato com os caras da lista de discussão apache-flex. No entanto, isso iria corrigir apenas o lado do cliente. Não sei dizer se o ColdFusion será capaz de trabalhar com valores nulos codificados dessa maneira.

Consulte também a postagem no blog de Radu Cotescu Como enviar valores nulos em solicitações soapUI .

Christofer Dutz
fonte
6
Há boas informações aqui, então não vou votar, mas achei que vale a pena comentar. Por padrão, o XMLEncoder.as realmente codifica um nullvalor verdadeiro corretamente, configurando xsi:nil="true"o elemento Na verdade, o problema parece estar na maneira como o XMLpróprio tipo do ActionScript (não o codificador) lida com a string "null".
Ben Burns,
22

É um erro, mas supondo que exista um comprimento mínimo para SEARCHSTRING, por exemplo, 2 caracteres, substringo SEARCHSTRINGparâmetro no segundo caractere e passe-o como dois parâmetros: SEARCHSTRING1 ("Nu")e SEARCHSTRING2 ("ll"). Concatenateeles se juntam novamente ao executar a consulta no banco de dados.

SPitBalls.com
fonte
32
CDATA foi adicionado à especificação XML para evitar esses tipos de kludges.
Ben Burns,
8
Não há necessidade de escapar "Nulo" com CDATA, não existe uma palavra-chave nula em XML.
Eckes
6
Concorde com @eckes. Não entendo por que há toda essa conversa sobre CDATA. CDATA é útil apenas para escapar de caracteres que possuem um significado especial em XML. nenhum: n, u, ltêm uma semântica especial em XML. "NULL" e "<! [CDATA [NULL]]>>" são idênticos a um analisador XML.
Jasonkarns
9
@jasonkarns - Concordo 100% que não deve haver nada de especial no nó string / text NULL, mas ser pedante <blah>null</blah>e <blah><![CDATA[null]]>não ser o mesmo para um analisador XML. Eles devem produzir os mesmos resultados, no entanto, o fluxo lógico para manipulá-los é diferente. É esse efeito que estamos explorando como uma solução alternativa para o bug na implementação do XML flexível. Defendo isso em detrimento de outras abordagens, pois preserva a legibilidade do texto e não tem efeitos colaterais para outros analisadores.
Ben Burns