Подключение к REST API через OAuth протокол из 1С

Введение

Задача: подключение к REST API по протоколу Oauth 2.0 и получение данных от API для дальнейшего парсинга и загрузки в БД.

Решение состоит из:
* Получение токена авторизации;
* Получение данных с использованием токена авторизации.

Получение токена авторизации

Получение токена авторизации выполняется при помощи POST запроса XTTP, при этом есть нюансы реализации этого запроса для x32 и x64 разрядных версий сервера 1С, а также особенности при работотчетые этого запроса в веб клиенте.

XTTP запрос для получения токена OAuth выглядит следующим образом:

 
&НаКлиенте
Функция XTTPЗапросАвторизации()
	
	XTTPЗапрос = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    Скрипт = Новый COMОбъект("MSScriptControl.ScriptControl");
    Скрипт.language = "javascript";
    Скрипт.AddObject("XTTPЗапрос", XTTPЗапрос);
    Скрипт.Eval("XTTPЗапрос.Option(4)=13056");
    
    XTTPЗапрос.Open("Post", "https://ваш_url/connect/token", 0);
    XTTPЗапрос.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    
    ПараметрыPOST = "client_id=ваш_client_id" + "&";
    ПараметрыPOST = ПараметрыPOST + "grant_type=ваш_grant_type" + "&";
    ПараметрыPOST = ПараметрыPOST + "username=" + Логин + "&";
    ПараметрыPOST = ПараметрыPOST + "password=" + Пароль + "&";
    ПараметрыPOST = ПараметрыPOST + "redirect_uri=https://ваш_redirect_uri/callback";
	
    XTTPЗапрос.send(ПараметрыPOST);
	
	Возвратпечатную версию XTTPЗапрос;
	
Конец пользователь Функции

&НаКлиенте
Процедура Войти(Команда)
	
	Ответ = XTTPЗапросАвторизации();
    Если Ответ.Status = 200 Тогда
		СтрокаОтвета = Ответ.ResponseText;
	
		РезультатЧтения = ДесериализироватьJSONОтвет(СтрокаОтвета);
		Токен = РезультатЧтения.token; 
	Конец пользователь Если;

Конец пользователь Процедуры

&НаСервереБезКонтекста
Функция ДесериализироватьJSONОтвет(СтрокаОтвета)
	
	ОтветJSON	= Новый ЧтениеJSON;
	ОтветJSON.УстановитьСтроку(СтрокаОтвета);

	РезультатЧтения	= ПрочитатьJSON(ОтветJSON);
	
	ОтветJSON.Закрыть();

	Возвратпечатную версию РезультатЧтения;

Конец пользователь Функции

 

Проблемы при работотчетые с POST XTTP подключением авторизации

1. Ошибка при получении токена на x64 разрядном сервере 1С.

Можно столкнуться с проблемой, когда у клиента 1С в развернута в платном облачном сервисе, где возможности изменить параметры сервера или зарегистрировать дополнительные dll нет возможности, либо запрещено выполнять JavaScript, который в нашем коде создается в строке COMОбъект("MSScriptControl.ScriptControl").

То есть, если подключение XTTP описано на сервере, например в модуле объекта обрабплатфорконсоль отчетов маотки и при этом сервер 1С x64 разрядный, то можно получить следующую ошибку: 

{ВнешняяОбработотчетыка.ЗагрузкаЧеков.МодульОбъекта(336)}: Ошибка при вызове конструктора (COMОбъект): -2147221164(0x80040154): Class not registered.

2. Ошибка при получении токена при помощи объекта "WinHttp.WinHttpRequest.5.1" на клиенте при работотчетые в веб клиенте 1С.

Если XTTP подключение описано на клиенте в форме обрабплатфорконсоль отчетов маотки, то при выполнении этого кода в браузере Chrome, можно получить следующую ошибку: 

{ВнешняяОбработотчетыка.ЗагрузкаЧеков.Форма.ФормаНастроек.Форма(187)}: Тип не определен (COMObject).

Ошибка связана с особенностями запуска ActiveX объектов в браузерах. В Internet explorer текст ошибки может отличаться.

Решение ошибок 

Для решения проблемы с исполнением кода на x64 разрядном сервере нужно перенести код подключения на клиент в форму обрабплатфорконсоль отчетов маотки, так как это представлено в примере кода выше. Но при этом получается что в браузере это решение всё ещё работотчетыать не будет. Потому что браузер с нужным нам COM объектом работотчетыать не может и углубляться в причины этой проблемы будет просто потерей времени. Гораздо более быстрым решением станет реализация этого запроса при помощи JavaScript помещенного в элемент Тарифы на абонемент формы с видом ПолеHTMLдокумента.

Решение получения токена в веб клиенте

Есть различные варианты реализации подхода через ПолеHTMLДокумента. Например можно сделать кнопку "Войти" на форме и по этой кнопке вызывать скрипт описанный в HTML документе, а можно сверстать HTML, поместить его в ПолеHTMLДокумента, то есть кнопка "Войти" будет элемент Тарифы на абонементом HTML страницы. Я реализовывал второй вариант.

 
&НаКлиенте
Процедура ПриОткрытии(Отказ)
	
// Для веб клиента отключаем элемент Тарифы на абонементы формы для подключения через запрос XTTP описанный выше, для не веб клиента соответственно наоборот.
	#Если ВебКлиент Тогда
		ЗаполнитьHTML();
		Элементы.ГруппаНастройкиСоединенияCOM.Видимость = Ложь;
// ГруппаНастройкиСоединенияCOM - содержит поля "Логин", "Пароль", и кнопку "Войти".
	#Иначе
		Элементы.ГруппаНастройкиСоединенияHTML.Видимость = Ложь;
// ГруппаНастройкиСоединенияHTML - содержит строковый реквизит Авторизация, который на форме имеет вид ПолеHTMLДокумента
	#Конец пользователь Если

Конец пользователь Процедуры

&НаКлиенте
Процедура ЗаполнитьHTML()
	
	JavaScript = ПолучитьСкриптАвторизации();

	ТекстХТМЛ = 
	#Область HTMLДокумент
	"<html>
	|<head>
	|    <meta charset=""UTF-8"">
	|    <title>Авторизация</title>
	|</head>
	|<body>
	|    <form>
	|        <table>
	|            <tr>
	|                <td><label for=""loginField"">Логин</label></td>
	|                <td><input id=""loginField"" type=""text"" name=""login""></td>
	|            </tr>
	|            <tr>
	|                <td><label for=""passwordField"">Пароль</label></td>
	|                <td><input id=""passwordField"" type=""password"" name=""password""></td>
	|            </tr>
	|            <tr>
	|                <td colspan=""2"" style=""text-align: center"">
	|                    <input type=""button"" onClick=""authorize()"" value=""Войти"">
	|                </td>
	|            </tr>
	|        </table>
	|    </form>
	|	 <div id=""result"" style=""display:none""></div><div id=""status"" style=""display:none""></div>
	|	 <div id=""showMessage""></div>
	|</body>" + JavaScript + "
	|</html>";
	#Конец пользователь Области
	
	Авторизация = ТекстХТМЛ;

Конец пользователь Процедуры

&НаКлиенте
Функция ПолучитьСкриптАвторизации()
	
	JavaScriptChrome = 
	#Область JavaScript
	"<script>
	|	window.onload
	|	{
// Если у нас отдельная форма настроек, то при первом открытии этой формы можно "запомнить"
//  значения логина и пароля, а при повторном открытии эти сохранконфигурацииенные данные заполнить в соответствующие поля в HTML.
	|	    var username = document.getElementById(""loginField"");
	|	    username.value = ""%1"";
	|	    var password = document.getElementById(""passwordField"");
	|	    password.value = ""%2"";
	|	}	
// Выполняем запрос к API и полученные данные помещаем в div result.
// А также вызываем функцию showMessage в которой выводим в форму сообщение 
//  о положительном или отрицательном результате выполнения запроса.
	|    function authorize() {
	|        var username = document.getElementById(""loginField"");
	|        var password = document.getElementById(""passwordField"");
	|        var details = {
	|            'client_id': 'ваш_client_id',
	|            'grant_type': 'ваш_grant_type',
	|            'username': username.value,
	|            'password': password.value,
	|            'redirect_uri': 'https://ваш_redirect_uri.com/callback'
	|        };
	|        var formBody = [];
	|        for (var property in details) {
	|            var encodedKey = encodeURIComponent(property);
	|            var encodedValue = encodeURIComponent(details[property]);
	|            formBody.push(encodedKey + ""="" + encodedValue);
	|        }
	|        formBody = formBody.join(""&"");
	|        fetch('https://ваш_url/connect/token', {
	|            method: 'POST',
	|            headers: {
	|                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
	|            },
	|            body: formBody
	|        }).then(function(response) {
	|    		var status = document.getElementById(""status"");
	|  	  		status.innerHTML = response.status;
	|    		showMessage(response.status);
	|    		return response.json();
	|		}).then(function(answer) {
	|    		var result = document.getElementById(""result"");
	|  		  	result.innerHTML = answer.token;
	|		});
	|    }
	|	function showMessage(statusCode){
	|    	var showMessage = document.getElementById(""showMessage"");
	|    	if(statusCode == ""200"") {
	|        showMessage.innerHTML = ""Подключение выполнено, можно загружать данные."";
	|    	} else {
	|        showMessage.innerHTML = ""Подключение не выполнено, проверьте данные авторизации. Код ответа: "" + statusCode;
	|    	}
	|	}	
	|</script>";
	#Конец пользователь Области
	
// У Internet Explorer есть ограничения по использованию некоторых методов. 
// Поэтому лучше использовать древние провереншаблонные методы.
	JavaScriptIE = 
	#Область JavaScript
	"<script>
// Аналогично сначала заполняем пароль и логин, если мы повторно открываем форму настроек.
	|	window.onload
	|	{
	|	    var username = document.getElementById(""loginField"");
	|	    username.value = ""%1"";
	|	    var password = document.getElementById(""passwordField"");
	|	    password.value = ""%2"";
	|	}	
// Выполняем запрос получения токена от API.
	|	function authorize() {
	|    	var username = document.getElementById(""loginField"");
	|    	var password = document.getElementById(""passwordField"");
	|		if (window.XMLHttpRequest) {
	|    		xmlhttp = new XMLHttpRequest();
	|		} else {
	|    		xmlhttp = new ActiveXObject(""Microsoft.XMLHTTP"");
	|		}
// Т.к. в 1С встроена 7я версия IE, рекомендую не писать циклов по конкатенации списка параметров, а просто описать их одной строкой,
//  иначе встроенный браузер может ругаться на ошибки скрипта.
	|		var data = ""password=""+password.value+""&username=""+username.value+""&grant_type=ваш_grant_type&client_id=ваш_client_id"";
	|		var xhr = xmlhttp;
	|		xhr.addEventListener(""readystatechange"", function () {
	|    		if (xhr.readyState == 4 && xhr.status == 200) {
	|        		var obj = JSON.parse(xhr.responseText);
	|        		result.innerHTML = obj.token;
	|        		status.innerHTML = xhr.status;
	|				showMessage(xhr.status);				
	|    		}
	|   		if (xhr.readyState == 4 && xhr.status != 200) {
	|        		status.innerHTML = xhr.status;
	|    		}
	|		});
	|		xhr.open(""POST"", ""https://ваш_url/connect/token"");
	|		xhr.setRequestHeader(""Content-Type"", ""application/x-www-form-urlencoded"");
	|		xhr.send(data);	
	|   }
	|	function showMessage(statusCode){
	|    	var showMessage = document.getElementById(""showMessage"");
	|    	if(statusCode == ""200"") {
	|        	showMessage.innerHTML = ""Подключение выполнено, можно загружать данные."";
	|    	} else {
	|        	showMessage.innerHTML = ""Подключение не выполнено, проверьте данные авторизации. Код ответа: "" + statusCode;
	|    	}
	|	}	
	|</script>";
	#Конец пользователь Области
	
	ТекущийБраузер = ОпределитьБраузер();
	
// Определяем браузер и используем подходящий нам скрипт.
	Если ТекущийБраузер = "IE" Тогда
		JavaScript = JavaScriptIE;
	Иначе
// Данный скрипт так же работотчетыает и в Firefox, на остальных браузерах не тестировал, т.к. посчитал это нецелесообразным 
//  и в случае необходимости за доп. время буду дорабатывать по требованию клиента
		JavaScript = JavaScriptChrome;
	Конец пользователь Если;
	
	JavaScript = СтрШаблон(JavaScript, Логин, Пароль);

	Возвратпечатную версию JavaScript;
	
Конец пользователь Функции

&НаКлиенте
Функция ОпределитьБраузер()
	
	СистемнаяИнформация = Новый СистемнаяИнформация; 
	ТекущаяСистема = СистемнаяИнформация.ИнформацияПрограммыПросмотра;
	
	Если ПустаяСтрока(ТекущаяСистема) Тогда
		Возвратпечатную версию "ТонкийКлиент";
	ИначеЕсли СтрНайти(ВРег(ТекущаяСистема), "CHROME") Тогда
		Возвратпечатную версию "Chrome";
	ИначеЕсли СтрНайти(ВРег(ТекущаяСистема), "FIREFOX") Тогда 
		Возвратпечатную версию "Firefox";
	Иначе
		Возвратпечатную версию "IE";
	Конец пользователь Если;
	
Конец пользователь Функции

Токен получен. Соответственно токен нужно положить в какой-нибудь реквизит, либо передать токен в основную форму обрабплатфорконсоль отчетов маотки, если форму авторизации вы сделали как отдельную форму настройки.

Получение данных из API с использованием токена авторизации

Для получения данных с использованием токена нужно описать XTTP запрос, но уже GET. Аналогично получению токена, реализация для веб клиента и тонкого клиента будут различаться. Для веб клиента это HTML+JavaScript, для тонкого клиента это XTTP с использованием COM "WinHttp.WinHttpRequest.5.1".

Есть некоторые общие функции для формировпотому ания параметров, которые подставляются в запрос.

 
#Область ВспомогательныеПроцедурыИФункции

&НаСервереБезКонтекста
Функция ПараметрыURLЗапроса(ДатаНачала, ДатаОкончания)
	
	ПараметрыURLЗапроса	= Новый Структура;
	ПараметрыURLЗапроса.Вставить("skip", 0);
	ПараметрыURLЗапроса.Вставить("take", 50);
	ПараметрыURLЗапроса.Вставить("fromDate", XMLСтрока(ДатаНачала)+"Z");
	ПараметрыURLЗапроса.Вставить("toDate", XMLСтрока(ДатаОкончания)+"Z");
	
	Возвратпечатную версию ПараметрыURLЗапроса;
	
Конец пользователь Функции

&НаСервереБезКонтекста
Функция СтрокаПараметровURLЗапроса_Преобразовать(ПараметрыURLЗапроса) 
	
	СтрокаПараметровURLЗапроса	= "";
	
	Для каждого КлючЗначение из ПараметрыURLЗапроса Цикл
		
		СтрокаПараметровURLЗапроса	= СтрокаПараметровURLЗапроса + КлючЗначение.Ключ + "=" + КодироватьСтроку(КлючЗначение.Значение, СпособКодированияСтроки.КодировкаURL) + "&";
		
	Конец пользователь Цикла;
	
	Если Прав(СтрокаПараметровURLЗапроса, 1) = "&" Тогда
		СтрокаПараметровURLЗапроса	= Лев(СтрокаПараметровURLЗапроса, СтрДлина(СтрокаПараметровURLЗапроса) - 1);
	Конец пользователь Если;
	
	Возвратпечатную версию СтрокаПараметровURLЗапроса;
	
Конец пользователь Функции

#Конец пользователь Области

 

Реализация получения данных для тонкого клиента

 
&НаКлиенте
Функция XTTPЗапросДанных()
	
	XTTPЗапрос = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    Скрипт = Новый COMОбъект("MSScriptControl.ScriptControl");
    Скрипт.language = "javascript";
    Скрипт.AddObject("XTTPЗапрос", XTTPЗапрос);
	Скрипт.Eval("XTTPЗапрос.Option(2)=1251");
	Скрипт.Eval("XTTPЗапрос.Option(4)=13056"); // intSslErrorIgnoreFlags
	Скрипт.Eval("XTTPЗапрос.Option(6)=true");  // blnEnableRedirects
	Скрипт.Eval("XTTPЗапрос.Option(12)=true"); // blnEnableHttpsToHttpRedirects 
	
	// Формировпотому ание GET запроса 
	АдресСервиса = "https://ваш_url/A/B/C";
	ПараметрыURLЗапроса = ПараметрыURLЗапроса(Объект.ДатаНачала, Конец пользователь Дня(Объект.ДатаОкончания));
	
	СтрокаПараметровURLЗапроса = СтрокаПараметровURLЗапроса_Преобразовать(ПараметрыURLЗапроса);
	СтрокаGetЗапроса = АдресСервиса + "?" + СтрокаПараметровURLЗапроса;
	
    XTTPЗапрос.Open("GET", СтрокаGetЗапроса, 0);
	XTTPЗапрос.setRequestHeader("Origin", "*");
    XTTPЗапрос.setRequestHeader("Authorization", "Bearer " + Объект.Токен);
    XTTPЗапрос.setRequestHeader("Content-Type", "application/json");
	
	XTTPЗапрос.send();
	
	Объект.Статус = Строка(XTTPЗапрос.Status);
	Если XTTPЗапрос.Status = 200 Тогда
		Возвратпечатную версию XTTPЗапрос.ResponseText; // результат запроса JSON строка, которую будем парсить и грузить эти данные в БД.
	Иначе
		Возвратпечатную версию "";
	Конец пользователь Если;
	
Конец пользователь Функции

Есть нюанс в заголовке XTTPЗапрос.setRequestHeader("Origin", "*"), чтобы запрос к API работотчетыал с этим заголовком, нужно обсуждать параметры API с теми кто им заведует. Насколько я знаю, в настройку Origin на сервере API нужно устанавливать значение адреса домена с которого осуществляется подключение. Без настройки этого заголовка на сервере и установки этого заголовка в коде 1С может возникать ошибка авторизации 403.

Реализация получения данных для веб клиента

Аналогично авторизации, необходимо при создании формы заполнить содержание HTML документа на форме. При этом не получиться сделать поле HTML на форме 1С невидимым, иначе оно не инициализируется. Поэтому просто делаем все элемент Тарифы на абонементы HTML разметки невидимыми при помощи display:none.

 
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработотчетыка)
	
	ТекстХТМЛ = 
	#Область HTMLДокумент
	"<html>
	|<head>
	|    <meta charset=""UTF-8"">
	|    <title>Загрузка данных</title>
	|</head>
	|<body>
	|    <form>
	|    	<input id=""enter"" type=""button"" value=""Загрузить данные"" style=""display:none"">
	|    </form>
	|	 <div id=""result"" style=""display:none""></div><div id=""status"" style=""display:none""></div>
	|</body> 
	|</html>";
	#Конец пользователь Области
	
	ЗагрузкаHTML = ТекстХТМЛ;

