Cómo pasar "Nulo" (¡un apellido real!) A un servicio web SOAP en ActionScript 3

4636

Tenemos un empleado cuyo apellido es Nulo. Nuestra aplicación de búsqueda de empleados se anula cuando se usa ese apellido como término de búsqueda (que ahora es bastante frecuente). El error recibido (¡gracias Fiddler!) Es:

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

Lindo, ¿eh?

El tipo de parámetro es string.

Estoy usando:

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

Tenga en cuenta que el error no se produce al llamar al servicio web como un objeto desde una página de ColdFusion.

cuenta
fuente
66
Puede que no lo ayude tanto con el problema específico, pero SOAP 1.2 permite valores anulables, consulte w3.org/TR/2001/WD-soap12-20010709/#_Toc478383513
JensG
66
Tengo la sensación de que involucra a Dave Null.
George Gibson el
2
Al menos no involucra a Chuck Norris. He aquí por qué mantenerse alejado de él en código: codesqueeze.com/…
SDsolar
42
¿El empleado ha considerado cambiar su nombre?
Tatranskymedved
11
Realmente debería considerar comprar un perro Pointer y llamarlo NullPointer.
Antonio Alvarez

Respuestas:

1109

Rastreándolo

Al principio pensé que se trataba de un error de coerción en el que se nullestaba coaccionando "null"y "null" == nullestaba pasando una prueba . No es. Estaba cerca, pero muy, muy mal. ¡Lo siento por eso!

Desde entonces, he jugado mucho en wonderfl.net y he rastreado el código enmx.rpc.xml.* . En la línea 1795 de XMLEncoder(en la fuente 3.5), en setValue, todo el XMLEncoding se reduce a

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

que es esencialmente lo mismo que:

currentChild.appendChild("null");

Este código, de acuerdo con mi violín original, devuelve un elemento XML vacío. ¿Pero por qué?

Porque

Según el comentarista Justin Mclean en el informe de error FLEX-33664 , el siguiente es el culpable (vea las dos últimas pruebas en mi violín que verifican esto):

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
}

Cuando currentChild.appendChildse pasa la cadena "null", primero la convierte en un elemento XML raíz con texto null, y luego prueba ese elemento contra el literal nulo. Esta es una prueba de igualdad débil, por lo que el XML que contiene nulo se coacciona al tipo nulo o el tipo nulo se coacciona a un elemento xml raíz que contiene la cadena "nulo", y la prueba pasa donde posiblemente podría fallar. Una solución podría ser usar siempre pruebas de igualdad estrictas al verificar XML (o cualquier cosa, realmente) para "nulidad".

Solución

La única solución razonable que se me ocurre, salvo corregir este error en cada maldita versión de ActionScript, es probar los campos para "nulo" y escapar de ellos como valores CDATA .

Los valores CDATA son la forma más apropiada de mutar un valor de texto completo que de otra forma causaría problemas de codificación / decodificación. La codificación hexadecimal, por ejemplo, está destinada a caracteres individuales. Se prefieren los valores CDATA cuando se escapa del texto completo de un elemento. La razón principal de esto es que mantiene la legibilidad humana.

Ben Burns
fuente
298

En la nota xkcd , el sitio web de Bobby Tables tiene buenos consejos para evitar la interpretación incorrecta de los datos del usuario (en este caso, la cadena "Nulo") en consultas SQL en varios idiomas, incluido ColdFusion .

No queda claro a partir de la pregunta que esta es la fuente del problema, y ​​dada la solución anotada en un comentario a la primera respuesta (incrustando los parámetros en una estructura) parece probable que se tratara de otra cosa.

Alex Dupuy
fuente
239

El problema podría estar en el codificador SOAP de Flex. Intente extender el codificador SOAP en su aplicación Flex y depure el programa para ver cómo se maneja el valor nulo.

Supongo que se pasa como NaN (no es un número). Esto estropeará el proceso de desorganización de mensajes SOAP en algún momento (más notablemente en el servidor JBoss 5 ...). Recuerdo extender el codificador SOAP y realizar una comprobación explícita de cómo se maneja NaN.

Excepciones no detectadas
fuente
12
name = "Null" es, por supuesto, útil, y no veo cómo debería estar relacionado con NaN.
eckes
129

@ doc_180 tenía el concepto correcto, excepto que está enfocado en los números, mientras que el póster original tenía problemas con las cadenas.

La solución es cambiar el mx.rpc.xml.XMLEncoderarchivo. Esta es la línea 121:

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

(Miré el SDK de Flex 4.5.1; los números de línea pueden diferir en otras versiones).

Básicamente, la validación falla porque 'el contenido es nulo' y, por lo tanto, su argumento no se agrega al paquete SOAP saliente; causando así el error de parámetro faltante.

