:: Visual Foxpro, Foxpro for DOS
XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
Возникла необходимость взаимодействовать с одной системой УТМ (это универсальный транспортный модуль системы ЕГАИС, но это не важно).
Взаимодействие строится с помощью http-запросов. В документации к софту предлагается использовать утилиту curl, где нужная мне задача - отправка некоторого xml-файла решается так:
curl -F "xml_file=@wbact.xml" [url]http://localhost:8080/opt/in/WayBillAct[/url]

эта команда выполняет POST запрос к локальному серверу и передает ему заданный файл. Пытаюсь реализовать нужно поведение в фоксе, используя объект XmlHttp:

tcData = [...] && формируем xml-файл
* формируем тело запроса
CRLF = chr(13) + chr(10)
local lcBoundary, lcContent
lcBoundary = [------------f7a7d1d718464590]
lcContent = ;
CRLF + m.lcBoundary +;
+ CRLF + [Content-Disposition: form-data; name="xml_file"; filename="wbact.xml"] ;
+ CRLF + [Content-Type: text/xml; charset=utf-8];
+ CRLF + CRLF + m.tcData ;
+ CRLF + CRLF + m.lcBoundary
* отправка запроса
oHttp=CREATEOBJECT("Msxml2.XMLHTTP" )
oHttp.Open( [POST], [ttp://localhost:8080/opt/in/WayBillAct], .F.)
oHttp.setRequestHeader("Accept", "text/xml")
oHttp.setRequestHeader( "Content-Type", [multipart/form-data; boundary=] + m.lcBoundary )
oHttp.setRequestHeader( "Content-Length", len( m.lcContent ) )
oHttp.send( m.lcContent )
if oHttp.status<>200
Обработка ошибки. Здесь я и вижу, что отправка не получилась.
endif
cData = oHttp.ResponseBody() &&ResponseText

параметры заголовка позаимствовал из кода для 1С, который создавался для решения той же задачи (1c.klerk.ru).

к сожалению, этот код приводит к тому, что сервер возвращает код 500 (внутренняя ошибка сервера) и совершенно не информативный ответ.
хотя если делать это утилитой curl, то все работает как надо.

Я понимаю, что вопрос мой не очень внятный, и, скорее всего, не столько о FoxPro, но это мой первый опыт работы с xmlhttp, с УТМ и т.д. - короче это темный лес, когда не знаешь, где накосячил и за что хвататься.
Но, может кто-то решал похожую задачу или увидит что-то подозрительное в моем коде. Короче, нужна конструктивная идея - с чего начать, что мне нужно знать?

P.S. Похожий код для получения данных от сервера GET-запросом работает без проблем. Т.е. приложение-сервер настроен и работает, адрес ресурса передается правильный.
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
Не хватает главной детали - ожидания ответа, типа:

Do While !m.oXMLHTTP.readyState=4 && 0/1/2/3/4 - до Open / после .Open / отправлен (Запрос был передан) / Ответ сервера принят частично / Complete-соединение закрыто (используется только 4...)
* Это даст возможность отработать фокс-кодам в кнопках Click и других методах, когда выполняются обороты цикла
* Без этой команды - не будут отрабатываться
DOEVENTS && [FORCE] - Обеспечивает остановку выполнения программного кода Visual FoxPro пока выполняется системное событие Windows, например такое как перемещение указателя мышки.
=sleep(100) && не будем частить с опросом, дадим потоку, в котором крутится фокс, поделать что-то своё
* таймаут ожидания еще можно приделать, несколько секунд
ENDDO
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
Разве третий параметр .F. в методе Open не избавляет от необходимости ожидать ответ?
Ну и, наверное, статус был бы другим - типа запрос обрабатывается, а не "внутренняя ошибка сервера" - нет?
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
хз, у меня так:

* Кстати, в Open() есть еще параметры:
* [3] - [1,T] - асинхронный запрос (см. .readyState), 0,F - синхронный запрос (ожидаем ответ сервера бесконечно)
* [4,5] - userName, password - данные для HTTP-авторизации
m.t = IIF(m.oMODA.Timeout>0, .T., .F.) && флаг АСИНХРОННО
m.oXMLHTTP.Open(m.oMODA.Open, m.u, m.t) && будем обмениваться с сервером, (1) - методом GET/POST, (2) - URL[?ПЗ], [3] - асинхронно, [4,5] - user,password
Т.е. я с .T. запускаю... с F не пробовал.

Но все равно, посмотри readyState чему равно:
m.r = (m.oXMLHTTP.readyState=4 .AND.; && Complete
INLIST(m.oXMLHTTP.status,200,400)) && или statustext="OK", или "Запрос не понят" (400 возвратили мне, когда паспорт указал в неверном формате...)

Доб. [ttp://localhost:8080/opt/in/WayBillAct] - это ошибка? http
Если не получится, то дальше заголовки запроса надо копать, попробуй также .Send(0h+m.lcContent ), кодировки... Да! URL-кодирование текста в Send надо наверное. Про текст в lcContent не понятно. В исходном примере именно так надо? Некоторые строки похожи на заголовки...



Исправлено 2 раз(а). Последнее : of63, 26.11.15 17:47
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
А зачем ты во внутренностях запроса сам рисуешь MIME заголовки? Это реально необходимо?

Вообще я бы поставил прокси какой (да хоть и самописный) и посмотрел какие реально запросы шлёт curl по такой команде и твой код...


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
of63
Но все равно, посмотри readyState чему равно:

Да, спасибо, это будет не лишним.

of63
Доб. [ttp://localhost:8080/opt/in/WayBillAct] - это ошибка?

Да, в реальном коде там переменная с адресом. Адрес верный, т.к. на неверный получаю соотв. сообщение "ресурс не найден".

of63
Если не получится, то дальше заголовки запроса надо копать, попробуй также .Send(0h+m.lcContent ), кодировки... Да! URL-кодирование текста в Send надо наверное. Про текст в lcContent не понятно. В исходном примере именно так надо? Некоторые строки похожи на заголовки...

0h нужен для xml-файла, сам запрос содержит файл внутри, значит, 0 нужно добавить к tcData, а не ко всему телу. Так? Это я пробовал, не помогает.
В исходном примере сделано как сделано, я не знаю, насколько автор кода для 1С правильно решает задачу. Но там (и в других примерах кода) все так делают - xml файл помещают в файл с заголовками, затем устанавливают параметры запроса, затем send.

Вот я думаю, может xml внутри должен быть закодирован, например в BASE-64 или это не обязательно?
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
Про 0h - это превращает строку запроса в Send() из C- в Q-тип. Похоже это как-то меняет внутренности Fox при обращении к внешним обьектам. Перекодирования, например, не будет, которое, здесь неуместно.

Про сам передаваемый XML-файл. Наверное, это просто обычный текстовый файл, фишка с 0h - это не про него. Надо ли как-то кодировать файл - это потом. Надо пробиться к серверу сначала. Может, просто пустой XML-файл создай для начала, или достань заведомо правильный.

С multipart не пробовал, но на форуме тут он упоминался.

И сниффер поставь какой нибудь (они все платные какие-то и убогие), чтобы посмортеть реальные HTTP-запросы от образцовой программы.

Да, и URL-кодирование (а может и еще UTF-8, в нужной последовательности) содержимого в Send(). Ведь ведь текст в Send() - это замена текста в адресной строке URL, это то, что после знака ?. А в строке не может быть CRLF, и даже пробелов, и даже русских букв, а только кошерные символы. Надо URL-кодировать. На форуме тоже есть примеры кодеров.



Исправлено 1 раз(а). Последнее : of63, 26.11.15 18:27
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
Igor Korolyov
А зачем ты во внутренностях запроса сам рисуешь MIME заголовки? Это реально необходимо?
Честно говоря, не знаю. Я посмотрел несколько примеров, как это решают другие для этой системы и сделал "как все". Все делают похожим образом - сперва создают xml, потом помещают его в файл с заголовками, ограничивая некоторой уникальной строкой. Затем еще задают параметры запроса. Возможно, что я что-то делаю не правильно или не оптимально - я просто не знаю другого способа. Т.е. цель, конечно, не сделать именно так, а решить задачу - отправить файл серверу.

У меня вот вопрос - мог я напутать с переводами строки в заголовках - например, в начале ставятся два CRLF - это не может быть лишним?
И строка-ограничитель - есть на нее какие-то ограничения? Например, сколько минусов в начале строки может стоять или допустимые символы, должен ли ограничитель быть всегда уникальным или можно использовать один и тот же?

Igor Korolyov
Вообще я бы поставил прокси какой (да хоть и самописный) и посмотрел какие реально запросы шлёт curl по такой команде и твой код...
Да, это мысль - сравнить отправляемые данные. Но получится ли у меня это реализовать, если самой системы (УТМ) у меня нет на машине? (и поставить ее - целое дело)
Или, по-другому, есть ли простой способ, готовая утилита какая-нибудь, чтобы я мог пользователя попросить провести такой эксперимент, без сложных настроек.
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
of63
Про 0h - это превращает строку запроса в Send() из C- в Q-тип. Похоже это как-то меняет внутренности Fox при обращении к внешним обьектам. Перекодирования, например, не будет, которое, здесь неуместно.

Ладно, попробую.

of63
Про сам передаваемый XML-файл. Наверное, это просто обычный текстовый файл, фишка с 0h - это не про него.
Да, обычный текст в переменной, xml в utf-8. Но, 0h приходится добавлять, если надо сохранить в файл, например, т.к. иначе он не правильно кодируется. Вот не знаю, нужно ли его здесь добавлять.

of63
Надо ли как-то кодировать файл - это потом. Надо пробиться к серверу сначала. Может, просто пустой XML-файл создай для начала, или достань заведомо правильный.
xml по структуре точно правильный. Т.е. curl отправляет его и получает ожидаемую реакцию. До сервера-то по-моему доходит дело. Ошибка ведь уже "серверная". И ResponceText содержит строку
<A><error>null</error><ver>2</ver></A>
такой же ответ сервер возвращает, когда, например, в данных ошибка, только вместо null - что-то осмысленное пишет. Но попробую и с пустым файлом.

of63
Да, и URL-кодирование (а может и еще UTF-8, в нужной последовательности) содержимого в Send(). Ведь ведь текст в Send() - это замена текста в адресной строке URL, это то, что после знака ?. А в строке не может быть CRLF, и даже пробелов, и даже русских букв, а только кошерные символы. Надо URL-кодировать. На форуме тоже есть примеры кодеров.

Мммм... Это точно? Я так понимаю, что в моем случае файл отправляется похожим образом, как вложение к электронному письму. Я вот только не уверен, не надо ли xml кодировать в base64 (в одних примерах вроде как люди это делали, в других - нет).

Ладно, спасибо, пойду спать. Утром, на свежую голову буду переваривать. Спасибо за наводки.
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
Поиск multipart/form-data (точное соответствие):
forum.foxclub.ru
forum.foxclub.ru
www.faqs.org (фирменная ссылка от ИК)

черт ногу сломит. пробовать все надо
Да, CRLF - это правильно, не надо CRLF кодировать, пара CRLF тоже что-то означает. А вот содержимое между CRLF-ами точно надо как-то закодировать, ятд.



Исправлено 1 раз(а). Последнее : of63, 26.11.15 19:26
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
matod
не уверен, не надо ли xml кодировать в base64
Зависит от заголовка Content-Transfer-Encoding для этой самой секции, как я понимаю.
matod
Но получится ли у меня это реализовать, если самой системы (УТМ) у меня нет на машине?
Не проблема - это делается через любой локальный прокси позволяющий сниффить (подсматривать) трафик. Самый тупой и примитивный можно даже на фоксе сваять - пара сокетов, один слушает - к нему и коннектится прога, другой пересылает все пакеты на "настоящий" адрес, ну и ессно все эти пакеты скидывает в файлики.
Мне, когда это потребовалось, оказалось проще свой тупой микро-проксик нарисовать, нежели искать/выбирать подходящий готовый.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
Ничего внятно не пишут про кодировку строки между CRLF-ами, но пишут, что точно должна быть свободна от символов CR LF в ней, приговаривая, что кодировка base64 - подходит для этой цели. И в описании curl тоже про base64 упоминают.
Закодируй вобщем m.tcData STRCONV(13), как сервер ответит на это?
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
to of63
Кодирования может и не быть (как Игорь выше сказал). В моем случае я нашел дамп, полученный сниффером для curl - там видно, что xml отправляется как есть, без кодирования. Получилось и без него (см. ниже)
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
А текст файла XML содержит символы CR, LF ?
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
Ура, и еще одна победа разума над бездушным железом! :bodr:

Несколько подсказок, еще пара найденных примеров и проблема успешно решена. Причина была проста - поверхностное чтение документации, а именно стандарта RFC2388, ошибка в коде, который я взял за основу, и невнимательность. Проблема была в том, что формируя тело запроса нужно строку разделитель предварять двумя символами "минус", а завершающую строку обрамлять парой минусов с двух сторон. В дампе сниффера и некоторых примерах сам разделитель начинается с последовательности минусов, поэтому эта пара визуально была не заметна, а в других примерах не обратил на минусы внимания.

Дополнительно, я убрал пару лишних CRLF из тела запроса и добавил в начало отправляемой строки 0h, по совету Олега. Не знаю, было ли это важно, но с этими изменениями все заработало - сервер отреагировал ожидаемым образом. Вот исправленный примерный код (tcUri - адрес, tcData - содержимое xml-файла БЕЗ символа 0h в начале, CRLF = chr(13)+chr(10) ):

* Формируем тело запроса
* Вначале нужно добавить 0h
* Строка-разграничитель должна начинаться с двух минусов "--"
* затем идет уникальный набор символов, не повторяющийся внутри данных (до 70 символов)
* затем подзаголовок блока, с обязательным параметром name - имя поля формы
* после разграничителя и строк заголовка перед данными должна быть пустая строка
* завершающий разграничитель дополняется минусами слева и справа
local lcBoundary, lcContent
lcBoundary = [------------957825eee5914fecb6d15609b790db30]
lcContent = ;
0h + [--] + m.lcBoundary +;
+ CRLF + [Content-Disposition: form-data; name="xml_file"; filename="wbact.xml"] ;
+ CRLF + [Content-Type: text/xml; charset=utf-8];
+ CRLF + CRLF + m.tcData ;
+ CRLF + [--] + m.lcBoundary + [--] + CRLF
* Создаем объект, устанавливаем параметры запроса, отправляем
* В данном случае используется синхронный запрос. Для асинхронного
* нужно будет организовать ожидание ответа с проверкой состояния ответа
local oHttp
oHttp=CREATEOBJECT("Msxml2.XMLHTTP" )
oHttp.Open( [POST], m.tcUri, .F.)
oHttp.setRequestHeader("Accept", "text/xml") && Это вроде не обязательно
* следующий заголовок обязательный, т.к. задает способ организации данных в запросе
oHttp.setRequestHeader( "Content-Type", [multipart/form-data; boundary=] + m.lcBoundary )
* длина тела - на всякий случай. В некоторых примерах ее не устанавливали, но, возможно, это
* скрыто внутри тех средств, которые использовали авторы
oHttp.setRequestHeader( "Content-Length", len( m.lcContent ) )
oHttp.send( m.lcContent )
if oHttp.status<>200
* здесь обрабатываем ошибку, например, формируем исключение
error [Status: ] + transform( oHttp.status ) + [ ] + oHttp.StatusText
endif
* Отправка успешно завершена, сохраняем ответ сервера
lcReturnData = oHttp.ResponseBody()

Олег, Игорь, спасибо за обсуждение - не только помогли ошибку найти, кое-что я себе на будущее отложил.
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
matod

Сообщений: 3062
Откуда: Иркутск
Дата регистрации: 31.10.2001
of63
А текст файла XML содержит символы CR, LF ?

Да CRLF как разделитель строк. Видимо, если правильно указать Content-type в подзаголовке, то это допускается.
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
Или может между подстроками m.lcBoundary допустимы вообще любые символы (кроме самой комбинации m.lcBoundary).
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Терминатором блока является исключительно двойной минус с соответствующим boundary с начала строки (т.е. как раз CRLF+"--blablabla") - если таковых последовательностей символов в отправляемых данных не будет, то можно обойтись и без кодирования. CRLF "внутри" блока не только допустимы, но и для некоторых случаев просто обязательны Пустая строка после заголовков тоже обязательна - она исключается из "данных".
0h - т.е. перевод из "строки букв" в "массив байт" влияет не на сам активикс или то чего по сети пойдёт, а исключительно на интерфейс взаимодействия фокса с активиксом. При том может оказаться недостаточным просто перевода в блоб - может потребоваться функция CREATEBINARY() которая ещё какие-то хитрые флаги где-то внутри фокса устанавливает, влияющие на то как же фоксовая строка будет передана активиксу (в какой тип VT_* она будет преобразована/помещена).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
PavlikPavlikovich

Сообщений: 176
Дата регистрации: 21.07.2010

Здравствуйте!
Подскажите. А если HTTP сервер недоступен? Как обыграть данную ошибку?
Ratings: 0 negative/0 positive
Re: XmlHttp: отправить xml-файл
of63
Автор

Сообщений: 25406
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
таймаутом
доб. Хотя там, если отказываешься от встроенного "асинхронного" механизма, вроде он отпускает юзера после N секунд... не пользуйся "синхронным" механизмом вызова, он не нужен.
() Термины "синхрон/асинхрон" в русском изложении совершенно не отражают сути... К счастью ими и не пользуются, но спрашивают об этом...



Исправлено 2 раз(а). Последнее : of63, 14.05.24 23:10
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 17 (Гостей: 17)

© 2000-2024 Fox Club 
Яндекс.Метрика