2010-01-25 10 views
5

-ottenere quotazioni in InnerXml per andare sul filo come "

Versione corta (avviso miscela diabolica di XML e la codifica dei caratteri gratuita di seguito.): Perché non posso ottenere la mia chiamata riferimento al servizio (C# , .net 3.5, codice di riferimento del servizio automagic aggiunto a VS2008) per codificare correttamente un parametro che dovrebbe essere simile al filo: (cercare i bit " ... quelli sono la mia rovina.)

(altro extra bit di sapone rimossi per chiarezza)

<SOAP-ENV:Body><SOAPSDK4:SetCondition xmlns:SOAPSDK4="http://tempuri.org/message/"> 
<sharedSecret>buggerall</sharedSecret> 

<xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot; status=& 
quot;3&quot; name=&quot;TestProfile2&quot; mask=&quot;0&quot; campaign_id=&quot;33&quot; 
campaign_protected=&quot;N&quot; condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/& 
gt;&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData> 
</SOAPSDK4:SetCondition></SOAP-ENV:Body> 

Impostare il parametro, effettuare la chiamata ... e il servizio restituisce un messaggio carino che dice "NO SOU-- SOAP FOR YOU!"

Ho provato diversi altri formati nella mia stringa parametro passato-to-the-webservice:

action=\"add\" 

che mi ha dato questo sul filo (via violinista): action="add"

action=&quot;add&quot; 

che ha dato me questo sul filo: action=&amp;quot;add&amp;quot;

e varie combinazioni (action = "" aggiungi ""!!) con html.encode, url.encode che praticamente non completa bombardato, o mostrato come doppi apici sul filo.

Oh, e ho anche provato <![CDATA[&quot;]] circostante. Neanche questo ha funzionato.

C'è un modo per forzare una codifica a virgolette doppie nel bit innerHtml del messaggio soap?

* (perché è così che il servizio li vuole non fare domande questi non sono i droidi che stai cercando..)

* * * lungo, tortuoso versione:

Sto scrivendo un'app per automatizzare alcune procedure attualmente gestite da un'app di GUI amministrativa (winform). (In realtà, si tratta di uno snap-in MMC in. ogni modo.)

per compiere è compiti, l'applicazione di WinForm comunica con i suoi server tramite chiamate di servizio Web standard.

Sto usando il simpatico dispositivo di generazione automatica di riferimento del servizio web VS2008 (questa è la descrizione tecnica) e sono stato autenticato correttamente con il servizio Web . Per assicurarmi che stavo facendo le cose correttamente, ho catturato le chiamate dall'app GUI e poi le ho confrontate con quello che stavo inviando su il filo. Tutto andava bene. Poi mi sono imbattuto nei mali della e commerciale.(Più correttamente, come fare le cose per codificare correttamente)

Per una delle chiamate, il servizio web si aspetta di vedere qualcosa di simile: (ho catturato l'applicazione invio di questa via violinista)

<?xml version="1.0" encoding="UTF-8" standalone="no"?><SOAP-ENV:Envelope 
xmlns:SOAPSDK1="http://www.w3.org/2001/XMLSchema" xmlns:SOAPSDK2=" 
http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAPSDK3=" 
http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV=" 
http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAPSDK4:SetCondition 
xmlns:SOAPSDK4="http://tempuri.org/message/"><sharedSecret>0500001007C3525F3-F315-460D- 
AF5C-D84767130126094</sharedSecret><xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO 
action=&quot;add&quot; status=&quot;3&quot; name=&quot;TestProfile2&quot; mask=& 
quot;0&quot; campaign_id=&quot;33&quot;campaign_protected=&quot;N&quot; 
condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;& 
lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData></SOAPSDK4:SetCondition></SOAP- 
ENV:Body></SOAP-ENV:Envelope> 

Estrarre tutte le informazioni SOAP-y extra per mostrare il bit pertinente: questa è la sezione <xmlData> che viene trasmessa. Notare le &quot; che circondano i parametri:

&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot; 
status=&quot;3&quot; name=&quot;TestProfile2&quot; mask=&quot;0&quot; 
campaign_id=&quot;33&quot; campaign_protected=&quot;N&quot; 
condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;& 
lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt; 

Nel mio codice, ho una stringa di costruito in questo modo:

var serviceParams = "<SEARCHINFO-LIST><SEARCH_INFO action=\"add\" 
status=\"3\" name=\"TestProfileFromExternApp\" mask=\"0\" campaign_id=\"33\" 
campaign_protected=\"N\" 
condition_protected=\"N\"><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>"; 

Quando la mia app invia fuori sopra il filo, violinista cattura questo: (ancora una volta , escludendo tutte le cose SOAP)

&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action="add" status="3" 
name="TestProfileFromExternApp" mask="0" campaign_id="33" 
campaign_protected="N" condition_protected="N"&gt;&lt;CONDITIONS/&gt; 
&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt; 

E il servizio ricevente invia un errore che non piace. Vuole il &quot; d ## nit.

Le parentesi angolari vengono codificate correttamente, ma le virgolette sono valide nella stringa HTTP e non vengono codificate.

"Ah-ha!" Dice I, "Presterò solo manualmente le cose!". Ho tentato di fare qualcosa di simile:

var serviceParams = "<SEARCHINFO-LIST><SEARCH_INFO action=&quot;add&quot; 
status=&quot;3&quot; name=&quot;TestProfileFromExternApp&quot; 
mask=&quot;0&quot; campaign_id=&quot;33&quot; 
campaign_protected=&quot;N&quot; 
condition_protected=&quot;N&quot;><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>"; 

che è stata inviata come (ancora una volta, via violinista) e tutti i miei e commerciali (nel &quot;) vengono convertiti a &amp;quot; in questo modo:

&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action=&amp;quot;add&amp;quot; 
status=&amp;quot;3&amp;quot; 
name=&amp;quot;TestProfileFromExternApp&amp;quot; mask=&amp;quot;0&amp;quot; 
campaign_id=&amp;quot;33&amp;quot; campaign_protected=&amp;quot;N&amp;quot; 
condition_protected=&amp;quot;N&amp;quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt; 

E, come puoi immaginare, il servizio web di ricezione è tornato con "BZZT! Grazie per aver giocato!".

Ho provato tutti i tipi di sequenze di escape e codifica con risultati simili. Effettivamente, dopo tutta la mia manipolazione passa attraverso qualcosa come HttpUtility.HtmlEncode a destra prima di uscire sul filo, e qualsiasi e commerciale nella stringa viene convertito in &amp;. E le virgolette (singole o doppie) vengono ignorate nella conversione. E il servizio web di ricezione vuole quelle citazioni rappresentate come &quot; doggone, o sta andando a prendere la sua palla e andare a casa.

mia ultima, disperata speranza era quella di catturare il messaggio giusto (ho pensato) prima è andato sul filo utilizzando IClientMessageInspector to implement message inspection in caso BeforeSendRequest ... e manualmente impostato quelle cose prima è andato al filo.

Cattura bene il messaggio. Posso anche inserire manualmente &quot;.

Ma quando viene inviato, sia wirehark che violinista mi assicurano che sta uscendo ben formattato ... con le virgolette che cerco disperatamente di eliminare.

<xmlData xsi:type="xsd:string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
&lt;SEARCHINFO-LIST&gt;&lt;SEARCH_INFO action="add" status="3" 
name="TestProfileFromExternApp" mask="0" campaign_id="33" campaign_protected="N" 
condition_protected="N"&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt; 
&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;</xmlData> 

Sono alla fine del mio ingegno. Accetterò qualsiasi suggerimento fino al sacrificio compreso [piccola cosa coccolona] sulla modifica di [divinità vile] o vendendo lo stesso la mia collezione [collezione cuore anime/cuore/figlio]. Fidati di me, sarebbe il male minore.

Per richiesta qui di seguito, ecco lo stub messaggio generato: (I che questo è quello che stavi chiedendo ...)

public int SetCondition(string sharedSecret, string xmlData, out string resultValue) 
{ 
    tzGui.tzCampaign.SetConditionRequest inValue = new tzGui.tzCampaign.SetConditionRequest(); 
    inValue.sharedSecret = sharedSecret; 
    inValue.xmlData = xmlData; 
    tzGui.tzCampaign.SetConditionResponse retVal = ((tzGui.tzCampaign.CampaignSoapPort)(this)).SetCondition(inValue); 
    resultValue = retVal.resultValue; 
    return retVal.Result; 
} 

Ed ecco come è sempre chiamato:

void SetConditionTask() 
{ 
    //ok, now we *try* and create a new profile 
    var tzCampaignCxn = new tzCampaign.CampaignSoapPortClient("CampaignSoapBinding"); 
    //no worky 
    //string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=\"add\" status=\"3\" name=\"TestProfileFromExternApp\" mask=\"0\" campaign_id=\"33\" campaign_protected=\"N\" condition_protected=\"N\"><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>"; 

    //this one doesn't work 
    //string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=<![CDATA[ &quot; ]]>add<![CDATA[ &quot; ]]> status=<![CDATA[ &quot; ]]>3<![CDATA[ &quot; ]]> name=<![CDATA[ &quot; ]]>TestProfileFromExternApp<![CDATA[ &quot; ]]> mask=<![CDATA[ &quot; ]]>0<![CDATA[ &quot; ]]> campaign_id=<![CDATA[ &quot; ]]>33<![CDATA[ &quot; ]]> campaign_protected=<![CDATA[ &quot; ]]>N<![CDATA[ &quot; ]]> condition_protected=<![CDATA[ &quot; ]]>N<![CDATA[ &quot; ]]>><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>"; 

    //this one doesn't either 
    string xmlData = "<SEARCHINFO-LIST><SEARCH_INFO action=&quot;add&quot; status=&quot;3&quot; name=&quot;TestProfileFromExternApp&quot; mask=&quot;0&quot; campaign_id=&quot;33&quot; campaign_protected=&quot;N&quot; condition_protected=&quot;N&quot;><CONDITIONS/><EXPRESSIONS/></SEARCH_INFO></SEARCHINFO_LIST>"; 

    string createProfileResultVal = string.Empty; 
    tzCampaignCxn.SetCondition(SharedSecret, xmlData, out createProfileResultVal); 
    txtResults.AppendText(Environment.NewLine + Environment.NewLine + createProfileResultVal); 
    } 
+1

Il problema è: " ..." ... 'è un XML perfettamente valido. Sebbene non sia vietato, le virgolette non devono essere sfuggite nei nodi di testo XML. Il tuo servizio web non parla XML standard se non gli piace. – dtb

+0

Hai perfettamente ragione ~ sono perfettamente validi. Ma ci dovrebbe essere un modo per me di mettere " sul filo ... giusto? (oh, e sfortunatamente non è il mio servizio web ... oh, ma se fosse ...!) :) –

+0

C'è un modo. Puoi risolvere ogni aspetto di WCF e sostituire parti con le tue implementazioni ... Puoi mostrarci la dichiarazione del metodo generato e come la stai chiamando? Ho un'idea per una soluzione semplice ma ho bisogno di controllare. – dtb

risposta

0

È necessario ottenere quella stringa nel messaggio SOAP come una sezione CDATA, in modo che sia possibile formattare esattamente come desiderato e in modo che WCF non lo tocchi.

Il problema si ha in questo momento è che il servizio è in attesa di una codifica che da qualche parte tra XML e HTML -, che quindi non è ben gestita dal formato che il formattatore Soap sta usando - così si sono sempre costretta a fuggire le citazioni te stesso.

Ho la sensazione, però, che se si circondano nei tag CDATA <![CDATA[*yourstring*]]> nel parametro di stringa, il markup per questo sarà sfuggito sul filo e raggiungere l'altra estremità come &lt;![[*yourstring-XML-element-encoded*]]&gt;

Se questo è il caso, ciò che è possibile fare è modificare il codice generato automaticamente dal generatore di riferimento del servizio VS in modo che il tipo di parametro della chiamata al metodo sul proxy accetti un tipo che è sostanzialmente uguale a uguale a una stringa, ma si serializza automaticamente come una sezione CDATA. Tale tipo è fornito sulla base del codice scritto da Marc Gravell per rispondere allo another question here. Il problema con questo è che se qualcuno usa il comando 'Aggiorna riferimento' in VS su quel riferimento di servizio, qualsiasi modifica apportata andrà persa.

Quindi, invece, genera il riferimento con l'utility della riga di comando svcutil (contrariamente alla credenza popolare, VS non usa questo strumento - il che è un peccato perché è più flessibile), importa la config e il codice che genera in il tuo progetto manualmente e sbarazzarsi del 'riferimento di servizio'. In questo modo, il codice è tuo da hackerare come vuoi, e puoi facilmente vederlo nell'albero del progetto in VS.

+0

Intendevo l'intera stringa - non appena intorno alle virgolette - ma in ogni caso, sospetto, sta ancora formattando i caratteri. In questo caso potresti provare a utilizzare la classe CDataWrapper dell'altro esempio a cui ho collegato, che implementa esplicitamente IXmlSerializable in modo che invii una stringa come CDATA: in questo modo si impedisce tutta la formattazione predefinita dei messaggi WCF per i dati di quel parametro - è solo che deve raggiungere e formatter WCF come classe CDataWrapper, non una stringa - da qui la necessità di modificare il codice proxy che è stato generato per voi. –

+0

Mmm ... ora sembra * esattamente * come quello di cui ho bisogno. Farò un tentativo ... –

+0

Ho provato a cambiare tutti i riferimenti a string xmlData su CDataWrapper e sembrava che sarebbe andato, ma poi: {"Si è verificato un errore che riflette 'xmlData'."} " Il tipo tzGui.CDataWrapper potrebbe non essere serializzato con i messaggi con codifica SOAP Impostare l'utilizzo per il messaggio su Letterale. " Ho modificato use = encoded to use = literal in Campaign.WSDL (come in questo modo): *** *** Stesso errore. Forse non l'ho cambiato nel posto giusto? –

2

Se capisco il tuo post lungo, sembra che tu stia costruendo parti del tuo XML usando stringhe. Non farlo. Utilizza sempre una delle API XML per creare XML. Conosce le regole di quotazione.

0

Non ho mai capito completamente come farlo funzionare. Tuttavia, ho trovato una soluzione. Una soluzione orribile, di cui non sono fiero. Ma ha funzionato.

Nell'interesse dei posteri, ecco cosa ho finalmente fatto. Per ciascuna delle (10ish) chiama avevo bisogno di fare, ho semplicemente catturato la chiamata SOAP via violinista, e poi scritto una rapida ricerca-n-sostituzione di routine

public string Newrule(string ruleName, DecisionSet decisionSet) 
    { 
     var soapString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><SOAP-ENV:Envelope " + 
         "xmlns:SOAPSDK1=\"http://www.w3.org/2001/XMLSchema\" xmlns:SOAPSDK2=\"http://www.w3.org/2001/XMLSchema-instance\" " + 
         "xmlns:SOAPSDK3=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" + 
         "<SOAP-ENV:Body><SOAPSDK4:SetCondition xmlns:SOAPSDK4=\"http://tempuri.org/message/\"><sharedSecret></sharedSecret>" + 
         "<xmlData>&lt;SEARCHINFO_LIST&gt;&lt;SEARCH_INFO action=&quot;add&quot; status=&quot;3&quot; " + 
         "name=&quot;" + ruleName + "&quot; mask=&quot;0&quot; DecisionSet_id=&quot;" + decisionSet.Id + 
         "&quot; DecisionSet_protected=&quot;N&quot; " + 
         "condition_protected=&quot;N&quot;&gt;&lt;CONDITIONS/&gt;&lt;EXPRESSIONS/&gt;&lt;/SEARCH_INFO&gt;&lt;/SEARCHINFO_LIST&gt;" + 
         "</xmlData></SOAPSDK4:SetCondition></SOAP-ENV:Body></SOAP-ENV:Envelope>"; 

     var headerUrl = "http://tempuri.org/action/DecisionSet.SetCondition"; 
     var serviceUrl = "/webservice/DecisionSet.WSDL"; 
     var result = sender.MakeRequest(soapString, serviceUrl, headerUrl,null); 
     var idSearch = @"SEARCH_INFO id=&quot;(\d+)&quot;"; 

     var ruleId = Regex.Match(result, idSearch).Groups[1].Value; 

     return ruleId; 
    } 

Questa chiamata una semplice routine per effettuare una chiamata http con le intestazioni appropriate. Inelegante, ma ha funzionato.

public string MakeRequest(string requestString, string serviceUrl, string headerUrl, string useragent) 
    { 
     string query = requestString.Replace(@"<sharedSecret></sharedSecret>", "<sharedSecret>"+secret+"</sharedSecret>"); 
     query = query.Replace(@"<SessionID></SessionID>", "<SessionID>" + secret + "</SessionID>"); 
     HttpWebRequest req = (HttpWebRequest)WebRequest.Create(server + serviceUrl); 
     //if (proxy != null) req.Proxy = new WebProxy(proxy, true); 
     req.Headers.Add("SOAPAction", headerUrl); 
     if (useragent == null) 
      req.UserAgent = "SOAP Toolkit 3.0"; 
     else 
     { 
      req.UserAgent = useragent; 
     } 
     req.ContentType = "text/xml;charset=\"utf-8\""; 
     req.Accept = "text/xml"; 
     req.Method = "POST"; 
     Stream stm = req.GetRequestStream(); 

     StreamWriter sw = new StreamWriter(stm); 
     sw.Write(query); 
     sw.Flush(); 
     stm.Close(); 
     WebResponse resp = req.GetResponse(); 
     stm = resp.GetResponseStream(); 
     StreamReader r = new StreamReader(stm); 
     string response = (r.ReadToEnd()); 

     return response; 
    }