Tiene que extender esta clase para eliminar la validación. Luego hay una gran bola de nieve en la cadena, modificando SOAPEncoder para usar su XMLEncoder modificado, y luego modificando Operation para usar su SOAPEncoder modificado, y luego moidfying WebService para usar su clase de Operación alternativa.

Pasé unas horas en eso, pero necesito seguir adelante. Probablemente tomará uno o dos días.

Es posible que pueda arreglar la línea XMLEncoder y hacer algunos parches de mono para usar su propia clase.

También agregaré que si cambia a usar RemoteObject / AMF con ColdFusion, el valor nulo se pasa sin problemas.


11/16/2013 actualización :

Tengo una adición más reciente a mi último comentario sobre RemoteObject / AMF. Si está utilizando ColdFusion 10; entonces las propiedades con un valor nulo en un objeto se eliminan del objeto del lado del servidor. Por lo tanto, debe verificar la existencia de propiedades antes de acceder a ella o obtendrá un error de tiempo de ejecución.

Comprueba así:

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

Este es un cambio de comportamiento de ColdFusion 9; donde las propiedades nulas se convertirían en cadenas vacías.


Editar 06/12/2013

Como había una pregunta sobre cómo se tratan los nulos, aquí hay una aplicación de muestra rápida para demostrar cómo una cadena "nulo" se relacionará con la palabra reservada nulo.

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

La salida de rastreo es:

la cadena nula no es igual a la palabra reservada nula usando la condición! =

la cadena nula no es igual a la palabra reservada nula usando la condición ==

la cadena nula no es igual a la palabra reservada nula usando la condición ===

