Простой пример передачи данных между клиентом и сервером через HTTP-сервисы




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

25 Comments

  1. BigB

    (0) В двух файлах, для скачивания есть ещё, что нибудь кроме текстов, что вы выложили в описании?

    Reply
  2. kristart

    (1) BigB, только описанное в статье, запрос — ответ.

    Reply
  3. Tasselhof

    Только вместо — Соединение.Записать(HTTPЗапрос, Ответ);

    надо — Соединение.Получить(HTTPЗапрос, Ответ);

    Reply
  4. kristart

    (3) Tasselhof, согласен, так тоже выглядит хорошо. Но в данном примере, HTTP-метод = любой, поэтому разницы нет.

    Reply
  5. FireFox_Manager

    Плюсанул за то, что автор кратко и доступно изложил суть вопроса и привел работающий код.

    Reply
  6. Andry.Boris

    Автору +

    Скачал, попробовал по вашей схеме, работает,

    проверил и с мобильного телефона, тоже все отлично отрабатывает.

    Главное коротко и понятно.

    Reply
  7. bubus

    А как передать файл HTTP сервису в 1с?

    Reply
  8. kristart

    (7) bubus, Можно сериализовать данные и передать сериализованную строку (если ее еще и сжать, поместив в хранилище — пакет меньше будет). Про сериализацию почитать можно например тут или просто в гугл вбить, примеров много.

    Reply
  9. bubus

    (8) спс уже сделал. Но вот про сжатие не знал. Отправляю в xml-двоичные-base64, а в http сервисе в обратном порядке, из мобильного приложения. Единственное что не нашел примера с методом PUT для HTTP сервиса. И еще вопрос, вы не знаете какой размер максимально можно приатачить к запросу? Понятно отрубит по таймауту, но есть ли ограничения по размерам? Где то читал про передаче через HTTP соединение из моб. приложения максимум 80кб. Типо баг что ли такой вроде был.

    Reply
  10. kristart

    (9) bubus, у меня небыло проблем с размерами файлов. В основном передавал файлы размером до 100мб — было все ок.

    Reply
  11. malikov_pro

    Автор немного «срезал», и при Соединение.Получить серверная сторона работать будет некорректно

    при POST тело сообщения передается и конструкция УстановитьТелоИзСтроки(ПараметрЗапроса) -> Запрос.ПолучитьТелоКакСтроку() работает

    при GET только через параметры запроса, пример: HTTPЗапрос( ИмяОпубликованнойБазы+ «/?geocode=1»); и принимаем на стороне сервера в Запрос.ПараметрыЗапроса

    Reply
  12. kristart

    (11) malikov_pro, хороший коммент, как Вы правильно указали я «срезал» все уточнения и много теории. Дело в том, что я хотел показать самый минимум. Все как для новичка, которому просто надо запустить сервис, а разбираться со временем.

    Кстати, некоторые параметры я сериализовал и передавал в заголовках (как строку). Еще один вариант прицепить данные.

    Reply
  13. alians-us

    у меня есть код

    &НаСервере
    
    Функция POSTЗапрос()
    ПараметрЗапроса=истина;
    Попытка
    //подключение к этому ПК
    Соединение = Новый HTTPСоединение(«127.0.0.1»);
    Исключение
    Возврат «Ошибка при создании HTTP соединения.»;
    КонецПопытки;
    
    HTTPЗапрос = Новый HTTPЗапрос(«Opoveshatel_android/hs/Exchange/query»);
    HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрЗапроса);
    
    Ответ = «»;
    
    Попытка
    HTTPОтвет =     Соединение.Получить(HTTPЗапрос, Ответ);
    Ответ = HTTPОтвет.ПолучитьТелоКакСтроку();
    Исключение
    Возврат «Ошибка при соединении.»;
    КонецПопытки;
    
    Возврат Ответ;
    КонецФункции
    
    &Наклиенте
    Процедура Команда1(Команда)
    
    Сообщить(POSTЗапрос());
    КонецПроцедуры
    

    Показать

    после нажатия на кнопку просто сообщает пустой ответ

    Reply
  14. kristart

    (13) alians-us, я думаю дело в том, что параметр не передается и дело именно в этом

    Вы используете метод GET

    Cоединение.Получить(HTTPЗапрос, Ответ);

    соответственно тело не передается

    HTTPЗапрос.УстановитьТелоИзСтроки(ПараметрЗапроса);

    нужно устанавливать параметры как писал malikov_pro:

    «при GET только через параметры запроса, пример:

    HTTPЗапрос( ИмяОпубликованнойБазы+ «/?geocode=1»);

    и принимаем на стороне сервера в Запрос.ПараметрыЗапроса

    либо заголовками, их можно установить вторым параметром при создании HTTPзапроса

    либо используйте метод PUT, как и описано в статье

    Cоединение.Записать(HTTPЗапрос, Ответ);
    Reply
  15. Region102

    (8) мне интересно как вы поместите ХранилищеЗначения в тело http запроса.

    Reply
  16. kristart

    (15) Region102, все проще чем кажется.

    Сериализуем необходимые данные и на выходе получаем строку которую и помещаем в тело.

    На принимающей стороне десериализуем и вуалая! — вот они наши данные.

    Соответственно передать можно все что сериализуется (посмотреть это свойство объекта можно в синтаксис помощнике).

    ХранилищеЗначения — сериализуется, то есть его так же можно передать в теле.

    Reply
  17. sml

    Доброго дня, плюсанул за краткость и содержание, а также за комменты.

    Есть вопрос: Если передавать данные через Заголовок (2й параметр запроса), то как его разбирать на стороне сервера?

    Reply
  18. kristart

    (17) sml, и Вам доброго дня.

    заголовки это обычное соответствие.

    На стороне сервера предположим есть принимающая функция

    Функция RegistrationChek(Запрос)

    Вот что мы получим

    Я передал два параметра поместив их в заголовки:

    Соответствие = Новый Соответствие();
    Соответствие.Вставить(«ТипОбращения», ПараметрыПодключения.ТипОбращения);
    Соответствие.Вставить(«ПакетОбмена»,ПараметрыПодключения.ИсходящееСообщение);
    HTTPЗапрос = Новый HTTPЗапрос(ПараметрыПодключения.РесурсНаСервере+ «/?geocode=1»,Соответствие);

    И вот они на сервере

    А потом уже обращаемся к своим параметра как хотим,

    можно так

    Запрос.Заголовки[«ПакетОбмена»]

    или так

    Запрос.Заголовки.Получить(«ПакетОбмена»)

    в общем как удобно.

    Reply
  19. serko8547

    Ув. Автор, прошу Вас поделиться опытом:

    Я начал использовать http-сервисами недавно, и напоролся на такую проблему: Если в 1с завести пользователя — то мне ответ на любой Get или post запрос приходит Ошибка 401 (не авторизован). Как быть?

    Был бы Вам очень благодарен, если Вы подскажете, как решить сию проблему )

    авторизация в смысле запросом логина-пароли — программно, т.е. я не ручками ввожу логин-пароль, а как мне авторизоваться программно.

    Reply
  20. kristart

    (19) serko8547, Приветствую.

    Если нет пользователя, авторизационные данные в HTTP-соединении указывать не обязательно; даже в базу пускает не запрашивая логин (без авторизации).

    Как только Вы завели пользователя — авторизация обязательна и при попытке войти в базу система будет запрашивать логин и пароль.

    В общем, Вам нужно указать логин и пароль при создании HTTP-соединения, примерно вот так:

    Попытка
    Соединение = Новый HTTPСоединение(ПараметрыПодключения.АдресСервера,
    ,ПараметрыПодключения.Пользователь,ПараметрыПодключения.Пароль,,Таймаут);
    Исключение
    //Сообщить(«Не удалось установить соединение с сервером»
    //+ Символы.ПС + ИнформацияОбОшибке().Описание, СтатусСообщения.Важное);
    ВызватьИсключение;
    КонецПопытки;
    Reply
  21. smirnovserg.s@gmail.com

    (14) извиняюсь, а в чем смысл указывать второй параметр Ответ в строке:

    HTTPОтвет =     Соединение.Записать(HTTPЗапрос, Ответ);

    сервис работает и без него.

    Reply
  22. kristart

    (21) smirnovserg.s@gmail.com, спасибо за замечание, Вы правы, этот параметр не нужен для метода Записать (в статье исправил).

    Его можно указывать используя метод Получить (GET-запрос) и тогда в ответ получим, как пишет синтаксис помощник — «Имя файла, в который помещаются данные полученного ресурса». В общем мы и так получаем все из HTTPОтвета, так что использование второго параметра, в нашем случае, считаю неоправданным.

    Reply
  23. tindir

    Еще одна статья наколенного кода.

    Reply
  24. kristart

    (23) tindir, Доброго времени суток. Поясните пожалуйста что Вы имеете ввиду «наколенного кода»? Возможно стоит что-нибудь исправить или переписать?

    Reply
  25. TOSUNIK

    Super !!!

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *