Jak przekazać „Null” (prawdziwe nazwisko!) Do usługi internetowej SOAP w ActionScript 3

4635

Mamy pracownika o nazwisku Null. Nasza aplikacja do wyszukiwania pracowników zostaje zabita, gdy to nazwisko jest używane jako wyszukiwane hasło (co zdarza się obecnie dość często). Otrzymany błąd (dzięki Fiddler!) To:

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

Urocze, co?

Typ parametru to string.

Ja używam:

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

Zauważ, że błąd nie występuje podczas wywoływania usługi internetowej jako obiektu ze strony ColdFusion.

rachunek
źródło
6
Może to ci nie pomóc w konkretnym problemie, ale SOAP 1.2 dopuszcza wartości zerowalne, patrz w3.org/TR/2001/WD-soap12-20010709/#_Toc478383513
JensG
6
Mam wrażenie, że dotyczy Dave'a Nulla.
George Gibson,
2
Przynajmniej nie dotyczy Chucka Norrisa. Oto dlaczego trzymać się od niego z dala w kodzie: codequeeze.com/…
SDsolar
42
Czy pracownik rozważał zmianę nazwiska?
Tatranskymedved
11
Powinien naprawdę rozważyć zakup psa ze wskaźnikiem i nazwanie go NullPointer.
Antonio Alvarez

Odpowiedzi:

1108

Śledzenie tego

Na początku myślałem, że to błąd przymusu, do którego nullzmuszano mnie, "null"a test "null" == nullzdawał. To nie jest. Byłem blisko, ale bardzo, bardzo źle. Przepraszam za to!

Od tego czasu robiłem wiele skrzypków na wonderfl.net i śledziłem kod w mx.rpc.xml.*. W linii 1795 XMLEncoder(w źródle 3.5), w setValue, całe XMLEncoding sprowadza się do

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

który jest zasadniczo taki sam jak:

currentChild.appendChild("null");

Ten kod, zgodnie z moim oryginalnym skrzypkiem, zwraca pusty element XML. Ale dlaczego?

Przyczyna

Według komentatora Justina Mcleana w raporcie o błędach FLEX-33664 , winowajcą jest następujący (zobacz dwa ostatnie testy w mojej skrzypce, które to potwierdzają):

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
}

Po currentChild.appendChildprzekazaniu ciągu "null"najpierw konwertuje go do głównego elementu XML z tekstem null, a następnie testuje ten element pod kątem literału zerowego. To jest słaby test równości, więc albo XML zawierający null jest przymuszany do typu null, albo typ null jest przymuszany do głównego elementu xml zawierającego ciąg „null”, a test przechodzi tam, gdzie prawdopodobnie powinien zawieść. Jedną z poprawek może być zawsze stosowanie ścisłych testów równości podczas sprawdzania XML (lub cokolwiek, naprawdę) pod kątem „nieważności”.

Rozwiązanie

Jedynym rozsądnym obejściem, jakie mogę wymyślić, poza naprawieniem tego błędu w każdej przeklętej wersji ActionScript, jest przetestowanie pól pod kątem „null” i uniknięcie ich jako wartości CDATA .

Wartości CDATA są najbardziej odpowiednim sposobem na zmutowanie całej wartości tekstowej, która w innym przypadku spowodowałaby problemy z kodowaniem / dekodowaniem. Na przykład kodowanie szesnastkowe jest przeznaczone dla pojedynczych znaków. Wartości CDATA są preferowane, gdy unikasz całego tekstu elementu. Głównym powodem tego jest to, że zachowuje czytelność dla ludzi.

Ben Burns
źródło
298

W nocie xkcd The Bobby Stoły strona ma dobrą radę dla uniknięcia niewłaściwej interpretacji danych użytkownika (w tym przypadku napis „null”) w zapytań SQL w różnych językach, w tym ColdFusion .

Z pytania nie wynika jasno, że jest to źródło problemu, a biorąc pod uwagę rozwiązanie zanotowane w komentarzu do pierwszej odpowiedzi (osadzenie parametrów w strukturze), wydaje się prawdopodobne, że było to coś innego.

Alex Dupuy
źródło
239