JeffryHouser
fuente
8
@ Reboog711 El apellido del empleado es, literalmente, la cadena "Nulo" como en "Mi nombre es Pat Null". Su respuesta no pasa el apellido del empleado. Su respuesta solo oculta el hecho de que "Null" está siendo coaccionado inapropiadamente al concepto de lenguaje null por el método appendChild () como lo describe Ben Burns. El resultado sigue siendo la falla del sistema para tratar con el Sr. o la Sra. Null.
Maxx Daymon
2
@MaxxDaymon Creo que malinterpretas cuál es mi respuesta en realidad. No presenta una solución; sino más bien una explicación de por qué ocurre el problema; y cita el código relevante de Flex Framework. Mi edición más reciente está quizás fuera de lugar; ya que discute un enfoque alternativo y no está directamente relacionado con la pregunta original.
JeffryHouser
1
Estás en el camino correcto, pero en ese punto del código contentestá la cadena "null", y "null" == null devuelve falso, por lo que la prueba se comporta como se esperaba. En cambio, creo que el problema es una mezcla de cómo XML.appendChild maneja un argumento de cadena, y cómo un elemento XML raíz que contiene solo la cadena "nulo" se puede convertir en un literal null.
Ben Burns
@ Reboog711 Echa un vistazo a mi violín. "null"! = null` regresar trueaquí es el comportamiento deseado. Si sucediera lo contrario, esto descartaría la cadena "nula" del proceso de codificación, que de hecho sería la causa del problema. Sin embargo, debido a que esta prueba tiene éxito, el codificador continúa hasta que XML.appendChild la descarta debido a un error de coerción.
Ben Burns
44
Sin preocupaciones. Si desea ver el problema real, agréguelo var xml:XML = <root>null</root>; var s:String = (xml == null) ? "wtf? xml coerced to null?!!" : "xml not coerced to null."; trace(s);a su muestra de código.
Ben Burns
65

Traduce todos los caracteres a sus equivalentes de entidad hexadecimal. En este caso, Nullse convertiría en&#4E;&#75;&#6C;&#6C;

doogle
fuente
41
Por favor no hagas esto. CDATA se creó para su uso en casos en los que necesita escapar de un bloque completo de texto.
Ben Burns
44
Podría estar equivocado, pero no creo que el voto negativo solo porque no fue tu solución es cómo se supone que debe funcionar. También debe tener en cuenta que el problema requiere una solución heurística, ya que no existe una forma obvia, como lo demuestra la variedad de soluciones publicadas. Por último, teniendo en cuenta que no conozco CF, ¿un decodificador no equivaldría simplemente al texto interno de <message><![CDATA[NULLfont>font>> </message> al texto interno de <message> NULL </ mensaje>? Si es así, ¿es realmente CDATA una solución?
doogle
77
Voté en contra porque es un antipatrón. El error en este caso no está en CF, está en ActionScript. Sin embargo, usted plantea un buen punto, no obstante. Agregaré una prueba a mi violín para la codificación CDATA.
Ben Burns
51

La cadena de un nullvalor en ActionScript dará la cadena "NULL". Mi sospecha es que alguien ha decidido que es, por lo tanto, una buena idea decodificar la cadena , "NULL"ya que nullcausa la rotura que ves aquí, probablemente porque estaban pasando nullobjetos y obteniendo cadenas en la base de datos, cuando no querían eso (así que asegúrese de verificar ese tipo de error también).

Andrew Aylett
fuente
Sí, hay varias posibilidades aquí que requerirán más depuración para reducir. 1) ¿Se utiliza aquí el WSDL lo suficientemente expresivo como para distinguir entre "NULL" como un valor de cadena y un valor nulo real (u omitido)? 2) Si es así, ¿el cliente codifica el apellido correctamente (como una cadena y no un literal nulo) 3) Si es así, ¿el servicio interpreta correctamente "NULL" como una cadena, o lo coacciona a un valor nulo?
pimlottc
39

Como truco, podría considerar tener un manejo especial en el lado del cliente, convirtiendo la cadena 'Nulo' en algo que nunca ocurrirá, por ejemplo, XXNULLXX y volviendo a convertir en el servidor.

No es bonito, pero puede resolver el problema para tal caso límite.

marca
fuente
32
XXNULLXX también podría ser un nombre. Usted no sabe Tal vez las personas en Indonesia no tengan apellido y usen una variante de XXX como apellido cuando sea necesario.
gb.
3
El mismo concepto, pero actualice todos los nombres en la base de datos y el prefacio con algún carácter (1Null, 1Smith). Despojar a ese personaje en el cliente. Por supuesto, esto podría ser un trabajo poco fácil que la solución de Reboog.
bobpaul
14
@BenBurns Sí, pero ¿y si quiero nombrar a mi hijo &#78;&#117;&#108;&#108;?
Sirenas
@Sirenas Ese no es el problema. Si mi nombre es "<& quot;>", entonces espero que se escape correctamente como & quot; & lt; & amp; quot; & gt; & quot ;, eso no hace falta decirlo. El verdadero problema es que una aplicación se comporta como si usara una lista negra para los nombres.
Sr. Lister el
30

Bueno, supongo que la implementación de Flex del codificador SOAP parece serializar valores nulos incorrectamente. Serializarlos como String Null no parece ser una buena solución. La versión formalmente correcta parece ser pasar un valor nulo como:

<childtag2 xsi:nil="true" />

Por lo tanto, el valor de "Nulo" no sería más que una cadena válida, que es exactamente lo que está buscando.

Supongo que solucionar esto en Apache Flex no debería ser tan difícil de hacer. Recomendaría abrir un problema de Jira o contactar a los chicos de la lista de correo apache-flex. Sin embargo, esto solo solucionaría el lado del cliente. No puedo decir si ColdFusion podrá trabajar con valores nulos codificados de esta manera.

Consulte también la publicación de blog de Radu Cotescu Cómo enviar valores nulos en solicitudes de soapUI .

Christofer Dutz
fuente
66
Aquí hay buena información, así que no votaré negativamente, pero pensé que valía la pena hacer un comentario. Por defecto, XMLEncoder.as realmente codificará un nullvalor verdadero correctamente, estableciendo xsi:nil="true"el elemento. El problema en realidad parece estar en la forma en que el XMLtipo ActionScript en sí (no el codificador) maneja la cadena "null".
Ben Burns
22

Es un error, pero suponiendo que haya una longitud mínima para SEARCHSTRING, por ejemplo, 2 caracteres, substringel SEARCHSTRINGparámetro en el segundo carácter y pasarlo como dos parámetros en su lugar: SEARCHSTRING1 ("Nu")y SEARCHSTRING2 ("ll"). Concatenatevolver a unirlos al ejecutar la consulta a la base de datos.

SPitBalls.com
fuente
32
Se agregó CDATA a la especificación XML para evitar este tipo de errores.
Ben Burns
8
No hay necesidad de escapar de "Nulo" con CDATA, no existe una palabra clave nula en XML.
eckes
66
De acuerdo con @eckes. No entiendo por qué se habla tanto de CDATA. CDATA solo es útil para escapar de caracteres que tienen un significado especial en XML. ninguno de: n, u, ltiene una semántica especial en XML. "NULL" y "<! [CDATA [NULL]]>>" son idénticos a un analizador XML.
jasonkarns
99
@jasonkarns: estoy de acuerdo al 100% en que no debe haber nada especial sobre el nodo de cadena / texto NULL, sino ser pedante <blah>null</blah>y <blah><![CDATA[null]]>no es lo mismo para un analizador XML. Ellos deben producir los mismos resultados, sin embargo, el flujo lógico para el manejo de ellos es diferente. Es este efecto el que estamos explotando como una solución al error en la implementación de flex XML. Abogo por esto sobre otros enfoques, ya que conserva la legibilidad del texto y no tiene efectos secundarios para otros analizadores.
Ben Burns