В первой части мы создали каркас для Get метода в расширении конфигурации. Во второй части мы поработали с OData и создали обработку, получающую через HTTP-Сервис используя метод Get данные сформированные при помощи СКД. В этой части поговорим про другие методы и БСП:Базовая функциональностьДлительные операции (при отсутствии БСП в Вашей конфигурации необходимо использовать фоновые задания).
В этой статье http-сервис будет в расширении конфигурации БСП 2.4.6.146 на платформе 8.3.10.2650 + IIS, а вот посылать запросы и получать ответы будем в БСП 3.0.1.231 на платформе 8.3.12.1595.
Начнем.
Какие методы можно добавить в HTTP-Сервис?
- GET — Получение данных.
- HEAD — Запрашивает ресурс так же, как и метод GET, но без тела ответа.
- PUT — Изменение данных.
- POST — Создания новой сущности на сервере.
- DELETE — Удаления данных.
- PATCH — Используется для частичного изменения ресурса.
- MERGE — Предназначен для дифференциального изменения данных. Аналогичен методу PATCH, но является расширением от Microsoft.
- OPTIONS — Предназначен для получения информации о способах работы с данным URI (доступные HTTP-методы, форматы представления и т.п.).
- TRACE — Возвращает полученный запрос так, что клиент может увидеть, какую информацию промежуточные серверы добавляют или изменяют в запросе.
- CONNECT — Преобразует соединение запроса в прозрачный TCP/IP-туннель, обычно чтобы содействовать установлению защищённого SSL-соединения через нешифрованный прокси.
- ANY (Любой) — Указывает, что может быть использован любой HTTP-метод из перечисления HTTPМетод.
- И команды для WebDAV(Web Distributed Authoring and Versioning) или просто DAV — набор расширений и дополнений к протоколу HTTP, поддерживающих совместную работу пользователей над редактированием файлов и управление файлами на удаленных веб-серверах. (Еще ссылка по WebDAV)
WebDAV расширяет HTTP следующими командами:
- PROPFIND — Получение свойств объекта на сервере в формате XML. Также можно получать структуру репозитория (дерево каталогов);
- PROPPATCH — Изменение свойств за одну транзакцию;
- MKCOL — Создать коллекцию объектов (каталог в случае доступа к файлам);
- COPY — Копирование из одного URI в другой;
- MOVE — Перемещение из одного URI в другой;
- LOCK — Поставить блокировку на объекте. WebDAV поддерживает эксклюзивные и общие (shared) блокировки;
- UNLOCK — Снять блокировку с ресурса.
Подробнее о методах можно почитать тут, тут и тут
Давайте обратим внимание на метод «ANY (Любой)» — Данный метод позволяет перехватывать любой метод и обрабатывать его.
В голову пришел небольшой эксперимент благодаря статье Отправка PUT запроса средствами 1С 8.3. Давайте создадим в нашем http-сервисе метод ANY и в цикле используя ВызватьHTTPМетод переберем методы (GET, HEAD, PUT, POST, DELETE, PATCH, MERGE, OPTIONS, TRACE, CONNECT), обращаясь с телом запроса и без. Будем возвращать информацию по запросу.
Для начала создадим Демо базу на основе БСП 2.4.6.146 (на платформе 8.3.10), добавим в нее расширении из предыдущей части, изменим Корневой URL на PAPI и добавим метод "Любой (ANY)".
Обратите внимание на заголовок "Content-Type" (Код обработчика PAPI_ALL). Данный заголовок содержит тип данных и служит для того чтобы можно было понять, что находится в запросе или ответе. Про возможные типы можно почитать тут. мы укажем "text/plain; charset=utf-8" — текстовые данные.
Функция PAPI_ALL(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
Результат = "";
//Запрос.HTTPМетод - Метод запроса.
Результат = Результат + Запрос.HTTPМетод + Символы.ПС;
//Запрос.БазовыйURL - Базовая часть URL-запроса. Включает имя сервиса.
Результат = Результат + Запрос.БазовыйURL + Символы.ПС;
//Запрос.ОтносительныйURL - Содержит относительную часть URL-адреса (относительно сервиса).
Результат = Результат + Запрос.ОтносительныйURL + Символы.ПС + Символы.ПС;
//Запрос.Заголовки - Содержит заголовки HTTP-запроса.
Результат = Результат + "Заголовки:" + Символы.ПС;
Для каждого Параметр Из Запрос.Заголовки Цикл
Результат = Результат + Параметр.Ключ + ":" + Параметр.Значение + Символы.ПС;
КонецЦикла;
//Запрос.ПараметрыURL - Содержит части URL-адреса, которые были параметризованы в шаблоне.
Результат = Результат + Символы.ПС + "Параметры URL:" + Символы.ПС;
Для каждого Параметр Из Запрос.ПараметрыURL Цикл
Результат = Результат + Параметр.Ключ + ":" + Параметр.Значение + Символы.ПС;
КонецЦикла;
//Запрос.ПараметрыЗапроса - Содержит параметры запроса (в строке URL-адреса параметры следуют после знака запроса).
Результат = Результат + Символы.ПС + "Параметры запроса:" + Символы.ПС;
Для каждого Параметр Из Запрос.ПараметрыЗапроса Цикл
Результат = Результат + Параметр.Ключ + ":" + Параметр.Значение + Символы.ПС;
КонецЦикла;
//Запрос.ПолучитьТелоКакСтроку() - Возвращает тело запроса в виде строки
ЗапросОтКлиента = Запрос.ПолучитьТелоКакСтроку();
Результат = Результат + Символы.ПС + "Тело запроса:" + Символы.ПС;
Результат = Результат + ЗапросОтКлиента + Символы.ПС;
//Ответ.УстановитьТелоИзСтроки() - Устанавливает тело ответа из строки.
Ответ.УстановитьТелоИзСтроки(Результат);
//Тип содержимого ответа https://ru.wikipedia.org/wiki/Список_MIME-типов
Ответ.Заголовки.Вставить("Content-Type","text/plain; charset=utf-8");
Возврат Ответ
КонецФункции
Шаблон у нас будет такой: /{ИмяМетода}
Теперь опубликуем:
Создадим, если ранее не создавали Демо базу на основе БСП 3.0.1.231 (на платформе 8.3.12), и создадим небольшую внешнюю обработку с одной командой ВсеМетоды и реквизитом Результат с типом строка.
&НаКлиенте
Процедура ВсеМетоды(Команда)
Результат = "";
ВсеМетодыНаСервере();
КонецПроцедуры
&НаСервере
Процедура ВсеМетодыНаСервере()
перПорт = 0;
перСервер = "127.0.0.1";
перПользователь = "Администратор";
перПароль = "";
Попытка
Если перПорт <= 0 Тогда
HTTPСоединение = Новый HTTPСоединение(перСервер,,перПользователь,перПароль);
Иначе
HTTPСоединение = Новый HTTPСоединение(перСервер,перПорт,перПользователь,перПароль);
КонецЕсли;
Исключение
Сообщить("Подключение не прошло по причине "+ОписаниеОшибки());
//Подчищаем соединение
HTTPСоединение = Неопределено;
Возврат;
КонецПопытки;
//Получаем массив методов которые будем использовать
МассивМетодов = МассивВсехМетодов();
Для Каждого элМассива из МассивМетодов Цикл
Результат = Результат + "Метод: [" + элМассива + "]++" + Символы.ПС;
//Параметры запроса (?parametr1=13¶metr2=test) передаю специально
//ПараметрURL ИмяМетода = HTTPМетоду
перРесурсНаСервере = "/DemoSSL2_4_6_146/hs/PAPI/"+элМассива+"?parametr1=13¶metr2=test";
Результат = Результат + "{Без тела запроса}" + Символы.ПС;
HTTPЗапрос = Новый HTTPЗапрос(перРесурсНаСервере);
//Тело отсутствует
HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод(элМассива,HTTPЗапрос);
//Получаем ответный текст или текст ошибки
Ответочка = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Результат = Результат + Ответочка + Символы.ПС + Символы.ПС;
Результат = Результат + "{С телом запроса}" + Символы.ПС;
//Тело присутствует
HTTPЗапрос.УстановитьТелоИзСтроки("Тело Запроса - просто текст");
HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод(элМассива,HTTPЗапрос);
//Получаем ответный текст или текст ошибки
Ответочка = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Результат = Результат + Ответочка + Символы.ПС;
Результат = Результат + "Метод: [" + элМассива + "]--" + Символы.ПС + Символы.ПС;
КонецЦикла;
//Подчищаем соединение
HTTPСоединение = Неопределено;
КонецПроцедуры
&НаСервереБезКонтекста
Функция МассивВсехМетодов()
МассивМетодов = новый Массив;
МассивМетодов.Добавить("GET");
МассивМетодов.Добавить("HEAD");
МассивМетодов.Добавить("PUT");
МассивМетодов.Добавить("POST");
МассивМетодов.Добавить("DELETE");
МассивМетодов.Добавить("PATCH");
МассивМетодов.Добавить("MERGE");
МассивМетодов.Добавить("OPTIONS");
МассивМетодов.Добавить("TRACE");
МассивМетодов.Добавить("CONNECT");
Возврат МассивМетодов;
КонецФункции
Проверяем результат:
#Software: Microsoft Internet Information Services 10.0
#Version: 1.0
#Date: 2024-08-22 10:14:43
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
- 2024-08-22 10:14:43 127.0.0.1 GET /DemoSSL2_4_6_146/hs/PAPI/GET parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 128
- 2024-08-22 10:14:43 127.0.0.1 GET /DemoSSL2_4_6_146/hs/PAPI/GET parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 118
- 2024-08-22 10:14:43 127.0.0.1 HEAD /DemoSSL2_4_6_146/hs/PAPI/HEAD parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 132
- 2024-08-22 10:14:43 127.0.0.1 HEAD /DemoSSL2_4_6_146/hs/PAPI/HEAD parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 119
- 2024-08-22 10:14:43 127.0.0.1 PUT /DemoSSL2_4_6_146/hs/PAPI/PUT parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 119
- 2024-08-22 10:14:43 127.0.0.1 PUT /DemoSSL2_4_6_146/hs/PAPI/PUT parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 125
- 2024-08-22 10:14:43 127.0.0.1 POST /DemoSSL2_4_6_146/hs/PAPI/POST parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 123
- 2024-08-22 10:14:43 127.0.0.1 POST /DemoSSL2_4_6_146/hs/PAPI/POST parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:43 127.0.0.1 DELETE /DemoSSL2_4_6_146/hs/PAPI/DELETE parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 119
- 2024-08-22 10:14:44 127.0.0.1 DELETE /DemoSSL2_4_6_146/hs/PAPI/DELETE parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:44 127.0.0.1 PATCH /DemoSSL2_4_6_146/hs/PAPI/PATCH parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:44 127.0.0.1 PATCH /DemoSSL2_4_6_146/hs/PAPI/PATCH parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:44 127.0.0.1 MERGE /DemoSSL2_4_6_146/hs/PAPI/MERGE parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 119
- 2024-08-22 10:14:44 127.0.0.1 MERGE /DemoSSL2_4_6_146/hs/PAPI/MERGE parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:44 127.0.0.1 OPTIONS /DemoSSL2_4_6_146/hs/PAPI/OPTIONS parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 122
- 2024-08-22 10:14:44 127.0.0.1 OPTIONS /DemoSSL2_4_6_146/hs/PAPI/OPTIONS parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 118
- 2024-08-22 10:14:44 127.0.0.1 TRACE /DemoSSL2_4_6_146/hs/PAPI/TRACE parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 119
- 2024-08-22 10:14:44 127.0.0.1 CONNECT /DemoSSL2_4_6_146/hs/PAPI/CONNECT parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 120
- 2024-08-22 10:14:45 127.0.0.1 CONNECT /DemoSSL2_4_6_146/hs/PAPI/CONNECT parametr1=13¶metr2=test 80 — 127.0.0.1 1C+Enterprise/8.3 — 200 0 0 121
Из логов видно, что все запросы кроме запроса с методом TRACE отработали и с телом запроса и без.
Результаты метода ANY:
Метод: [GET]++
{Без тела запроса}
GET
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/GET
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:GET
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
GET
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/GET
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:GET
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Метод: [GET]—
|
Метод отработал в обоих случаях одинаково, тело запроса не было передано. Так и заявлено в методе.
Метод: [HEAD]++
{Без тела запроса}
{С телом запроса}
Метод: [HEAD]—
|
Метод отработал в обоих случаях одинаково, тело ответа не было передано. Так и заявлено в методе.
Метод: [PUT]++
{Без тела запроса}
PUT
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/PUT
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:PUT
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
PUT
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/PUT
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Expect:100-continue
Параметры URL:
ИмяМетода:PUT
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [PUT]—
|
Метод отработал в обоих случаях, поймать ситуацию из статьи Отправка PUT запроса средствами 1С 8.3 когда PUT 1С-Кой менялся на HEAD не удалось. Наверное была ошибка платформы которую исправили.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Expect — указывает ожидания, которые должен выполнить сервер, чтобы правильно обработать запрос.
Единственным ожиданием, определенным в спецификации, является "Expect: 100-continue", на который сервер должен ответить:
100
если информации, содержащейся в заголовке, достаточно, чтобы вызвать немедленный успех,
417
(Expectation Failed) если он не может удовлетворить ожидания; или любой другой статус 4xx..
Например, сервер может отклонить запрос, если его Content-Length
слишком большой.
Метод: [POST]++
{Без тела запроса}
POST
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/POST
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:POST
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
POST
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/POST
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:POST
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [POST]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Метод: [DELETE]++
{Без тела запроса}
DELETE
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/DELETE
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:DELETE
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
DELETE
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/DELETE
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:DELETE
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [DELETE]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Метод: [PATCH]++
{Без тела запроса}
PATCH
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/PATCH
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:PATCH
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
PATCH
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/PATCH
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:PATCH
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [PATCH]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Метод: [MERGE]++
{Без тела запроса}
MERGE
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/MERGE
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:MERGE
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
MERGE
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/MERGE
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:MERGE
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [MERGE]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Метод: [OPTIONS]++
{Без тела запроса}
OPTIONS
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/OPTIONS
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:OPTIONS
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
OPTIONS
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/OPTIONS
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:OPTIONS
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [OPTIONS]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Метод: [TRACE]++
{Без тела запроса}
TRACE
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/TRACE
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:TRACE
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request</h2>
<hr><p>HTTP Error 400. The request is badly formed.</p>
</BODY></HTML>
Метод: [TRACE]—
|
Метод отработал только без тела запроса. С телом запроса вернул ошибку с кодом ответа 400 — "Сервер обнаружил в запросе клиента синтаксическую ошибку".
Метод: [CONNECT]++
{Без тела запроса}
CONNECT
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/CONNECT
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:CONNECT
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
{С телом запроса}
CONNECT
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/CONNECT
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:49
Параметры URL:
ИмяМетода:CONNECT
Параметры запроса:
parametr1:13
parametr2:test
Тело запроса:
Тело Запроса — просто текст
Метод: [CONNECT]—
|
Метод отработал в обоих случаях.
Content-Length — указывает размер тела объекта в десятичном числе октетов (байтов)
Как мы видим, часть методов имеет схожее назначение за некоторыми отличиями. Большинство API предоставляют клиентам только методы GET, POST и реже PUT.
Давайте сравним GET и POST:
Параметры сравнения
|
GET
|
POST
|
Описание
|
Запросы можно кэшировать
|
Да
|
Нет
|
Могут быть кэшированы браузером.
|
Запросы сохраняются в истории браузера
|
Да
|
Нет
|
Следовательно, если логин и пароль указан в параметрах запроса, злоумышленник без труда их узнает.
|
Запросы можно добавить в закладки, идемпотентность |
Да
|
Нет
|
При многочисленном повторном обращении вернут один и тот же результат, кроме случаев когда информация устарела.
|
Безопасные запросы
|
Да
|
Нет
|
Спецификация HTTP 1.1 вводит два понятия: безопасный и небезопасный запрос, или если быть более точным, метод.
Безопасные — это методы, которые могут лишь запросить информацию. Они не могут изменить запрашиваемый ресурс, не могут привести к нежелательным результатам для пользователя, других лиц или сервера.
|
Содержит тело запроса
|
Нет
|
Да
|
Для POST параметры передаются в теле.
|
Можно передавать файлы
|
Нет
|
Да
|
|
Способен передать большие объемы данных
|
Нет
|
Да
|
|
Запрос ограничен длинной URL
|
Да
|
Нет
|
Несмотря на то, что RFC не описывает такой параметр, как длина URL, Internet Explorer упорно придерживается мнения, что максимальная длина URL не может превышать 2048 символов, это накладывает некоторые ограничения на использование GET.
|
Картинка ниже, утрировано, но очень наглядно показывает выбор между GET и POST:
Ссылки по сравнению GET с POST:
Давайте нарисуем сервис «Список заданий за период», раз уж мы используем демо базу БСП, то использовать будем Отчет.Задания и его СКД макет с вариантом настроек «СписокЗаданий». Будем подавать Начало и Конец периода и формировать ответ в формате JSON. Наша задача сделать так, чтобы можно было забирать данные как через GET, так и через POST.
Добавляем метод POST и пишем код его обработки.
//Обработка метода POST
Функция PAPI_POST(Запрос)
СтруктураВхПараметров = Новый Структура;
//Получаем имя метода
перИмяМетода = Запрос.ПараметрыURL["ИмяМетода"];
//Помещаем имя метода в структуру
СтруктураВхПараметров.Вставить("ИмяМетода",перИмяМетода);
//Метод запроса
СтруктураВхПараметров.Вставить("МетодЗапроса",Запрос.HTTPМетод);
//Тело запроса
ЗапросОтКлиента = Запрос.ПолучитьТелоКакСтроку();
СтруктураВхПараметров.Вставить("ТелоЗапроса",ЗапросОтКлиента);
//Передаем входящие параметры (Модуль для метода POST)
СтруктураОтвет = PAPI_ОбработкаМетодовPOST.PAPI_ОбработкаМетодовPOST(СтруктураВхПараметров);
Ответ = Новый HTTPСервисОтвет(СтруктураОтвет.КодОтвета);
Если СтруктураОтвет.Отработало Тогда
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ДанныеОтвета,КодировкаТекста.UTF8);
Иначе
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ТекстОшибки,КодировкаТекста.UTF8);
КонецЕсли;
//В зависимости от формата меняем "Content-Type"
PAPI_ОбщиеПроцедурыИФункции.ВозвращаемНужныйФорматОтвета(Ответ,СтруктураОтвет);
Возврат Ответ;
КонецФункции
Правим, обработчик метода GET, добавляем Метод запроса.
//Обработка метода GET
Функция PAPI_GET(Запрос)
СтруктураВхПараметров = Новый Структура;
//Получаем имя метода
перИмяМетода = Запрос.ПараметрыURL["ИмяМетода"];
//Помещаем имя метода в структуру
СтруктураВхПараметров.Вставить("ИмяМетода",перИмяМетода);
//Метод запроса
СтруктураВхПараметров.Вставить("МетодЗапроса",Запрос.HTTPМетод);
//Забираем параметры из запроса
ВхПараметрыЗапроса = Новый Соответствие;
Для каждого Параметр Из Запрос.ПараметрыЗапроса Цикл
ВхПараметрыЗапроса.Вставить(Параметр.Ключ,Параметр.Значение);
КонецЦикла;
СтруктураВхПараметров.Вставить("ПараметрыЗапроса",ВхПараметрыЗапроса);
//Передаем входящие параметры (Модуль для метода GET)
СтруктураОтвет = PAPI_ОбработкаМетодовGET.PAPI_ОбработкаМетодовGET(СтруктураВхПараметров);
//Создаем ответ с кодом состояния
Ответ = Новый HTTPСервисОтвет(СтруктураОтвет.КодОтвета);
Если СтруктураОтвет.Отработало Тогда
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ДанныеОтвета,КодировкаТекста.UTF8);
Иначе
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ТекстОшибки,КодировкаТекста.UTF8);
КонецЕсли;
//В зависимости от формата меняем "Content-Type"
PAPI_ОбщиеПроцедурыИФункции.ВозвращаемНужныйФорматОтвета(Ответ,СтруктураОтвет);
Возврат Ответ;
КонецФункции
В модуль PAPI_ОбработкаУниверсальныхМетодов добавляем код для нового метода.
Код модуля PAPI_ОбработкаУниверсальныхМетодов
#Область Методы
//Пример из Части 2. Метод написан под ERP, будет работать в УТ и КА
Процедура ПолучитьОстаткиИДоступностьТоваровПоСкладу(СтруктураОтвет,СтруктураВхПараметров) Экспорт
//ПраметрыЗапроса - number
Склад_Key = СтруктураВхПараметров.ПараметрыЗапроса.Получить("Ref_Key");
Если не Склад_Key = Неопределено Тогда
//Получаем Склад
Попытка
Склад = XMLЗначение(Тип("СправочникСсылка.Склады"), Склад_Key);
Исключение
Склад = Неопределено;
КонецПопытки;
Если ЗначениеЗаполнено(Склад) Тогда
//Получаем схему компоновки и вариант отчета
//СхемаКомпоновки = Отчеты.ОстаткиИДоступностьТоваров.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
//ВариантНастроеки = СхемаКомпоновки.ВариантыНастроек.Найти("ОстаткиИДоступностьТоваров");
СхемаКомпоновки = Отчеты.PAPI_ХранилищеМакетов.ПолучитьМакет("СКД_ОстаткиИДоступностьТоваров_HTTP");
ВариантНастроеки = СхемаКомпоновки.ВариантыНастроек.Найти("ОстаткиИДоступностьТоваровНеТаблица");
НастройкиКомпоновки = ВариантНастроеки.Настройки;
//Взято из модуля отчета.ОстаткиИДоступностьТоваров "ПриКомпоновкеРезультата"++
ТекстЗапроса = СхемаКомпоновки.НаборыДанных.Основной.Запрос;
ТекстЗапроса = СтрЗаменить(
ТекстЗапроса,
"&ТекстЗапросаВесНоменклатуры",
Справочники.УпаковкиЕдиницыИзмерения.ТекстЗапросаВесУпаковки("Набор.Номенклатура.ЕдиницаИзмерения", "Набор.Номенклатура"));
ТекстЗапроса = СтрЗаменить(
ТекстЗапроса,
"&ТекстЗапросаОбъемНоменклатуры",
Справочники.УпаковкиЕдиницыИзмерения.ТекстЗапросаОбъемУпаковки("Набор.Номенклатура.ЕдиницаИзмерения", "Набор.Номенклатура"));
СхемаКомпоновки.НаборыДанных.Основной.Запрос = ТекстЗапроса;
//Взято из модуля отчета.ОстаткиИДоступностьТоваров "ПриКомпоновкеРезультата"--
ПрараметрОтбора = НастройкиКомпоновки.Отбор.Элементы;
СкладПолеКомпоновки = Новый ПолеКомпоновкиДанных("Склад");
Для Каждого СтрокаОтбора из ПрараметрОтбора Цикл
Если СтрокаОтбора.ЛевоеЗначение = СкладПолеКомпоновки Тогда
СтрокаОтбора.Использование = Истина;
СтрокаОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
СтрокаОтбора.ПравоеЗначение = Склад;
КонецЕсли;
КонецЦикла;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, НастройкиКомпоновки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки,,,Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ДеревоЗначений = Новый ДеревоЗначений;
ПроцессорВывода.УстановитьОбъект(ДеревоЗначений);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
//Обходим дерево и формируем РезультатОтвет
РезультатОтвет = Новый Массив;
Для Каждого ВерхнийУровеньДерева Из ДеревоЗначений.Строки Цикл
Для Каждого ПодчиненныйУровеньДерева Из ВерхнийУровеньДерева.Строки Цикл
Если ЗначениеЗаполнено(ПодчиненныйУровеньДерева.Номенклатура)
И ТипЗнч(ПодчиненныйУровеньДерева.Номенклатура) = Тип("СправочникСсылка.Номенклатура")
Тогда
РезультатОтвет.Добавить(Новый Структура("ИмяЗначение,Артикул,Guid,ДоступноСейчас,Остаток",
ПодчиненныйУровеньДерева.Номенклатура.Наименование +
?(ЗначениеЗаполнено(ПодчиненныйУровеньДерева.Характеристика),
","+Строка(ПодчиненныйУровеньДерева.Характеристика),""),
ПодчиненныйУровеньДерева.НоменклатураАртикул,
XMLСтрока(ПодчиненныйУровеньДерева.Номенклатура),
ПодчиненныйУровеньДерева.ДоступноСейчас,
ПодчиненныйУровеньДерева.ВНаличии));
КонецЕсли;
КонецЦикла;
КонецЦикла;
//РезультатОтвет в JSON
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " " , Истина, ЭкранированиеСимволовJSON.Нет, Ложь, Ложь, Ложь, Ложь);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ПроверятьСтруктуру = Истина;
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(ЗаписьJSON, РезультатОтвет);
Результат = ЗаписьJSON.Закрыть();
СтруктураОтвет.ДанныеОтвета = Результат;
СтруктураОтвет.Вставить("ФорматОтвета",PAPI_ОбщиеПроцедурыИФункции.ФорматОтветаJSON());
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Не заполнен или неверно заполнен параметр ""Ref_Key""",ложь,"");
КонецЕсли;
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Отсутствует параметр ""Ref_Key""",ложь,"");
КонецЕсли;
КонецПроцедуры
//Пример из Части 3. Метод написан под Демо БСП, следовательно будет работать в конфигурациях с БСП
Процедура ПолучитьСписокЗаданийЗаПериод(СтруктураОтвет,СтруктураВхПараметров) Экспорт
//Проверяем входящие параметры
Если ВРег(СтруктураВхПараметров.МетодЗапроса) = ВРег("GET") Тогда
перНачалоПериода = СтруктураВхПараметров.ПараметрыЗапроса.Получить("StartPeriod");
НачалоПериода = ?(перНачалоПериода = Неопределено,
Неопределено,PAPI_ОбщиеПроцедурыИФункции.ПолучитьДатуВремяИзСтроки(перНачалоПериода));
перКонецПериода = СтруктураВхПараметров.ПараметрыЗапроса.Получить("EndPeriod");
КонецПериода = ?(перКонецПериода = Неопределено,
Неопределено,PAPI_ОбщиеПроцедурыИФункции.ПолучитьДатуВремяИзСтроки(перКонецПериода));
ИначеЕсли ВРег(СтруктураВхПараметров.МетодЗапроса) = ВРег("POST") Тогда
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтруктураВхПараметров.ТелоЗапроса);
Попытка
ТелоЗапросаJSON = ПрочитатьJSON(ЧтениеJSON);
Исключение
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,
400,"Не удалось получить данные!",Ложь,"");
КонецПопытки;
Если СтруктураОтвет.Отработало Тогда
Если ТипЗнч(ТелоЗапросаJSON) = Тип("Структура") Тогда
НачалоПериода = ?(ТелоЗапросаJSON.Свойство("StartPeriod"),
PAPI_ОбщиеПроцедурыИФункции.ПолучитьДатуВремяИзСтроки(ТелоЗапросаJSON.StartPeriod),Неопределено);
КонецПериода = ?(ТелоЗапросаJSON.Свойство("EndPeriod"),
PAPI_ОбщиеПроцедурыИФункции.ПолучитьДатуВремяИзСтроки(ТелоЗапросаJSON.EndPeriod),Неопределено);
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,
400,"Передана не структура!",Ложь,"");
КонецЕсли;
КонецЕсли;
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,
501,"Метод: "+СтруктураВхПараметров.МетодЗапроса+" не поддерживается!",Ложь,"");
КонецЕсли;
//Проверяем была ли ошибка
Если СтруктураОтвет.Отработало Тогда
//Получаем схему компоновки и вариант отчета
СхемаКомпоновки = Отчеты.Задания.ПолучитьМакет("Макет");
ВариантНастроеки = СхемаКомпоновки.ВариантыНастроек.Найти("СписокЗаданий");
НастройкиКомпоновки = ВариантНастроеки.Настройки;
//Взято из модуля отчета.Задания "ПриКомпоновкеРезультата"++
ИспользоватьДатуИВремяВСрокахЗадач = ПолучитьФункциональнуюОпцию("ИспользоватьДатуИВремяВСрокахЗадач");
СрокИсполнения = СхемаКомпоновки.НаборыДанных[0].Поля.Найти("СрокИсполнения");
СрокИсполнения.Оформление.УстановитьЗначениеПараметра("Формат", ?(ИспользоватьДатуИВремяВСрокахЗадач, "ДЛФ=DT", "ДЛФ=D"));
//Взято из модуля отчета.Задания "ПриКомпоновкеРезультата"--
Параметр = НастройкиКомпоновки.ПараметрыДанных.Элементы.Найти("Период");
Если Параметр <> Неопределено Тогда
Параметр.Использование = ?(ЗначениеЗаполнено(НачалоПериода)
ИЛИ ЗначениеЗаполнено(КонецПериода),Истина,Ложь);
ПериодВрем = Новый СтандартныйПериод;
Если ЗначениеЗаполнено(НачалоПериода) Тогда
ПериодВрем.ДатаНачала = НачалоДня(НачалоПериода);
КонецЕсли;
Если ЗначениеЗаполнено(КонецПериода) Тогда
ПериодВрем.ДатаОкончания = КонецДня(КонецПериода);
КонецЕсли;
Если Параметр.Использование Тогда
Параметр.Значение = ПериодВрем;
КонецЕсли;
КонецЕсли;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, НастройкиКомпоновки, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки,,,Истина);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗ = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
//Обходим ТЗ и формируем РезультатОтвет
РезультатОтвет = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Если ЗначениеЗаполнено(СтрокаТЗ.Ссылка)
И ТипЗнч(СтрокаТЗ.Ссылка) = Тип("БизнесПроцессСсылка.Задание")
Тогда
РезультатОтвет.Добавить(Новый Структура("Задание,Guid,Автор,Дата,Выполнено",
СтрокаТЗ.Ссылка.Наименование,
XMLСтрока(СтрокаТЗ.Ссылка),
СтрокаТЗ.Автор.Наименование,
СтрокаТЗ.Дата,
СтрокаТЗ.Выполнено));
КонецЕсли;
КонецЦикла;
//РезультатОтвет в JSON
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " " , Истина, ЭкранированиеСимволовJSON.Нет, Ложь, Ложь, Ложь, Ложь);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ПроверятьСтруктуру = Истина;
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(ЗаписьJSON, РезультатОтвет);
Результат = ЗаписьJSON.Закрыть();
СтруктураОтвет.ДанныеОтвета = Результат;
СтруктураОтвет.Вставить("ФорматОтвета",PAPI_ОбщиеПроцедурыИФункции.ФорматОтветаJSON());
КонецЕсли;
КонецПроцедуры
#КонецОбласти
В модуль PAPI_ОбщиеПроцедурыИФункции добавляем функцию ПолучитьДатуВремяИзСтроки — для преобразования данных в дату.
Код модуля PAPI_ОбщиеПроцедурыИФункции
//Заполняем структуру ответа
Процедура ЗаполнитьСтруктуруОтвета(СтруктураОтвет,КодОтвета,ТекстОшибки,Отработало,ДанныеОтвета) Экспорт
СтруктураОтвет.КодОтвета = КодОтвета;
СтруктураОтвет.ТекстОшибки = ТекстОшибки;
СтруктураОтвет.Отработало = Отработало;
СтруктураОтвет.ДанныеОтвета = ДанныеОтвета;
КонецПроцедуры
#Область РаботаСДанными
//Получаем дату из входных параметров
Функция ПолучитьДатуВремяИзСтроки(ДатаВремя) Экспорт
Результат = Неопределено;
ДатаВремя = СтрЗаменить(ДатаВремя, "-", "");
ДатаВремя = СтрЗаменить(ДатаВремя, ":", "");
Если СтрДлина(ДатаВремя) - Найти(ДатаВремя, " ") = 5 Тогда // время в формате Ч:ММ:СС
ДатаВремя = СтрЗаменить(ДатаВремя, " ", "0");
ИначеЕсли Найти(ДатаВремя, "T") > 0 Тогда //время в формате ГГГГММДДTЧЧММСС
ДатаВремя = СтрЗаменить(ДатаВремя, "T", "");
Иначе // время в формате ЧЧ:ММ:СС
ДатаВремя = СтрЗаменить(ДатаВремя, " ", "");
КонецЕсли;
ОписаниеТипа = Новый ОписаниеТипов("Дата");
Результат = ОписаниеТипа.ПривестиЗначение(ДатаВремя); // дата и время в виде "ГГГГММДДЧЧММСС"
Если Не ЗначениеЗаполнено(Результат) Тогда
Результат = ОписаниеТипа.ПривестиЗначение(Лев(ДатаВремя, 8)); // только дата "ГГГГММДД"
КонецЕсли;
Возврат Результат;
КонецФункции
#КонецОбласти
#Область ФорматыОтвета
Процедура ВозвращаемНужныйФорматОтвета(Ответ,СтруктураОтвет) Экспорт
Если СтруктураОтвет.Свойство("ФорматОтвета") Тогда
ФорматОтвета = СтруктураОтвет.ФорматОтвета;
Иначе
ФорматОтвета = "";
КонецЕсли;
Если ВРег(ФорматОтвета) = Врег(ФорматОтветаJSON()) Тогда
Ответ.Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Иначе
Ответ.Заголовки.Вставить("Content-Type","text/html; charset=utf-8");
КонецЕсли;
КонецПроцедуры
Функция ФорматОтветаJSON() Экспорт
Возврат "JSON";
КонецФункции
#КонецОбласти
В модуль PAPI_ОбработкаМетодовGET добавляем код обработки метода "ПолучитьСписокЗаданийЗаПериод"
Код модуля PAPI_ОбработкаМетодовGET
//Функция экспортная, специально чтобы можно было отлаживать как по HTTP(https://its.1c.ru/db/metod8dev#content:5756:hdoc)
//Так и через внешнюю обработку
Функция PAPI_ОбработкаМетодовGET(СтруктураВхПараметров) Экспорт
//структура ответа. Собственна нужна для формирования ответа
СтруктураОтвет = Новый Структура("ДанныеОтвета,Отработало,ТекстОшибки,КодОтвета","",Истина,"",200);
#Область ПримерыЧасть1
//Переправляем на нужный метод
Если ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("Список") Тогда
ПолучитьСписок(СтруктураОтвет,СтруктураВхПараметров);
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("Документ") Тогда
ПолучитьДокумент(СтруктураОтвет,СтруктураВхПараметров);
#КонецОбласти
//Получаем список заданий за период (БСП)
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ПолучитьСписокЗаданийЗаПериод") Тогда
PAPI_ОбработкаУниверсальныхМетодов.ПолучитьСписокЗаданийЗаПериод(СтруктураОтвет,СтруктураВхПараметров);
#Область ПримерыЧасть2
//Получаем доступные остатки по складу в формате JSON (ERP, УТ, КА)
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ОстаткиИДоступностьТоваровПоСкладу") Тогда
PAPI_ОбработкаУниверсальныхМетодов.ПолучитьОстаткиИДоступностьТоваровПоСкладу(СтруктураОтвет,СтруктураВхПараметров);
#КонецОбласти
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,501,"Отсутствует Метод " + ?(СтруктураВхПараметров.ИмяМетода=Неопределено,"",СтруктураВхПараметров.ИмяМетода),ложь,"");
КонецЕсли;
Возврат СтруктураОтвет;
КонецФункции
#Область Методы
Процедура ПолучитьСписок(СтруктураОтвет,СтруктураВхПараметров)
Результат = "Расходная накладная, 1, 01.01.2014
|Расходная накладная, 2, 01.01.2014";
СтруктураОтвет.ДанныеОтвета = Результат;
КонецПроцедуры
Процедура ПолучитьДокумент(СтруктураОтвет,СтруктураВхПараметров)
//ПраметрыЗапроса - number
ПарНомер = СтруктураВхПараметров.ПараметрыЗапроса.Получить("number");
Номер = Число(?(ПарНомер=Неопределено,0,ПарНомер));
Если Номер > 2 Тогда
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Отсутствует документ с номером: " + Номер,ложь,"");
Иначе
Если Номер = 1 Тогда
Результат = "Расходная накладная, 1, 01.01.2014";
Иначе
Результат = "Расходная накладная, 2, 01.01.2014";
КонецЕсли;
СтруктураОтвет.ДанныеОтвета = Результат;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
Создаем модуль PAPI_ОбработкаМетодовPOST
Код модуля PAPI_ОбработкаМетодовPOST
//Функция экспортная, специально чтобы можно было отлаживать как по HTTP(https://its.1c.ru/db/metod8dev#content:5756:hdoc)
//Так и через внешнюю обработку
Функция PAPI_ОбработкаМетодовPOST(СтруктураВхПараметров) Экспорт
//структура ответа. Собственна нужна для формирования ответа
СтруктураОтвет = Новый Структура("ДанныеОтвета,Отработало,ТекстОшибки,КодОтвета","",Истина,"",200);
//Переправляем на нужный метод
//Получаем список заданий за период (БСП)
Если ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ПолучитьСписокЗаданийЗаПериод") Тогда
PAPI_ОбработкаУниверсальныхМетодов.ПолучитьСписокЗаданийЗаПериод(СтруктураОтвет,СтруктураВхПараметров);
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,501,"Отсутствует Метод " + ?(СтруктураВхПараметров.ИмяМетода=Неопределено,"",СтруктураВхПараметров.ИмяМетода),ложь,"");
КонецЕсли;
Возврат СтруктураОтвет;
КонецФункции
Наш сервис готов! Давайте теперь дорисуем обработку и будем тестировать. Обратите внимание, что я оставил метод ANY, по сути, обращение к нему отличается лишь отсутствием в URL "V1/". Данный метод позволит Вам выводить параметры вашего запроса.
GET: http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI/V1/ПолучитьСписокЗаданийЗаПериод?StartPeriod=2014-01-01T00:00:00&EndPeriod=2024-01-01
[{"Задание":"Ознакомиться с демонстрационной конфигурацией","Guid":"b63d513e-8d7f-11de-a1ba-005056c00008","Автор":"Администратор","Дата":"2009-08-20T15:47:29","Выполнено":false},{"Задание":"Выяснить, почему нельзя изменить заказ покупателя","Guid":"1bbc0bef-09e6-11e0-915d-e0cb4ed5f627","Автор":"Петрищев Олег Константинович (руководитель)","Дата":"2010-12-17T17:46:40","Выполнено":false},{"Задание":"Проверить документацию","Guid":"8533f2f3-1efc-11e4-bcee-bcaec5d979f0","Автор":"Петрищев Олег Константинович (руководитель)","Дата":"2014-08-08T17:03:26","Выполнено":false},{"Задание":"Обновить демо конфигурацию","Guid":"f55262f3-c43d-11e5-a15c-20cf30c96081","Автор":"Петрищев Олег Константинович (руководитель)","Дата":"2024-01-26T18:02:26","Выполнено":false}]
GET через ANY: http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI/ПолучитьСписокЗаданийЗаПериод?StartPeriod=2014-01-01T00:00:00&EndPeriod=2024-01-01
GET
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/ПолучитьСписокЗаданийЗаПериод
Заголовки:
X-Compress:null
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Accept-Encoding:gzip, deflate, br
Connection:keep-alive
Accept-Language:ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Upgrade-Insecure-Requests:1
Host:127.0.0.1
Параметры URL:
ИмяМетода:ПолучитьСписокЗаданийЗаПериод
Параметры запроса:
StartPeriod:2014-01-01T00:00:00
EndPeriod:2024-01-01
Тело запроса:
Доделаем обработку, которую создавали выше. Добавим реквизиты КонецПериода и НачалоПериода с типом Дата, МетодСервиса с типом Строка и делаем РежимВыбораИзСписка (GET(GET), POST(POST), Проверить(ANY)). Добавляем команду ПолучитьРезультат.
Код команды ПолучитьРезультат
&НаКлиенте
Процедура ПолучитьРезультат(Команда)
Если не ЗначениеЗаполнено(МетодСервиса) Тогда
Сообщить("Необходимо выбрать ""Метод сервиса""!");
Возврат;
КонецЕсли;
Результат = "";
ПолучитьРезультатНаСервере();
КонецПроцедуры
&НаСервере
Процедура ПолучитьРезультатНаСервере()
перПорт = 0;
перСервер = "127.0.0.1";
перПользователь = "Администратор";
перПароль = "";
Попытка
Если перПорт <= 0 Тогда
HTTPСоединение = Новый HTTPСоединение(перСервер,,перПользователь,перПароль);
Иначе
HTTPСоединение = Новый HTTPСоединение(перСервер,перПорт,перПользователь,перПароль);
КонецЕсли;
Исключение
Сообщить("Подключение не прошло по причине "+ОписаниеОшибки());
//Подчищаем соединение
HTTPСоединение = Неопределено;
Возврат;
КонецПопытки;
Если МетодСервиса = "POST" ИЛИ МетодСервиса = "ANY" Тогда
//Создаем структуру запроса
СтруктураЗапроса = новый Структура;
Если ЗначениеЗаполнено(НачалоПериода) Тогда
СтруктураЗапроса.Вставить("StartPeriod",НачалоПериода);
КонецЕсли;
Если ЗначениеЗаполнено(КонецПериода) Тогда
СтруктураЗапроса.Вставить("EndPeriod",КонецПериода);
КонецЕсли;
перРесурсНаСервере = "/DemoSSL2_4_6_146/hs/PAPI/"+?(МетодСервиса = "ANY","","V1/")+"ПолучитьСписокЗаданийЗаПериод";
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " " , Истина, ЭкранированиеСимволовJSON.Нет, Ложь, Ложь, Ложь, Ложь);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ПроверятьСтруктуру = Истина;
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(ЗаписьJSON, СтруктураЗапроса);
ЗапросJSON = ЗаписьJSON.Закрыть();
HTTPЗапрос = Новый HTTPЗапрос(перРесурсНаСервере);
HTTPЗапрос.УстановитьТелоИзСтроки(ЗапросJSON);
HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("POST",HTTPЗапрос);
//Получаем ответный текст или текст ошибки
Результат = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
КонецЕсли;
Если МетодСервиса = "GET" ИЛИ МетодСервиса = "ANY" Тогда
//Создаем параметры запроса
перСтруктураЗапроса = "";
Если ЗначениеЗаполнено(НачалоПериода) Тогда
перСтруктураЗапроса = "?StartPeriod=" + Формат(НачалоПериода,"ДФ=yyyy-MM-dd");
КонецЕсли;
Если ЗначениеЗаполнено(КонецПериода) Тогда
перСтруктураЗапроса = ?(ПустаяСтрока(перСтруктураЗапроса),
"?EndPeriod="+Формат(КонецПериода,"ДФ=yyyy-MM-dd"),
перСтруктураЗапроса+"&EndPeriod=" + Формат(КонецПериода,"ДФ=yyyy-MM-dd"));
КонецЕсли;
перРесурсНаСервере = "/DemoSSL2_4_6_146/hs/PAPI/"+?(МетодСервиса = "ANY","","V1/")+"ПолучитьСписокЗаданийЗаПериод"+перСтруктураЗапроса;
HTTPЗапрос = Новый HTTPЗапрос(перРесурсНаСервере);
HTTPОтвет = HTTPСоединение.ВызватьHTTPМетод("GET",HTTPЗапрос);
//Получаем ответный текст или текст ошибки
Ответочка = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Результат = ?(ПустаяСтрока(Результат), Ответочка,
"Метод: [POST]++"+ Символы.ПС + Результат + Символы.ПС + "Метод: [POST]--" + Символы.ПС +
"Метод: [GET]++" + Символы.ПС + Ответочка + Символы.ПС + "Метод: [GET]--");
КонецЕсли;
//Подчищаем соединение
HTTPСоединение = Неопределено;
КонецПроцедуры
Проверяем:
Метод: [POST]++
POST
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/ПолучитьСписокЗаданийЗаПериод
Заголовки:
Content-Type:application/octet-stream
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:71
Параметры URL:
ИмяМетода:ПолучитьСписокЗаданийЗаПериод
Параметры запроса:
Тело запроса:
{"StartPeriod":"2014-01-01T00:00:00","EndPeriod":"2024-08-25T00:00:00"}
Метод: [POST]—
Метод: [GET]++
GET
http://127.0.0.1/DemoSSL2_4_6_146/hs/PAPI
/ПолучитьСписокЗаданийЗаПериод
Заголовки:
Host:127.0.0.1
User-Agent:1C+Enterprise/8.3
Accept:*/*
Authorization:Basic 0JDQtNC80LjQvdC40YHRgtGA0LDRgtC+0YA6
Content-Length:0
Параметры URL:
ИмяМетода:ПолучитьСписокЗаданийЗаПериод
Параметры запроса:
StartPeriod:2014-01-01
EndPeriod:2024-08-25
Тело запроса:
Метод: [GET]—
Длительные операции.
Есть случаи, когда мы знаем, что код будет работать долгое время и браузер либо предложит разорвать операцию, либо разорвет ее намеренно (зависит от используемого браузера), либо мы знаем, что результат будет формироваться долгое время и нет смысла держать сеанс (лицензию) и мы просто можем дать задание на выполнение какой либо операции и забрать результат по готовности. 18.4. Особенности выполнения фоновых заданий в файловом и клиент-серверном вариантах.
Давайте для нашего GET метода создадим параметр BTask=True — будет означать, что операцию нужно выполнить используя БСП:Базовая функциональностьДлительные операции. Создадим модуль для асинхронных операций. Создадим метод GET для проверки выполнения фонового задания и получения результата "ФоновоеЗаданиеПроверить" с параметрами Result_Key (Адрес хранилища результата. Обязательный параметр) и Task_Key (Идентификатор фонового задания. Необязательный параметр).
Предлагаю перенести наше разрешение в Демо базу на основе БСП 3.0.1.231 (на платформе 8.3.12). Хочу создать справочник для хранения результатов, что позволит обращаться к результатам хоть через год… Пока не удалят элемент справочника.
Редактируем обработчик PAPI_GET, добавляем в структуру БазовыйURL.
//Обработка метода GET
Функция PAPI_GET(Запрос)
СтруктураВхПараметров = Новый Структура;
//Получаем имя метода
перИмяМетода = Запрос.ПараметрыURL["ИмяМетода"];
//Помещаем имя метода в структуру
СтруктураВхПараметров.Вставить("ИмяМетода",перИмяМетода);
//Метод запроса
СтруктураВхПараметров.Вставить("МетодЗапроса",Запрос.HTTPМетод);
//Базовая часть URL-запроса
СтруктураВхПараметров.Вставить("БазовыйURL",Запрос.БазовыйURL);
//Забираем параметры из запроса
ВхПараметрыЗапроса = Новый Соответствие;
Для каждого Параметр Из Запрос.ПараметрыЗапроса Цикл
ВхПараметрыЗапроса.Вставить(СокрЛП(Параметр.Ключ),Параметр.Значение);
КонецЦикла;
СтруктураВхПараметров.Вставить("ПараметрыЗапроса",ВхПараметрыЗапроса);
//Передаем входящие параметры (Модуль для метода GET)
СтруктураОтвет = PAPI_ОбработкаМетодовGET.PAPI_ОбработкаМетодовGET(СтруктураВхПараметров);
//Создаем ответ с кодом состояния
Ответ = Новый HTTPСервисОтвет(СтруктураОтвет.КодОтвета);
Если СтруктураОтвет.Отработало Тогда
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ДанныеОтвета,КодировкаТекста.UTF8);
Иначе
Ответ.УстановитьТелоИзСтроки(СтруктураОтвет.ТекстОшибки,КодировкаТекста.UTF8);
КонецЕсли;
//В зависимости от формата меняем "Content-Type"
PAPI_ОбщиеПроцедурыИФункции.ВозвращаемНужныйФорматОтвета(Ответ,СтруктураОтвет);
Возврат Ответ;
КонецФункции
Создаем Справочник.PAPI_ХранилищеРезультатов. Добавляем реквизиты АдресРезультата — Строка(200) и Результат — ХранилищеЗначения
Создаем модуль PAPI_АсинхронныеОперации
Код модуля PAPI_АсинхронныеОперации
Процедура ФЗ_ВыполнитьМетод_ДлительнаяОперация(СтруктураВхПараметров,АдресРезультата) Экспорт
//Удаляем ключ запуска в Фоне
СтруктураВхПараметров.ПараметрыЗапроса.Удалить("BTask");
//Получаем полученную структуру и выполняем как обычно не в фоне
СтруктураОтвет = PAPI_ОбработкаМетодовGET.PAPI_ОбработкаМетодовGET(СтруктураВхПараметров);
//Помещаем результат в справочник PAPI_ХранилищеРезультатов
НайденнаяСсылка = Справочники.PAPI_ХранилищеРезультатов.НайтиПоРеквизиту("АдресРезультата",СтруктураВхПараметров.АдресРезультата);
Если НайденнаяСсылка = Справочники.PAPI_ХранилищеРезультатов.ПустаяСсылка() Тогда
ТекущийОбъект = Справочники.PAPI_ХранилищеРезультатов.СоздатьЭлемент();
ТекущийОбъект.АдресРезультата = СтруктураВхПараметров.АдресРезультата;
ТекущийОбъект.УстановитьНовыйКод();
Иначе
ТекущийОбъект = НайденнаяСсылка.ПолучитьОбъект();
КонецЕсли;
ТекущийОбъект.Результат = Новый ХранилищеЗначения(СтруктураОтвет, Новый СжатиеДанных(9));
ТекущийОбъект.ОбменДанными.Загрузка = Истина;
ТекущийОбъект.Записать();
КонецПроцедуры
Правим модуль PAPI_ОбработкаМетодовGET. Добавляем код для запуска фонового задания и метод получения результата фонового задания.
Код модуля PAPI_ОбработкаМетодовGET
//Функция экспортная, специально чтобы можно было отлаживать как по HTTP(https://its.1c.ru/db/metod8dev#content:5756:hdoc)
//Так и через внешнюю обработку
Функция PAPI_ОбработкаМетодовGET(СтруктураВхПараметров) Экспорт
//структура ответа. Собственна нужна для формирования ответа
СтруктураОтвет = Новый Структура("ДанныеОтвета,Отработало,ТекстОшибки,КодОтвета","",Истина,"",200);
//Операцию нужно выполнить используя БСП:Базовая функциональностьДлительные операции++
ВФоне = СтруктураВхПараметров.ПараметрыЗапроса.Получить("BTask");
Если не ВФоне = Неопределено Тогда
ВФоне = Булево(ВФоне);
Если ВФоне = Истина Тогда
УникальныйИдентификатор = Новый УникальныйИдентификатор;
//Уникальный адрес результата
АдресРезультата = СтруктураВхПараметров.ИмяМетода+"_"+Строка(УникальныйИдентификатор);
//Подгатавливаем параметры для Длительной операции
ПараметрыЗапуска = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыЗапуска.НаименованиеФоновогоЗадания = "Выполнение_" + АдресРезультата;
ПараметрыЗапуска.ОжидатьЗавершение = Ложь;
ПараметрыЗапуска.КлючФоновогоЗадания = УникальныйИдентификатор;
ПараметрыЗапуска.ЗапуститьВФоне = Истина;
//Добавляем адрес в структуру
СтруктураВхПараметров.Вставить("АдресРезультата",АдресРезультата);
//Запускаем метод в фоне
РезультатФоновогоЗадания = ДлительныеОперации.ВыполнитьВФоне(
"PAPI_АсинхронныеОперации.ФЗ_ВыполнитьМетод_ДлительнаяОперация",
СтруктураВхПараметров,
ПараметрыЗапуска);
//Проверяем что задание запустилось
Если РезультатФоновогоЗадания.Статус = "Ошибка" Тогда
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Ошибка при формировании: "+Строка(РезультатФоновогоЗадания.КраткоеПредставлениеОшибки),Ложь,"");
Возврат СтруктураОтвет;
КонецЕсли;
ФоновоеЗаданиеИдентификатор = РезультатФоновогоЗадания.ИдентификаторЗадания;
АдресСсылки = СтруктураВхПараметров.БазовыйURL + "/V1/ФоновоеЗаданиеПроверить?" +
"Result_Key=" + АдресРезультата + "&" +
"Task_Key=" + Строка(ФоновоеЗаданиеИдентификатор);
//Формируем html ответ
СтруктураОтвет.ДанныеОтвета = "<html>
|<body>
| <b>Задание отправлено на выполнение!</b><br>
| <p>Идентификатор фонового задания = " + Строка(ФоновоеЗаданиеИдентификатор) + "<br>
| Адрес хранилища результата = " + АдресРезультата + "</p>
| <p><a href=""" + АдресСсылки + """>Ссылка на получение результата</a></p>
|</body>
|</html>";
Возврат СтруктураОтвет;
КонецЕсли;
КонецЕсли;
//Операцию нужно выполнить используя БСП:Базовая функциональностьДлительные операции--
#Область ПримерыЧасть1
//Переправляем на нужный метод
Если ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("Список") Тогда
ПолучитьСписок(СтруктураОтвет,СтруктураВхПараметров);
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("Документ") Тогда
ПолучитьДокумент(СтруктураОтвет,СтруктураВхПараметров);
#КонецОбласти
//Получаем список заданий за период (БСП)
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ПолучитьСписокЗаданийЗаПериод") Тогда
PAPI_ОбработкаУниверсальныхМетодов.ПолучитьСписокЗаданийЗаПериод(СтруктураОтвет,СтруктураВхПараметров);
//Проверяем фоновое задание. Если Выполнено забираем результат
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ФоновоеЗаданиеПроверить") Тогда
ПроверитьФоновоеЗадание(СтруктураОтвет,СтруктураВхПараметров);
#Область ПримерыЧасть2
//Получаем доступные остатки по складу в формате JSON (ERP, УТ, КА)
ИначеЕсли ВРег(СтруктураВхПараметров.ИмяМетода) = ВРег("ОстаткиИДоступностьТоваровПоСкладу") Тогда
PAPI_ОбработкаУниверсальныхМетодов.ПолучитьОстаткиИДоступностьТоваровПоСкладу(СтруктураОтвет,СтруктураВхПараметров);
#КонецОбласти
Иначе
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,501,"Отсутствует Метод " + ?(СтруктураВхПараметров.ИмяМетода=Неопределено,"",СтруктураВхПараметров.ИмяМетода),Ложь,"");
КонецЕсли;
Возврат СтруктураОтвет;
КонецФункции
#Область Методы
Процедура ПолучитьСписок(СтруктураОтвет,СтруктураВхПараметров)
Результат = "Расходная накладная, 1, 01.01.2014
|Расходная накладная, 2, 01.01.2014";
СтруктураОтвет.ДанныеОтвета = Результат;
КонецПроцедуры
Процедура ПолучитьДокумент(СтруктураОтвет,СтруктураВхПараметров)
//ПраметрыЗапроса - number
ПарНомер = СтруктураВхПараметров.ПараметрыЗапроса.Получить("number");
Номер = Число(?(ПарНомер=Неопределено,0,ПарНомер));
Если Номер > 2 Тогда
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Отсутствует документ с номером: " + Номер,ложь,"");
Иначе
Если Номер = 1 Тогда
Результат = "Расходная накладная, 1, 01.01.2014";
Иначе
Результат = "Расходная накладная, 2, 01.01.2014";
КонецЕсли;
СтруктураОтвет.ДанныеОтвета = Результат;
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьФоновоеЗадание(СтруктураОтвет,СтруктураВхПараметров)
перФоновоеЗаданиеИдентификатор = СтруктураВхПараметров.ПараметрыЗапроса.Получить("Task_Key");
АдресРезультата = СтруктураВхПараметров.ПараметрыЗапроса.Получить("Result_Key");
//Проверяем фоновое задание
Если перФоновоеЗаданиеИдентификатор = Неопределено Тогда
Задание = PAPI_ОбщиеПроцедурыИФункции.НайтиЗадание(Неопределено);
Иначе
//Получаем Уникальный идентификатор задания
ФоновоеЗаданиеИдентификатор = Новый УникальныйИдентификатор(перФоновоеЗаданиеИдентификатор);
Задание = PAPI_ОбщиеПроцедурыИФункции.НайтиЗадание(ФоновоеЗаданиеИдентификатор);
КонецЕсли;
//При отсутствии адреса результата прерываем
Если АдресРезультата = Неопределено Тогда
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Отсутствует параметр ""Result_Key""",Ложь,"");
Возврат;
КонецЕсли;
Если Не Задание.Выполняется Тогда
Попытка
НайденнаяСсылка = Справочники.PAPI_ХранилищеРезультатов.НайтиПоРеквизиту("АдресРезультата",АдресРезультата);
Если НайденнаяСсылка = Справочники.PAPI_ХранилищеРезультатов.ПустаяСсылка() Тогда
//Можно вынести в отдельную функцию
АдресСсылки = СтруктураВхПараметров.БазовыйURL + "/V1/ФоновоеЗаданиеПроверить?" +
"Result_Key=" + АдресРезультата + ?(ЗначениеЗаполнено(перФоновоеЗаданиеИдентификатор),
"&Task_Key=" + перФоновоеЗаданиеИдентификатор,"");
//Формируем html ответ
СтруктураОтвет.ДанныеОтвета = "<html>
|<body>
| <b>Задание еще выполняется, попробуйте получить результат позже!</b><br>
| <p>Идентификатор фонового задания = " + перФоновоеЗаданиеИдентификатор + "<br>
| Адрес хранилища результата = " + АдресРезультата + "</p>
|<p><a href=""" + АдресСсылки + """>Ссылка на получение результата</a></p>
|</body>
|</html>";
Иначе
СтруктураОтвет = НайденнаяСсылка.Результат.Получить();
КонецЕсли;
Исключение
PAPI_ОбщиеПроцедурыИФункции.ЗаполнитьСтруктуруОтвета(СтруктураОтвет,400,"Ошибка при формировании: "+ОписаниеОшибки(),Ложь,"");
КонецПопытки;
Иначе
//Задание еще в процессе
АдресСсылки = СтруктураВхПараметров.БазовыйURL + "/V1/ФоновоеЗаданиеПроверить?" +
"Result_Key=" + АдресРезультата + ?(ЗначениеЗаполнено(перФоновоеЗаданиеИдентификатор),
"&Task_Key=" + перФоновоеЗаданиеИдентификатор,"");
//Формируем html ответ
СтруктураОтвет.ДанныеОтвета = "<html>
|<body>
| <b>Задание еще выполняется, попробуйте получить результат позже!</b><br>
| <p>Идентификатор фонового задания = " + перФоновоеЗаданиеИдентификатор + "<br>
| Адрес хранилища результата = " + АдресРезультата + "</p>
|<p><a href=""" + АдресСсылки + """>Ссылка на получение результата</a></p>
|</body>
|</html>";
КонецЕсли;
КонецПроцедуры
#КонецОбласти
В модуль PAPI_ОбщиеПроцедурыИФункции, добавляем функцию НайтиЗадание.
Код модуля PAPI_ОбщиеПроцедурыИФункции
//Заполняем структуру ответа
Процедура ЗаполнитьСтруктуруОтвета(СтруктураОтвет,КодОтвета,ТекстОшибки,Отработало,ДанныеОтвета) Экспорт
СтруктураОтвет.КодОтвета = КодОтвета;
СтруктураОтвет.ТекстОшибки = ТекстОшибки;
СтруктураОтвет.Отработало = Отработало;
СтруктураОтвет.ДанныеОтвета = ДанныеОтвета;
КонецПроцедуры
#Область РаботаСДанными
//Получаем дату из входных параметров
Функция ПолучитьДатуВремяИзСтроки(ДатаВремя) Экспорт
Результат = Неопределено;
ДатаВремя = СтрЗаменить(ДатаВремя, "-", "");
ДатаВремя = СтрЗаменить(ДатаВремя, ":", "");
Если СтрДлина(ДатаВремя) - Найти(ДатаВремя, " ") = 5 Тогда // время в формате Ч:ММ:СС
ДатаВремя = СтрЗаменить(ДатаВремя, " ", "0");
ИначеЕсли Найти(ДатаВремя, "T") > 0 Тогда //время в формате ГГГГММДДTЧЧММСС
ДатаВремя = СтрЗаменить(ДатаВремя, "T", "");
Иначе // время в формате ЧЧ:ММ:СС
ДатаВремя = СтрЗаменить(ДатаВремя, " ", "");
КонецЕсли;
ОписаниеТипа = Новый ОписаниеТипов("Дата");
Результат = ОписаниеТипа.ПривестиЗначение(ДатаВремя); // дата и время в виде "ГГГГММДДЧЧММСС"
Если Не ЗначениеЗаполнено(Результат) Тогда
Результат = ОписаниеТипа.ПривестиЗначение(Лев(ДатаВремя, 8)); // только дата "ГГГГММДД"
КонецЕсли;
Возврат Результат;
КонецФункции
#КонецОбласти
#Область ФорматыОтвета
Процедура ВозвращаемНужныйФорматОтвета(Ответ,СтруктураОтвет) Экспорт
Если СтруктураОтвет.Свойство("ФорматОтвета") Тогда
ФорматОтвета = СтруктураОтвет.ФорматОтвета;
Иначе
ФорматОтвета = "";
КонецЕсли;
Если ВРег(ФорматОтвета) = Врег(ФорматОтветаJSON()) Тогда
Ответ.Заголовки.Вставить("Content-Type","application/json; charset=utf-8");
Иначе
Ответ.Заголовки.Вставить("Content-Type","text/html; charset=utf-8");
КонецЕсли;
КонецПроцедуры
Функция ФорматОтветаJSON() Экспорт
Возврат "JSON";
КонецФункции
#КонецОбласти
#Область РаботаСФоновымиЗаданиями
Функция НайтиЗадание(Идентификатор) Экспорт
// Считывает состояние фонового задания по переданному идентификатору.
//
// Параметры:
// Идентификатор - УникальныйИдентификатор - Идентификатор фонового задания.
//
// Возвращаемое значение:
// Неопределено - Задание не найдено.
// Структура - Сведения о задании.
// * Выполняется - Булево - Истина когда фоновое задание еще выполняется.
// * Успешно - Булево - Истина когда сеанс фонового задания завершился без ошибок.
// * Ошибка - Строка, ИнформацияОбОшибке, Неопределено - Описание ошибки.
//
УстановитьПривилегированныйРежим(Истина);
Результат = Новый Структура("Выполняется, Успех, Ошибка", Ложь, Ложь, Неопределено);
Если Идентификатор = Неопределено Тогда
Возврат Результат;
КонецЕсли;
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
Если Задание = Неопределено Тогда
Возврат Результат;
КонецЕсли;
Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Результат.Выполняется = Истина;
Иначе
Результат.Выполняется = Ложь;
Если Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
Результат.Успех = Истина;
Иначе
Результат.Успех = Ложь;
Результат.Ошибка = Задание.ИнформацияОбОшибке;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
#КонецОбласти
Проверяем результат, через браузер выполним следующий запрос:
http://127.0.0.1/DemoSSL3_0_1_231/hs/PAPI/V1/ПолучитьСписокЗаданийЗаПериод?StartPeriod=2008-01-01T00:00:00&EndPeriod=2024-01-01&BTask=true
Нажимаем "Ссылка на получение результата"
Была сформирована ссылка на сохраненный результат:
http://127.0.0.1/DemoSSL3_0_1_231/hs/PAPI/V1/ФоновоеЗаданиеПроверить?Result_Key=ПолучитьСписокЗаданийЗаПериод_b2fe7494-3e86-4748-bce6-a33d69c14ca2&Task_Key=e8a450b1-bdd5-4703-a76b-9c6015a39fb7
Соответственно можно и без идентификатора задания обращаться:
http://127.0.0.1/DemoSSL3_0_1_231/hs/PAPI/V1/ФоновоеЗаданиеПроверить?Result_Key=ПолучитьСписокЗаданийЗаПериод_b2fe7494-3e86-4748-bce6-a33d69c14ca2
Третью часть заканчиваю. Мы рассмотрели с Вами методы GET, POST и ANY, создали сервис на основании данных отчета СКД. Сделали возможность запускать фоновое задание через GET и сохранили результат. Данный механизм при должной доработке позволит выдавать одинаковые запросы без обработки запросов.
Статьи из данного цикла:
HTTP Сервисы: Путь к своему сервису. Часть 1
HTTP Сервисы: Путь к своему сервису. Часть 2
HTTP Сервисы: Путь к своему сервису. Часть 4
Спасибо за статью и слово «идемпотентный «.
Обязательно продолжайте
(1)Не за что ;)) А за слово не мне спасибо, а Бенджамину Пирсу.
Веб-/хттп-сервисы не занимают клиентскую лицензию (работают без ее использования)
(5) Ясно. Но все равно пока не приходит на ум такой сценарий, при котором на стороне потребителя хттп-сервиса такая экономия лицензии будет иметь смысл.
(6) Это просто вариант, который возможен, а смысл длительных операций и хранения результата может использоваться во многих вещах. Про одну из таких вещей я сказал в конце данной статьи вскользь — хранение результата для «одинаковых запросов». О еще одной вещице расскажу в 4 части, если соберусь ее писать…
+
Вопросик про длительные операции возник.
Вы в запросе входящем передаёте ИдентификаторЗадания для поиска фонового и АдресРезультат для поиска элемента в справочнике хранилище результатов. А можно ли бы идентификатор задания сохранить в реквизите справочника ХранилищеРезультатов, тогда в url запросе можно было передавать только один параметр АдресРезультата (или вообще уже передавать уникальный идентификатор элемента ХранилищеРезультатов) ?
(9)
Не вижу смысла хранить идентификатор фонового задания.
Фоновое задание служит для формирования результата и идентификатор нужен только на момент его работы, чтобы понимать ,что фоновое задание еще работает или уже завершено. Пока задание не отработало результата в принципе быть не может. А когда есть результат, идентификатор задания уже не нужен.
У меня в статье описано, что идентификатор задания не обязательный параметр, к результату вы можете потом обращаться так:
где Result_Key — адрес результата
Я конечно понимаю что имя метода ФоновоеЗаданиеПроверить несовсем корректно, надо было назвать ПолучитьРезультат или что то в этом духе…
С другой стороны я просто рисую примеры, а не коммерческий проект))
Спасибо за статью.
вопрос про длину URL для метода GET.
Нужно в параметре передавать список ID, но опытным путем выяснили, что в строку URL можно уложить примерно 310 символов.
vs-uat01-1c111/DEV_1/hs/DBReconciliation/GetData/1205687050,1205687070,1205687110,1205697010,1205702490,1205704160,1205704310,1205704320,1205704330,1205711230,1205711240,1205711250,1205711260,1205711270,1205711280,1205711300,1205711310,1205711320,1205711330,1205711340,1205711350,1205711370
Может где-то на IIS есть настройка, на ограничение длины запроса или в 1С органичение на длину передаваемого параметра?
Платформа 8.3.13.
(11)Картинка из 4 части
Максимальная длинна URL и максимальная длина строки запроса
Как вариант еще тут посмотрите ->http://qaru.site/questions/327774/wcf-get-url-length-limit-issue-bad-request-invalid-url
Отпишитесь если помогло.
(12) Приветсвую, спасибо за помощь,
помог именно вариант 2, когда в реестре прописали HKEY_LOCAL_MACHINESystemCurrentControlSetServicesHTTPPa rameters
UrlSegmentMaxLength = 1000.