Problem może dotyczyć kodera SOAP Flex. Spróbuj rozszerzyć koder SOAP w aplikacji Flex i debuguj program, aby zobaczyć, jak obsługiwana jest wartość null.

Domyślam się, że jest przekazywany jako NaN (Not a Number). To kiedyś zepsuje proces usuwania wiadomości z protokołu SOAP (przede wszystkim na serwerze JBoss 5 ...). Pamiętam rozszerzenie enkodera SOAP i wyraźne sprawdzenie, jak obsługiwany jest NaN.

uncaught_exceptions
źródło
12
name = "Null" jest oczywiście przydatne i nie rozumiem, jak powinien być powiązany z NaN.
eckes
129

@ doc_180 miał właściwą koncepcję, tyle że skupiał się na liczbach, podczas gdy oryginalny plakat miał problemy z łańcuchami.

Rozwiązaniem jest zmiana mx.rpc.xml.XMLEncoderpliku. To jest linia 121:

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

(Patrzyłem na Flex 4.5.1 SDK; numery linii mogą się różnić w innych wersjach.)

Zasadniczo sprawdzanie poprawności kończy się niepowodzeniem, ponieważ „treść jest pusta” i dlatego argument nie jest dodawany do wychodzącego pakietu SOAP; powodując w ten sposób błąd braku parametru.

Musisz rozszerzyć tę klasę, aby usunąć sprawdzanie poprawności. Następnie jest duża śnieżka w górę łańcucha, modyfikująca SOAPEncoder, aby używał zmodyfikowanego XMLEncodera, a następnie modyfikuje Operację, aby używać zmodyfikowanego SOAPEncodera, a następnie moidfying WebService, aby użyć alternatywnej klasy Operacji.

Spędziłem nad tym kilka godzin, ale muszę przejść dalej. To zajmie prawdopodobnie dzień lub dwa.

Możesz być w stanie po prostu naprawić linię XMLEncoder i zrobić trochę łatania małp, aby użyć własnej klasy.

Dodam też, że jeśli przełączysz się na używanie RemoteObject / AMF z ColdFusion, zerowanie jest przekazywane bez problemów.


Aktualizacja z 16.11.2013 :

Mam jeszcze jeden najnowszy dodatek do mojego ostatniego komentarza na temat RemoteObject / AMF. Jeśli używasz ColdFusion 10; następnie właściwości z wartością zerową na obiekcie są usuwane z obiektu po stronie serwera. Musisz więc sprawdzić istnienie właściwości przed uzyskaniem do niej dostępu, w przeciwnym razie pojawi się błąd czasu wykonywania.

Sprawdź w ten sposób:

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

Jest to zmiana zachowania w stosunku do ColdFusion 9; gdzie właściwości zerowe zamieniłyby się w puste ciągi.


Edytuj 12.06.2013

Ponieważ pojawiło się pytanie o to, jak traktowane są wartości zerowe, oto krótka przykładowa aplikacja pokazująca, jak ciąg „null” będzie odnosił się do słowa zarezerwowanego 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>

Dane wyjściowe śledzenia to:

ciąg zerowy nie jest równy null zarezerwowanemu słowu przy użyciu warunku! =

ciąg zerowy nie jest równy null zarezerwowanemu słowu przy użyciu warunku ==

ciąg zerowy nie jest równy null zarezerwowanemu słowu przy użyciu warunku ===