Конец пользователь Процедуры

Т.к. время запроса к серверу может в зависимости от нагрузки на сервер быть разным, то нужно предусмотреть ожидание получения результата. Поэтому получения данных из HTML+JS у меня реалезовано не через функцию, а процедуру, в которой после выполнения скрипта подключается обрабплатфорконсоль отчетов маотчик ожидания с вызовом функции, и в этой функции после того как мы увидели, что результат запроса был помещен в div "result" JavaScript'ом, мы получаем из этого div'а текст JSON и отправляем его на обрабплатфорконсоль отчетов маотку в другие процедуры и функции.

 
#Область ЗагрузкаДанныхВебКлиент

&НаКлиенте
Процедура ЗагрузитьДанныеСПомощьюJavaScript()
	
	// Формировпотому ание GET запроса 
	АдресСервиса = "https://ваш_url/A/B/C";
	ПараметрыURLЗапроса = ПараметрыURLЗапроса(Объект.ДатаНачала, Конец пользователь Дня(Объект.ДатаОкончания));
	
	СтрокаПараметровURLЗапроса = СтрокаПараметровURLЗапроса_Преобразовать(ПараметрыURLЗапроса);
	СтрокаGetЗапроса = АдресСервиса + "?" + СтрокаПараметровURLЗапроса;
	
	JavaScript = ПолучитьJavaСкриптЗагрузкиДанных(СтрокаGetЗапроса, Объект.Токен);
	
	ДИВ = Элементы.ЗагрузкаHTML.Документ.getElementById("enter");
    
    НашлиАтрибут = ДИВ.getAttributeNode("onclick");
    Если НашлиАтрибут <> Null Тогда
        ДИВ.removeAttribute("onclick");
    Конец пользователь Если; 
    
    Атрибут  = Элементы.ЗагрузкаHTML.Документ.createAttribute("onclick");
    Атрибут.value = JavaScript;
    ДИВ.attributes.setNamedItem(Атрибут); 
    
    ДИВ.click();
	
	КоличествоВызовов = 0;

// Ожидаем когда в div result появится текст JSON
	ПодключитьОбработотчетычикОжидания("ПолучитьРезультатЗапросаВРежимеОжидания", 1, Ложь);
	
Конец пользователь Процедуры

&НаКлиенте
Процедура ПолучитьРезультатЗапросаВРежимеОжидания()
	
	Отказ = Ложь;
	КоличествоВызовов = КоличествоВызовов + 1;
	
	ДИВResult = Элементы.ЗагрузкаHTML.Документ.getElementById("result");
	ДИВStatus = Элементы.ЗагрузкаHTML.Документ.getElementById("status");
	Объект.Статус = ДИВStatus.innerHTML;
	СтрокаJSON 	  = ДИВResult.innerHTML;
	
// Подождали 20 секунд, считаем что что то пошло не так и прекращаем загрузку. 
// Количество секунд регулируется на свое усмотрение.
	Если КоличествоВызовов > 20 Тогда
		ТекстОтказа = НСтр("ru='Данные от сервера не получены в течении %1 секунд'");
		ТекстОтказа = СтрШаблон(ТекстОтказа, КоличествоВызовов);
		ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОтказа);
		ОтключитьОбработотчетычикОжидания("ПолучитьРезультатЗапросаВРежимеОжидания");
		Возвратпечатную версию;
	Иначе
		Если (Не ПустаяСтрока(СтрокаJSON) Или Не ПустаяСтрока(Объект.Статус)) Тогда
			
			ОтключитьОбработотчетычикОжидания("ПолучитьРезультатЗапросаВРежимеОжидания");
			Если ПустаяСтрока(СтрокаJSON) Тогда
				ТекстОтказа = НСтр("ru='Данные от сервера не получены. Код ответа: %1'");
				ТекстОтказа = СтрШаблон(ТекстОтказа, Объект.Статус);
				ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОтказа);
				Возвратпечатную версию;
			Иначе
// Появился в div result текст JSON, отправляем его на обрабплатфорконсоль отчетов маотку.
				ОбработотчетыкаДанныхНаСервере(СтрокаJSON, Отказ, ТекстОтказа);	
			Конец пользователь Если;
			
			Если Отказ Тогда
				ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОтказа);
			Конец пользователь Если;
			
		Конец пользователь Если;	
	Конец пользователь Если;	
	
Конец пользователь Процедуры

