Получение кавычек в innerXML для передачи по сети, как " - PullRequest
5 голосов
/ 26 января 2010

(предупреждение - нечестивая смесь xml и бесплатной кодировки символов ниже.)

Короткая версия: Почему я не могу получить свой справочный вызов службы (c #, .net 3.5, код автоматической ссылки на службу, добавленный в VS2008) для правильного кодирования параметра, который должен выглядеть следующим образом: (ищите " биты .. .то моя проклятие.)

(другие дополнительные мыльные биты удалены для ясности)

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

Я устанавливаю параметр, звоню ... и служба возвращает приятное сообщение, говорящее: "НЕТ ТАК, МЫСЛИ ДЛЯ ВАС!"

Я попытался использовать несколько других форматов в строке параметров, передаваемых через веб-сервис:

action=\"add\"

который дал мне это по проводу (через фиддлер): action="add"

action=&quot;add&quot;

который дал мне это по телеграфу: action=&amp;quot;add&amp;quot;

и различные комбинации (action = "" add ""?!) С html.encode, url.encode, которые почти либо полностью разбомблены, либо показаны в виде двойных кавычек на проводе.

О, и я даже попробовал <![CDATA[&quot;]] окружающих. Это тоже не сработало.

Есть ли способ принудительно использовать двойную кавычку в бите innerHtml мыльного сообщения?

* (потому что так их хочет служба. Не задавайте вопросов. Это не те дроиды, которых вы ищете)

* * * Длинная извилистая версия:

Я пишу приложение для автоматизации некоторых процедур, которые в настоящее время обрабатываются (winform) административное приложение с графическим интерфейсом. (На самом деле, это MMC оснастки. Так или иначе.)

Для выполнения своих задач приложение winform связывается с сервером через стандартные вызовы веб-службы.

Я использую изящную VS2008 "ссылку на веб-сервис" для автоматического создания вещей (это техническое описание), и я успешно прошел аутентификацию веб-сервис. Чтобы убедиться, что я все делал правильно, я захватил звонки из приложения с графическим интерфейсом, а затем сравнили их с тем, что я отправлял на провод. Все было хорошо. Затем я столкнулся с пороками амперсанда. (Больше правильно, как заставить вещи кодировать правильно)

Для одного из вызовов веб-служба ожидает увидеть что-то вроде этого: (Я захватил приложение, отправив это через Fiddler)

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

Извлечение всего дополнительного SOAP-y материала для отображения соответствующего бита - это раздел <xmlData>, который передается. Обратите внимание на &quot;, окружающий параметры:

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

В моем коде у меня есть строка, построенная так:

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

Когда мое приложение отправляет его по проводам, скрипач фиксирует это: (опять же, убирая все мыло)

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

И принимающая служба отправляет обратно ошибку, которая ему не нравится. Он хочет &quot; d ## нит.

Правильно закодированы угловые скобки, но кавычки действительны в Строка HTTP, и не кодируется.

"Ах-ха!" говорит я: «Я просто буду предварительно кодировать вещи вручную!». Я пытался сделать что-то вроде этого:

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

Который был разослан как (опять же через фидлер) и всем моим амперсандам (в &quot;) конвертируется в &amp;quot; примерно так:

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

И, как вы можете догадаться, принимающий веб-сервис вернулся с "BZZT!" Спасибо за игру! ".

Я пробовал все виды escape и кодировать последовательности с похожими результатами. По сути, после всех моих манипуляций он проходит что-то вроде HttpUtility.HtmlEncode вправо , прежде чем выходить на провод, и любой амперсанды в строке преобразуются в &amp;. И цитаты (одиночные или double) игнорируются при конвертации. И принимающий веб-сервис хочет те кавычки, представленные как &quot; преследуют его, или он собирается возьми мяч и иди домой.

Моя последняя отчаянная надежда состояла в том, чтобы поймать сообщение правильно (я думал) перед тем, как оно поступило в провод, используя IClientMessageInspector для реализации проверки сообщения в событии BeforeSendRequest ... и вручную установить эти вещи, прежде чем он пошел на провод.

Я прекрасно фиксирую сообщение. Я могу даже вручную ввести &quot;.

Но когда его отправляют, и Wireshark, и Fiddler уверяют меня, что все идет хорошо отформатированным ... с кавычками, от которых я так отчаянно пытаюсь избавиться.

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

Я в своем уме. Я приму любые предложения, включая пожертвование [маленькая приятная вещь] на алтаре [мерзкое божество] или продажу того же моего [душа / сердце / сын бионакла сбор] . Поверь мне, это будет меньшее зло.

В соответствии с запросом ниже, вот сгенерированная заглушка сообщения: (Я думаю это то, что вы просили ...)

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

А вот как это называется:

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);
    }

Ответы [ 3 ]

2 голосов
/ 30 января 2010

Если я понимаю ваш длинный пост, вы, кажется, строите части своего XML, используя строки. Не делай этого. Всегда используйте один из API-интерфейсов XML для создания XML. Он знает правила цитирования.

0 голосов
/ 09 февраля 2010

Я так и не понял, как заставить это работать. Тем не менее, я нашел решение. Уродливое, хакерское решение, которым я не горжусь. Но это сработало.

В интересах потомков вот что я наконец сделал. Для каждого из (10ish) вызовов, которые мне нужно было сделать, я просто перехватывал SOAP-вызов через fiddler, а затем написал процедуру быстрого поиска-n-replace

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

Это вызвало простую процедуру, чтобы сделать http-вызов с соответствующими заголовками. Невежественный, но это сработало.

    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;
    }
0 голосов
/ 26 января 2010

Вам нужно вставить эту строку в сообщение SOAP в виде раздела CDATA, чтобы вы могли отформатировать его точно так, как вам хочется, и чтобы WCF не касался его.

Проблема, с которой вы столкнулись в данный момент, заключается в том, что служба ожидает кодировку где-то между xml и html, что, следовательно, плохо обрабатывается форматом, используемым форматером Soap, поэтому вам всегда придется избегать цитирует себя.

Однако у меня есть догадка, что если вы окружите его тегами CDATA <![CDATA[*yourstring*]]> в параметре строки, разметка для этого будет экранирована через провод и достигнет другого конца как &lt;![[*yourstring-XML-element-encoded*]]&gt;

Если это так, то вы могли бы изменить код, который был автоматически сгенерирован генератором ссылок на службы VS, чтобы тип параметра вызова метода на прокси-сервере принимал тип, равный в основном то же самое, что и строка, но сериализуется как раздел CDATA. Такой тип предоставляется на основе кода, написанного Марком Гравеллом для ответа на другой вопрос здесь . Проблема заключается в том, что если кто-либо использует команду «Обновить ссылку» в VS для этой ссылки на службу, любые внесенные вами изменения будут потеряны.

Итак, вместо этого сгенерируйте ссылку с помощью утилиты командной строки svcutil (вопреки распространенному мнению, VS не использует этот инструмент - что обидно, потому что он более гибкий), импортируйте конфигурацию и код, который он генерирует, в свой проект вручную и избавиться от «справочной службы». Таким образом, ваш код будет взломан так, как вы хотите, и вы можете легко увидеть его в дереве проекта в VS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...