JeffryHouser
źródło
8
@ Reboog711 Nazwisko pracownika to dosłownie ciąg „Null”, jak w „Mam na imię Pat Null”. Twoja odpowiedź nie pasuje do nazwiska pracownika. Odpowiedź ukrywa tylko fakt, że „Null” jest niewłaściwie przymuszane do koncepcji języka null za pomocą metody appendChild () opisanej przez Bena Burnsa. Rezultatem jest nadal brak zdolności systemu do radzenia sobie z panem lub panią Null.
Maxx Daymon
2
@MaxxDaymon Myślę, że źle zrozumiałeś, jaka jest moja odpowiedź. Nie przedstawia rozwiązania; ale raczej wyjaśnienie, dlaczego problem występuje; i cytuje odpowiedni kod z Flex Framework. Moja najnowsza edycja jest prawdopodobnie niewłaściwa; ponieważ omawia alternatywne podejście i nie jest bezpośrednio związane z pierwotnym pytaniem.
JeffryHouser
1
Jesteś trochę na dobrej drodze, ale w tym momencie w kodzie contentjest ciąg "null", a „null” == null zwraca false, więc test zachowuje się zgodnie z przeznaczeniem. Zamiast tego uważam, że problem polega na tym, jak XML.appendChild obsługuje argument ciągu znaków i jak główny element XML zawierający tylko ciąg „null” może zostać przekonwertowany na literał null.
Ben Burns,
@ Reboog711 Spójrz na moje skrzypce. „null”! = powrót null truejest pożądanym zachowaniem tutaj. Gdyby stało się inaczej, to odrzuciłoby ciąg „null” z procesu kodowania, co w rzeczywistości byłoby przyczyną problemu. Ponieważ jednak test się powiedzie, koder kontynuuje działanie, dopóki XML.appendChild nie porzuci go z powodu błędu przymusu.
Ben Burns,
4
Bez obaw. Jeśli chcesz zobaczyć prawdziwy problem, dodaj var xml:XML = <root>null</root>; var s:String = (xml == null) ? "wtf? xml coerced to null?!!" : "xml not coerced to null."; trace(s);do próbki kodu.
Ben Burns,
65

Przetłumacz wszystkie znaki na ich odpowiedniki w postaci heksadecymalnej. W takim przypadku Nullzostanie przekonwertowany na&#4E;&#75;&#6C;&#6C;

doogle
źródło
41
Proszę nie rób tego. CDATA został stworzony do użytku w przypadkach, w których musisz uciec cały blok tekstu.
Ben Burns,
4
Mogę się mylić, ale nie sądzę, aby głosować z góry tylko dlatego, że to nie było twoje rozwiązanie, ale to, jak powinno działać. Należy również pamiętać, że problem wymaga heurystycznego rozwiązania, ponieważ nie ma jednego oczywistego sposobu, o czym świadczy różnorodność opublikowanych rozwiązań. Na koniec, pamiętając, że nie znam CF, czy dekoder nie po prostu zrównałby wewnętrznego tekstu <wiadomości> <![CDATA[NULL]]> </message> z wewnętrznym tekstem <wiadomości> NULL </ wiadomość>? Jeśli tak, to czy CDATA jest w ogóle rozwiązaniem?
doogle
7
Odrzuciłem głos, ponieważ jest to anty-wzór. Błąd w tym przypadku nie jest w CF, tylko w ActionScript. Niemniej jednak podnosisz dobrą rację. Dodam test do mojego skrzypce na kodowanie CDATA.
Ben Burns,
51

Tworzenie łańcucha nullwartości w języku ActionScript da ciąg "NULL". Podejrzewam, że ktoś zdecydował, że dobrym pomysłem jest więc odkodowanie łańcucha, "NULL"ponieważ nullpowoduje to zerwanie, które tu widzicie - prawdopodobnie dlatego, że przechodzili oni przez nullobiekty i dostawali ciągi do bazy danych, kiedy nie chcieli że (więc sprawdź też, czy nie ma tego rodzaju błędu).

Andrew Aylett
źródło
Tak, istnieje wiele możliwości, które będą wymagały więcej debugowania w celu zawężenia. 1) Czy używany tutaj WSDL jest wystarczająco wyrazisty, aby odróżnić „NULL” jako wartość ciągu od rzeczywistej wartości zerowej (lub pominiętej)? 2) Jeśli tak, to czy klient poprawnie koduje nazwisko (jako ciąg znaków, a nie literał zerowy) 3) Jeśli tak, to czy usługa poprawnie interpretuje „NULL” jako ciąg znaków, czy wymusza go na wartość zerową?
pimlottc,
39

Jako hack możesz rozważyć specjalną obsługę po stronie klienta, konwersję łańcucha „Null” na coś, co nigdy się nie zdarzy, na przykład XXNULLXX i konwersję z powrotem na serwerze.

To nie jest ładne, ale może rozwiązać problem takiego przypadku granicznego.

znak
źródło
32
XXNULLXX może być również nazwą. Nie wiesz Może ludzie w Indonezji nie mają nazwiska i w razie potrzeby używają wariantu XXX jako nazwiska.
gb.
3
Ta sama koncepcja, ale zaktualizuj wszystkie nazwy w bazie danych, a następnie wprowadź znak (1Null, 1Smith). Zdejmij tę postać z klienta. Oczywiście może to być praca z roztoczami niż rozwiązanie Reboog.
bobpaul
14
@BenBurns Tak, ale co, jeśli chcę nazwać moje dziecko &#78;&#117;&#108;&#108;?
Syreny
@Sirens To nie jest problem. Jeśli nazywam się „<", „spodziewam się, że będzie poprawnie poprzedzony znakiem„ "&";, co jest oczywiste. Prawdziwy problem polega na tym, że aplikacja zachowuje się tak, jakby używała czarnej listy dla nazw.
Pan Lister,
30

Wydaje mi się, że implementacja Flex przez koder SOAP wydaje się niepoprawnie serializować wartości zerowe. Serializacja ich jako ciąg zerowy nie wydaje się dobrym rozwiązaniem. Wydaje się, że formalnie poprawna wersja ma wartość zerową, ponieważ:

<childtag2 xsi:nil="true" />

Zatem wartość „Null” byłaby niczym innym jak prawidłowym łańcuchem, dokładnie tego, czego szukasz.

Wydaje mi się, że naprawienie tego w Apache Flex nie powinno być trudne. Polecam otwarcie problemu z Jira lub skontaktowanie się z facetami z listy mailingowej apache-flex. To jednak naprawiłoby tylko stronę klienta. Nie mogę powiedzieć, czy ColdFusion będzie w stanie pracować z wartościami zerowymi zakodowanymi w ten sposób.

Zobacz także wpis na blogu Radu Cotescu Jak wysyłać wartości zerowe w żądaniach soapUI .

Christofer Dutz
źródło
6
Są tutaj dobre informacje, więc nie będę głosować negatywnie, ale pomyślałem, że warto to skomentować. Domyślnie XMLEncoder.as faktycznie zakoduje nullpoprawnie wartość true , ustawiając xsi:nil="true"element. Wydaje się, że problem polega na tym, jak XMLsam typ ActionScript (nie koder) obsługuje ciąg "null".
Ben Burns
22

Jest to kludge, ale zakładając, że istnieje minimalna długość dla SEARCHSTRING, na przykład 2 znaki, parametr na drugi znak i przekazać go jako zamiast dwóch parametrów: a je z powrotem razem podczas wykonywania zapytania do bazy danych.substringSEARCHSTRINGSEARCHSTRING1 ("Nu")SEARCHSTRING2 ("ll"). Concatenate

SPitBalls.com
źródło
32
CDATA została dodana do specyfikacji XML, aby uniknąć tego rodzaju kłopotów.
Ben Burns
8
Nie ma potrzeby ucieczki przed „Null” za pomocą CDATA, nie ma czegoś takiego jak słowo kluczowe null w XML.
eckes 18.04.15
6
Zgadzam się z @eckes. Nie rozumiem, dlaczego tyle się mówi o CDATA. CDATA jest użyteczne tylko do zmiany znaczenia znaków, które mają specjalne znaczenie w XML. żaden z: n, u, lmają specjalne semantykę w XML. „NULL” i „<! [CDATA [NULL]]>” są identyczne z analizatorem składni XML.
jasonkarns 18.04.15
9
@ jasonkarns - Zgadzam się w 100%, że w łańcuchu tekstowym / węźle nie powinno być nic specjalnego NULL, ale trzeba być pedantycznym <blah>null</blah>i <blah><![CDATA[null]]>nie być tym samym dla parsera XML. Oni powinni produkować takie same wyniki, jednak przepływ logika dla ich postępowania jest inna. Ten efekt wykorzystujemy jako obejście błędu w implementacji flex XML. Opowiadam się za tym w stosunku do innych podejść, ponieważ zachowuje on czytelność tekstu i nie ma skutków ubocznych dla innych parserów.
Ben Burns,