&НаКлиенте
Функция ПолучитьJavaСкриптЗагрузкиДанных(СтрокаGetЗапроса, Токен)

	JavaScriptChrome = 
	#Область JavaScriptChrome
	"fetch(""%1"", {
	|		method: ""GET"",
	|		headers: {
	|			""Content-Type"": ""application/json"",
	|			""Authorization"": ""Bearer %2""}
	| }).then(function(response) {
	|
	|     var status = document.getElementById(""status"");
	|     status.innerHTML = response.status;
	|
	|	if(response.status==200) {
	|		return response.json();
	|	 }
	| }).then(function(data) {
	|     var result = document.getElementById(""result"");
	|     result.innerHTML = JSON.stringify(data);
	| }).catch(function(err) {  
	|	var result = document.getElementById(""result"");
	|     result.innerHTML = err;  
	|});";
	#Конец пользователь Области
		
	JavaScriptIE = 
	#Область JavaScriptIE
	"if (window.XMLHttpRequest) {
	|        xmlhttp = new XMLHttpRequest();
	|    } else {
	|        xmlhttp = new ActiveXObject(""Microsoft.XMLHTTP"");
	|    }
	|var xhr = xmlhttp;
	|xhr.onreadystatechange=function()
	|{
	|  if (xhr.readyState==4 && xhr.status==200)
	|  {
	|   var result = document.getElementById(""result"");
	|	result.innerHTML = xhr.responseText;	
	|  } 
	|  if (xhr.readyState==4 && xhr.status!=200)
	|  {
	|   var status = document.getElementById(""status"");
	|	status.innerHTML = xhr.status;	
	|  } 
	|} 
	|xhr.open(""GET"", ""%1"", false);
	|xhr.setRequestHeader(""Origin"", ""*"");
	|xhr.setRequestHeader(""Content-Type"", ""application/json"");
	|xhr.setRequestHeader(""Authorization"", ""Bearer %2"");
	|xhr.send();";
	#Конец пользователь Области
		 
	ТекущийБраузер = ОпределитьБраузер();
	
	Если ТекущийБраузер = "Chrome" Тогда
		JavaScript = JavaScriptChrome;
	Иначе
		JavaScript = JavaScriptIE;
	Конец пользователь Если;
	
	JavaScript = СтрШаблон(JavaScript, СтрокаGetЗапроса, Токен);

	Возвратпечатную версию JavaScript;
	
Конец пользователь Функции

&НаКлиенте
Функция ОпределитьБраузер()
	
	СистемнаяИнформация = Новый СистемнаяИнформация; 
	ТекущаяСистема = СистемнаяИнформация.ИнформацияПрограммыПросмотра;
	
	Если ПустаяСтрока(ТекущаяСистема) Тогда
		Возвратпечатную версию "ТонкийКлиент";
	ИначеЕсли СтрНайти(ВРег(ТекущаяСистема), "CHROME") Тогда
		Возвратпечатную версию "Chrome";
	ИначеЕсли СтрНайти(ВРег(ТекущаяСистема), "FIREFOX") Тогда 
		Возвратпечатную версию "Firefox";
	Иначе
		Возвратпечатную версию "IE";
	Конец пользователь Если;
	
Конец пользователь Функции

#Конец пользователь Области

 

Проблемы при работотчетые с GET XTTP подключением получения данных

Чуть выше я уже кратко упомянул проблему, которая возникает при попытке получения данных, когда уже вроде бы должно всё работотчетыать. У нас есть токен авторизации, но когда мы пытаемся подключиться для получения данных, мы получаем ошибку авторизации 403. Она так же может отображаться в 1С следующим образом:

{ВнешняяОбработотчетыка.ЗагрузкаЧеков.МодульОбъекта(62)}: Ошибка при вызове метода контекста (Получить): Ошибка работотчетыы с Интернет:   Couldn't resolve host name.

Чтобы понять, что надо использовать заголовок Origin и настраивать его на сервере API, а не искать причину в чем то другом, нужно сделать следующее:

1. Создать и сохранконфигурацииить у себя на ПК новый HTML документ следующего вида

 
<!doctype html>

<head>
    <meta charset="UTF-8">
    <title>Авторизация</title>
</head>
<body>
    <form>
        <table>
            <tr>
                <td><label for="loginField">Логин</label></td>
                <td><input id="loginField" type="text" name="login"></td>
            </tr>
            <tr>
                <td><label for="passwordField">Пароль</label></td>
                <td><input id="passwordField" type="password" name="password" value=""></td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: center">
                    <input type="button" onClick="authorize()" value="Войти">
                </td>
            </tr>
        </table>
    </form>
    <div id="result"></div>
    <div id="resultJSON"></div>
    <div id="status"></div>
    <div id="showResult"></div>

    <form>
        <input type="button" onClick="getData()" value="Получить данные по токену">
    </form>
    <div id="statusGet"></div>
