Параметры get запроса в заголовок из 1с. Отработка перенаправления http запроса

Ну вот вам пример обработки XML-файла с контрагентами на стороне сервера:

Код VBS
require_once dirname(__FILE__) . "/../inc/initf.php" ;
class Onec_Import_Customers {
private static $instance ;
function __construct(){

Self::$instance = $this ;

Public function process () {
$rawHttp = file_get_contents("php://input") ;
$xml_raw = str_replace("xml=", "", $rawHttp) ;

If ($_SESSION["loggedin"] || true){
file_put_contents("log/onec_import_customers_" .time(). ".log", "REQUEST " . print_r($_REQUEST, 1) . " SERVER " . print_r($_SERVER,1). " FILES " . print_r($_FILES,1) . $xml_raw) ;
file_put_contents("log/onec_import_customers_last.log", "REQUEST " . print_r($_REQUEST, 1) . " SERVER " . print_r($_SERVER,1). " FILES " . print_r($_FILES,1) . $xml_raw) ;

//$xml = stripslashes($_POST["xml"]);
$xml = stripslashes($xml_raw);
if(!$xml) {
$xml = $xml_raw ;
//die ("no XML data (post key "xml")") ;
}
if ($this->setCustomers($xml)) {
die ("OK");
} else {
die ("FAIL") ;
}
} else {
die ();
}
}

Private function setCustomers($xml){
$db = db::getInstance() ;

$sxml = simplexml_load_string($xml) ;

$customers = $sxml->{"Договор"} ? $sxml->{"Договор"} : self::err("Invalid file format. Customers.") ;

$final = array () ;
$k = 0 ;

$allCustomers = array () ;

Foreach ($customers as $cust) {
$password = base::generatePassword(6,1) ;

$arr ["password"] = $password ;

$arr ["email"] = (array)$cust->{"Почта"} ;//? (array)$cust->{"Почта"} : self::err("Invalid file format. Customer no:" . $k . ". Invalid email") ;
$arr ["email"] = $arr ["email"] ? $arr ["email"] : "";//: self::err("Invalid file format. customer no:" . $k . ". Invalid email") ;

$arr ["app_name"] = (array)$cust->{"Наименование"} ;//? (array)$cust->{"Наименование"} : self::err("Invalid file format. Customer no:" . $k . ". Invalid name") ;
$arr ["app_name"] = $arr ["app_name"] ? $arr ["app_name"] : "";//self::err("Invalid file format. customer no:" . $k . ". Invalid name") ;

$arr ["clientid"] = (array)$cust->{"Номер"} ? (array)$cust->{"Номер"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid clientid") ;
$arr ["clientid"] = $arr ["clientid"] ? $arr ["clientid"] : self::err("Invalid file format. Customers no:" . $k . ". Invalid clientid") ;

$arr ["date"] = (array)$cust->{"Дата"} ? (array)$cust->{"Дата"} : self::err("Invalid file format. Customers no:" . $k. ". Invalid date") ;
$arr ["date"] = $arr ["date"] ? $arr ["date"] : self::err("Invalid file format. Customers no:" . $k . ". Invalid date") ;

$arr ["date"] = explode(".",$arr ["date"]);
krsort($arr ["date"]);
$arr ["date"] = implode("-",$arr ["date"]) . " 00:00:00" ;

$arr ["phone_home"] = (array)$cust->{"Телефон"} ;//? (array)$cust->{"Телефон"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid phone") ;
$arr ["phone_home"] = $arr ["phone_home"] ? $arr ["phone_home"] : "";//self::err("Invalid file format. Customers no:" . $k . ". Invalid phone") ;

$arr ["district"] = (array)$cust->{"Район"} ;//? (array)$cust->{"Район"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid district") ;
$arr ["district"] = $arr ["district"] ? $arr ["district"] : "";//self::err("Invalid file format. Customers no:" . $k . ". Invalid district") ;

$arr ["street"] = (array)$cust->{"Улица"} ;//? (array)$cust->{"Улица"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid street") ;
$arr ["street"] = $arr ["street"] ? $arr ["street"] : "";//self::err("Invalid file format. Customers no:" . $k . ". Invalid street") ;

$arr ["building"] = (array)$cust->{"Дом"} ;//? (array)$cust->{"Дом"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid building") ;
$arr ["building"] = $arr ["building"] ? $arr ["building"] : "" ;//self::err("Invalid file format. Customers no:" . $k . ". Invalid building") ;

$arr ["apartament"] = (array)$cust->{"Квартира"} ;//? (array)$cust->{"Квартира"} : self::err("Invalid file format. Customers no:" . $k . ". Invalid apartament") ;
$arr ["apartament"] = $arr ["apartament"] ? $arr ["apartament"] : "";// self::err("Invalid file format. Customers no:" . $k . ". Invalid apartament") ;

$allCustomers [$arr ["clientid"]]= array("password"=>$password, "email"=>$arr ["email"]) ;

$final = $arr ;
++$k ;
}

Return $this->buildCustomers($final) ;
/*
if($this->buildCustomers($final)) {
foreach ($allCustomers as $clientid=>$data) {
self::sendPasswordToMail($data["email"], $clientid, $data["password"]) ;
}
}*/

Private static function sendPasswordToMail($email, $client_id, $password) {
$db = db::getInstance() ;
$config = config_model::getInstance() ;
$lng = Request::$currentLang["id"] ;
$email_text = $db->getRow("s1_text", "*", "`alias`="registration_ok" AND `lng_id`="{$lng}"") ;
$body = str_replace("%password%", $password, $email_text["content"]) ;
$body = str_replace("%client_id%", $client_id, $body) ;
base::mailSend($body, $email_text["title"] . " - " . $config->defaultTitle("site.ru") , $email, $app["app_name"], $config->site_admin_mail("[email protected]"), $config->from_name("site")) ;

Private function buildCustomers ($data) {

$db = db::getInstance() ;

$qry = "I_nsert INTO s1_customer (`active`,`password`,`app_name`,`email`,`date`,`clientid`,`phone_home`,`street`, `district`, `building`, `apartament`) VALUES " ;
foreach ($data as $rows){
$queryArr = "(
"0"
,MD5("{$rows["password">")
,"{$db->escape($rows["app_name"])}"
,"{$db->escape($rows["email"])}"
,"{$db->escape($rows["date"])}"
,"{$db->escape($rows["clientid"])}"
,"{$db->escape($rows["phone_home"])}"
,"{$db->escape($rows["street"])}"
,"{$db->escape($rows["district"])}"
,"{$db->escape($rows["building"])}"
,"{$db->escape($rows["apartament"])}"
)" ;
}
$qry .= implode(",", $queryArr) ;
$qry .= " ON DUPLICATE KEY UPDATE
`app_name` = VALUES(app_name)
,`date` = VALUES(date)
,`email` = VALUES(email)
,`phone_home` = VALUES(phone_home)
,`street` = VALUES(street)
,`district` = VALUES(district)
,`building` = VALUES(building)
,`apartament` = VALUES(apartament)
" ;
return $db->query($qry) ;
}

Public static function getInstance(){
if (!self::$instance)
{
new self() ;
}
return self::$instance ;

Private static function err($msg) {
throw new ImportException($msg) ;
}

Class ImportException extends Exception {

Function __construct ($msg) {
die ("Error: " . $msg) ;

Начиная со второй версии 8 платформы, у пользователей и разработчиков появилась возможность использования непосредственно в 1С http запрос. При этом программа поддерживает два типа запросов:

  • POST запросы;
  • GET запросы.

Таким образом, был создан достаточно удобный инструмент для обмена данными и взаимодействия с веб сервисами и службами, работающими через http.

GET запрос

Безусловно, простейшие примеры использования запросов гораздо лучше иллюстрируют их возможности, чем много строчек описания. Поэтому попробуем:

  1. Получим тело главной страницы нашего сайта;
  2. Отработаем перенаправление запроса;
  3. Заберем картинку с сайта.

Получение тела сайта

Начнем с простого. На Рис..

Результатом выполнения этого участка кода является достаточно большой текст, конечный участок которого отображен на Рис.2.

Рис.2

В первой строке кода мы создаем объект соединения с http ресурсом. Объект может содержать следующие свойства:

  • Сервер — строка подключения, содержащая адрес сервера;
  • Порт – содержит число, указывающее на порт сервера, по умолчанию, в зависимости от типа подключения, можно указать 80 для незащищенных соединений или 443 для защищенных SSL.
  • Имя пользователя – указывается, если необходима авторизация на сервере;
  • Пароль – пароль пользователя на указанном ресурсе;
  • Прокси – может содержать объект типа ИнтернетПрокси, указывается, когда для связи с сервером используется прокси;
  • ЗащищенноеСоединение – по умолчанию имеет значение ЛОЖЬ, переключение в ИСТИНА указывает на использование https протокола.

Кроме этого, у объекта HTTPСоединение существуют свои методы, обращение к которым позволяет более полно описать алгоритм выполнения обработчика:

  • ВызватьHTTPметод – содержит два обязательных параметра HTTPметод и HTTPзапрос, поддерживает возможность записи тела ответа в файл, указанный в третьем параметре;
  • Записать – с помощью PUT запроса отправляет данные на сервер;
  • Изменить – изменяет объект, обрабатывая PATCH запросы;
  • ОтправитьДляОбработки – метод указывающий на использование POST запроса, как и во всех предыдущих методах, обязательно должен содержать текст запроса, так же может передавать адрес файла ответа для записи данных;
  • Получить – о нем подробнее будет рассказано ниже;
  • ПолучитьЗаголовки – еще один метод, который будет использован в статье;
  • Удалить – фактически это запрос Delite, который удаляет переданный в запросе ресурс с сервера.

Второй строкой мы создаем запрос к выбранному сайту, текст нашего обращения содержит один слэш, а это значит, что мы хотим получить главную страницу. Если бы следом за слешем шло какое-либо выражение, например «page2» или «news» мы бы получили другую страницу.

Третья строка выполняет наш запрос к серверу.

В четвертой мы показываем результат.

Отработка перенаправления http запроса

Представим ситуацию, когда нам надо программно получить результат поиска через любую поисковую систему по ключу «Запросы в 1с». Участок кода, необходимый для обращения к GOOGLE указан на рис.3

Рис.3

Здесь помимо уже знакомых нам конструкций присутствуют Заголовки и КодСостояния. Разберемся с ними.

КодСостояния – стандартная величина, оговоренная в «Request for Comments» , может принимать следующие значения:

  1. Если все нормально вернется значение в диапазоне от 100 до 299;
  2. В случае перенаправления вернется код в диапазоне от 300 до 399, в нашем случае удачное постоянное перенаправление на ресурс определится кодом 301;
  3. При ошибках в запросе параметр примет значение от 400 до 499;
  4. Значение в области 500-599 указывает на проблемы с сервером.

У каждой страницы есть заголовок, в тексте которого можно выделить несколько параметров (Рис.4):

  1. Схему подключения (все, что идет до двух слешей «//»);
  2. Адресную строку соединения;
  3. Имя пользователя и пароль;
  4. Порт и хост для подключения.

Именно эту разбивку выполняет функция РазбитьСтрокуАдреса. Получив, таким образом, новый адрес, мы можем сохранить страницу на нашем компьютере и открыть её в браузере по умолчанию (процедура ПолучитьСтраницу).

Рис.5

Здесь нет новых функций и способов работы с запросами, мы фактически создаем текстовый документ из тела сайта и запускаем страницу в браузере.

Файл мы помещаем в корень диска D и называем test.

Забираем картинку с сайта

Возникает естественный вопрос: если нам не нужен весь сайт целиком, а необходимо получить только отдельные его элементы, можно ли это сделать и как? Да можно. Код программы, позволяющий это сделать, представлен на Рис.6

Рис.6

Как видно из рисунка, в теле запроса у нас представлен код элемента структуры сайта, который нам необходимо получить. Этой части не было в нашем предыдущем описании и на этом моменте нужно остановиться подробнее.

Мы использовали браузер Opera для доступа к сайту. В нем имеется один важный для нас инструмент, при щелчке правой кнопкой мыши на элементе, можно вызвать контекстное меню, один из пунктов которого «Посмотреть код элемента».

Именно благодаря ему, мы можем получить адрес, который будет использоваться в запросе Рис.7.

POST-запрос

В отличие от несложных Get запросов, POST http запросы имеют текстовое тело, которое может храниться как в обычном текстовом виде, так и в виде файлов с расширением xml, soap, json. В сети достаточно много инструментов для создания текстов запроса, которые позволяют отлаживать и мониторить исполнение тех или иных обращений.

В 1С для того, чтобы запустить запрос с определенным текстом, у объекта HTTPзапрос есть процедура УстановитьТелоИзСтроки.

При разработке процедуры отправки на сайт информации из 1С с версией платформы 8.3.9.2170 столкнулся с проблемой: разработчик сайта предоставил мне возможность записывать нужную информацию только при помощи HTTP запроса методом PUT.

Недолго думая, я набросал простенький код:

Соединение = Новый HTTPСоединение("www.mysite.ru"); Заголовки = Новый Соответствие; Заголовки["Content-Type"] = "application/x-www-form-urlencoded"; Запрос = Новый HTTPЗапрос("/api/order_items/93076?order_item=30", Заголовки); Соединение.Записать(Запрос);

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

Однако, как вы уже наверно поняли, ничего не произошло. После того как я убедился, что на сайте ошибок нет (путем отправки аналогичного запроса через плагин к Хрому), я запустил на своем локальном компьютере web-сервер и стал экспериментировать.

Сразу же выяснилась странная вещь: вышеприведенный код генерирует не PUT, а HEAD запрос!

В логах апача я увидел следующее:

127.0.0.1 - - "HEAD /api/order_items/93076?order_item=30 HTTP/1.1"

Я немного удивился (в руководстве ведь было черным по белому написано PUT), но не растерялся - можно ведь вызвать метод напрямую:

Соединение.ВызватьHTTPМетод("PUT",Запрос);

В логах то же самое:

127.0.0.1 - - "HEAD /api/order_items/93076?order_item=30 HTTP/1.1"

"Может быть я что-то не так делаю?" - задал я себе вопрос. Но в интернете и в мануалах не было никаких подсказок. Что ж, метод научного тыка еще никто не отменял. Для начала я попробовал сделать так:

Соединение.ВызватьHTTPМетод("фывфыв",Запрос);

В логах получил:

127.0.0.1 - - "?????? /api/order_items/93076?order_item=30 HTTP/1.1"

Любопытно, значит 1С заменяет конкретно метод PUT (чем же он 1С не угодил?).

После еще нескольких попыток я пришел к варианту:

Соединение.ВызватьHTTPМетод("PUT ",Запрос);

В логах получил:

127.0.0.1 - - "PUT /api/order_items/93076?order_item=30 HTTP/1.1"

И уже этот вариант отработал на сайте и все остались довольны.

Подсказал более корректное решение проблемы: необходимо задать тело запроса, любое, даже пустое. Например, сработает такой вариант:

Соединение = Новый HTTPСоединение("www.mysite.ru"); Заголовки = Новый Соответствие; Заголовки["Content-Type"] = "application/x-www-form-urlencoded"; Запрос = Новый HTTPЗапрос("/api/order_items/93076?order_item=30", Заголовки); Запрос.УстановитьТелоИзСтроки("", КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать); Соединение.Записать(Запрос);

И уже совсем правильно, наверное, передавать в теле запроса сами значения параметров.

Вывод следующий: PUT запрос без тела платформа 1С считает ошибочным и заменяет метод на HEAD.

Любопытно что POST запрос без тела 1С никак не отслеживает и не превращает в GET, проверял ради спортивного интереса.

Как сказал бы всем известный Вовочка из знаменитого анекдота: "Где логика?".

Надеюсь кому-то моя публикация сбережет несколько часов жизни в поисках ответа. =)))


Top