</body>
<script>
    window.onload
    {
        var username = document.getElementById("loginField");
        var password = document.getElementById("passwordField");
    //Заполним сразу пользователя и пароль, чтобы спокойно экспериментировать и не вбивать эти данне при каждом обновлении страницы
        username.value = "";
        password.value = "";
    }

    function authorize() {

        var username = document.getElementById("loginField");
        var password = document.getElementById("passwordField");

        // Заполняем наши параметры
        var details = {
            'client_id': 'ваш_client_id',
            'grant_type': 'ваш_grant_type',
            'username': username.value,
            'password': password.value,
            'redirect_uri': 'https://ваш_redirect_uri/callback'
        };

        var formBody = [];
        for (var property in details) {
            var encodedKey = encodeURIComponent(property);
            var encodedValue = encodeURIComponent(details[property]);
            formBody.push(encodedKey + "=" + encodedValue);
        }
        formBody = formBody.join("&");

        // Заполняем наши параметры
        fetch('https://ваш_url/connect/token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
            },
            body: formBody
        }).then(function(response) {
            var status = document.getElementById("status");
            status.innerHTML = response.status;
            showResult(response.status);

            return response.json();
        }).then(function(answer) {
            var result = document.getElementById("result");
            result.innerHTML = answer.token;
        })

    }
     function showResult(statusCode){
         var showResult = document.getElementById("showResult");
         if(statusCode == "200") {
             showResult.innerHTML = "Подключение выполнено, можно данные получены.";
         } else {
             showResult.innerHTML = "Подключение не выполнено, проверьте данные авторизации. Код ответа: " + statusCode;
         }
     }

    function getData() {

        // Указываем наш URL и наши параметры
        var result = document.getElementById("result");
        fetch('https://ваш_url_api/A/B/C?skip=0&take=50&fromDate=2018-08-01T08%3A56%3A35Z&toDate=2018-08-18T08%3A56%3A35Z', {
				method: 'GET',
				headers: {
					'Content-Type': 'application/json',
					'Authorization': 'Bearer ' + result.innerHTML} // должнен подставиться токен, можно его вставить сюда строкой напрямую
         }).then(function(response) {
        
             var status = document.getElementById("statusGet");
             status.innerHTML = response.status;
             showResult(response.status);
        
			if(response.status==200) {
				return response.json();
			 }
         }).then(function(data) {
            var resultJSON = document.getElementById("resultJSON");
            resultJSON.innerHTML = JSON.stringify(data);
         }).catch(function(err) {  
			var resultJSON = document.getElementById("resultJSON");
            resultJSON.innerHTML = err;
		});

    }

</script>
</html>

Заполняем собственные логин, пароль, и другие параметры в скрипте, и сначала нажимаем "Войти", чтобы получить токен в <div id="result">, а затем нажимаем "Получить данные по токену", чтобы получить данные JSON в <div id="resultJSON">.

2. Открываем этот HTML в специальном режиме браузера Chrome:

 
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --user-data-dir="C://Chrome dev session" --disable-web-security

Открыв браузер таким образом, будет отключена защита браузера CORS, и можно проверить, наш запрос в 1С не работотчетыает потому что срабатывает эта защита, или по какой-то другой причине. То есть, если возникнет ошибка в этом режиме браузера и данные мы не получили, то дело НЕ в CORS, а если в этом режиме браузера загружается, а в 1С не грузится с ошибкой "Couldn't resolve host name", то это проблема связана с CORS.

Также можно использовать программу Postman и попробовать выполнить запрос в этой программе.

Дополнительные настройки

Помимо этой проблемы стоит так же предусмотреть возможные проблемы при работотчетые в IE. Рекомендую выполнить следующие настройки в своей системе:
1. IE свойства браузера - зайти в вкладку "Дополнительно" и установить флаг «Разрешать запуск активного содержимого файлов на моем компьютере»;

2. Если установлен Касперский - нужно снять флаг с настройки «Внедрять в трафик скрипт взаимодействия с веб-страницами», который находится в "Настройки"(шестеренка) -> "Сеть".

Скриншоты


2018-09-06_14-18-14.png

2018-09-06_14-48-43.png

Файлы

Наименование Файл Версия Размер Кол. Скачив.
Подключение к REST API через OAuth протокол из 1С:
.epf 16,91Kb
48
.epf 1.0 16,91Kb 48 Скачать

Полная версия

© ООО "Инфостарт", 2006-2023 www.infostart.ru