Имя: Пароль:
1C
1C 7.7
v7: Отправка СМС из 1С через SMS-Fly
,
0 Anempadest
 
11.06.15
14:18
Решаю задачу по отправке СМС из 1С. Использую API сервиса SMS-Fly (Украина).
Всё уже написал (соединение, отправка запроса XML, разбор ответа), но  есть одно препятствие: не могу авторизироваться.

От службы поддержки получил пример на PHP:
***
Если коротко, то алгоритм действий для разных языков одинаков.
1) Открывается сокет на наш ресурс на 80 порт (стандартный порт http протокола);
2) Формируется XML
       $data          = "<?xml version=\"1.0\" encoding=\"utf-8\"?><request><operation>GETBALANCE</operation></request>";
3) Формируются заголовки для передаваемых данных. в PHP это выглядит
       $headers = "POST $path HTTP/1.1\r\n";  
       $headers .= "Host: $hostname\r\n";  
       $headers .= 'Authorization: Basic '.base64_encode($user.':'.$pwd)."\r\n";          
       $headers .= "Content-type: text/xml\r\n";  
       $headers .= "Content-Length: ".strlen($data)."\r\n\r\n";  
4) данные записываются в открытое соединение.
5) читаются данные из открытого соединения для получения ответа от сервера.
6) полученные данные анализируются приложением, отслеживается результат выполнения команды.
***

Я не большой специалист по HTTP протоколу и отправке POST запросов, поэтому с трудом понимаю написанное. Написал так:
***
Анализатор=СоздатьОбъект("AddIn.XMLParser");
//соединение
Соединение=СоздатьОбъект("Addin.V7HttpReader");
Соединение.КоличествоПопытокАвторизации=3;
//заголовок
Заголовок=
"POST http://sms-fly.com/api/api.php HTTP/1.1
|Host: http://sms-fly.com
|Authorization: Basic "+СокрЛП(Рассылка.Логин)+":"+СокрЛП(Рассылка.Пароль)+"
|Content-type: text/xml
|Content-Length: 1000";
//отправка заголовка
Приемник="";
Соединение.ОтправитьДляОбработки(Рассылка.Адрес,Заголовок,2,Приемник,2);
***
В переменную приёмник получаю ответ "HTTP/1.1 401 Unauthorized", то есть авторизация не выполнена.
Уже не знаю, что делать. Поможете?
1 EverGreenMouse
 
11.06.15
14:27
|Authorization: Basic "+СокрЛП(Рассылка.Логин)+":"+СокрЛП(Рассылка.Пароль)+"
не тут ли косяк?
Может так?
|Authorization: Basic "СокрЛП(Рассылка.Логин)+":"+СокрЛП(Рассылка.Пароль)"
2 Anempadest
 
11.06.15
14:29
(1) Я же создаю текстовую строку с разделителями строк.
3 AMKahm
 
11.06.15
14:47
А где конвертация в base64 в вашем коде?
типа

Authorization: Basic "+base64_encode(СокрЛП(Рассылка.Логин)+":"+СокрЛП(Рассылка.Пароль))
4 Anempadest
 
11.06.15
14:58
(3) Да, ТП написала про конвертацию. А оно обязательно?
Логин - это номер телефона, то есть numeric. А пароль состоит из латинских символов.
Мне кажется, перекодировать ничего не нужно. Или я ошибаюсь?
5 AMKahm
 
11.06.15
15:01
Ошибаетесь. Вот - посмотрите здесь:
http://ostermiller.org/calc/encode.html
6 AMKahm
 
11.06.15
15:02
login:pass
кодируются в
bG9naW46cGFzcw==
7 Anempadest
 
11.06.15
15:04
(8) Я понял. Спасибо. Сейчас буду делать.
Простое кодирование и декодирование base64
8 Anempadest
 
11.06.15
21:35
Хорошие новости.
[code]
Анализатор=СоздатьОбъект("AddIn.XMLParser");
//соединение
Соединение=СоздатьОбъект("Addin.V7HttpReader");
Соединение.КоличествоПопытокАвторизации=3;
//кодирование
Проход=СокрЛП(Рассылка.Логин)+":"+СокрЛП(Рассылка.Пароль);
Если гКодировать(Проход,"base64")=0 Тогда
    Возврат(0);
КонецЕсли;
//создание
Файл=КаталогВременныхФайлов()+"sms_send.xml";
Смс_Создать(Анализатор,Файл);
//тело
Текст=гСодержимоеФайла(Файл);
//заголовок
Заголовок=
"POST http://sms-fly.com/api/api.php HTTP/1.1\r\n
|Host: http://sms-fly.com\r\n
|Authorization: Basic "+Проход+"\r\n
|Content-Type: text/xml\r\n
|Content-Length: "+СтрДлина(Текст)+"\r\n\r\n";
//отправка
Приемник=КаталогВременныхФайлов()+"sms_send_а.xml";
Соединение.ОтправитьДляОбработки(Рассылка.Адрес,Заголовок+Текст,2,Приемник,1);
[/code]
Удалось авторизоваться на сайте. Но желаемый результат не достигнут.
В файл Приемник сервер возвращает просто Главную страницу сайта, а должен вернуть ответ в формате XML.
Есть какие-то идеи?
9 Garykom
 
гуру
11.06.15
22:10
(8) сменить оператора рассылки смс

ЗЫ это послать по адресу...
10 Garykom
 
гуру
11.06.15
22:10
(9)+ у нормальных даже готовые примеры для разных 1С выложены..
11 Anempadest
 
12.06.15
03:38
(10) Истину говоришь, товарищ!
Но у этой конторы - самая низкая стоимость СМС в Украине: 0,159 грн.
12 Anempadest
 
12.06.15
14:03
Работающий код:
<code>
Соединение=СоздатьОбъект("WinHttp.WinHttpRequest.5.1");
Соединение.Option(2,"utf-8");
Соединение.Open("POST","http://sms-fly.com/api/api.php",0);
Соединение.SetRequestHeader("Authorization","Basic "+Ключ);
Соединение.SetRequestHeader("Content-Type","text/xml");
Соединение.SetRequestHeader("Content-Length",СтрДлина(Запрос));
Соединение.Send(Запрос);
</code>
13 Anempadest
 
12.06.15
14:42
Внешнюю компоненту V7PLUS для отправки данного запроса использовать не получилось. Сервер не авторизует соединение, то есть метод не работает:
Соединение.УстановитьЗаголовокЗапроса("Authorization","Basic "+Ключ);
14 oleg_km
 
12.06.15
16:38
(12) Вообще-то метод SetCredentials должен это вроде делать.
15 Anempadest
 
12.06.15
17:10
(14) Присмотритесь внимательней к коду. Сервер просит отправить Логин и Пароль в закодированном виде.
'Authorization: Basic '.base64_encode($user.':'.$pwd)."\r\n";
16 Serginio1
 
12.06.15
18:54
17 oleg_km
 
12.06.15
19:53
(15) А может теорию почитаешь? В протоколе HTTP есть авторизация Basic, Digest, NTLM, SSL. Как правило, разработчики API уже реализуют эту реализацию на уровне своего API. Ты лучше попробуй
18 Anempadest
 
13.06.15
02:24
(17) Только ради вас попробовал. И получил ошибку:
Соединение.SetCredentials(Логин,Пароль);
{Обработка.Извещение.Форма.Модуль(272)}: WinHttp.WinHttpRequest: Нельзя вызвать этот метод до вызова метода Send

Мне специалист из СП дал чёткие инструкции, что нужно использовать заголовок:
'Authorization: Basic '.base64_encode($user.':'.$pwd)."\r\n";
19 Serginio1
 
13.06.15
10:03
(18) Ты бы 16 почитал внимательно
20 Serginio1
 
13.06.15
10:24
Там пример на JavaScript вполне читаемый. Дело в том, что при отсутствии авторизации сервер возвращает еще и какую авторизацию он хочет
21 Anempadest
 
13.06.15
14:45
(20) Прости, что я тебя не понимаю, но мне кажется, что ты меня троллишь.
Смотри. Эта служба просит у меня послать запрос для отправки СМС. Для авторизации на сервере нужно в заголовке запроса разместить переменную "Authorization" и значение. Обрати внимание, что значение переменной - это не просто строка "Логин:Пароль". Эта строка должна быть ещё закодирована по протоколу Base64. Понял? В заголовке есть переменная и значение. И только так. По-другому работать не будет. Если бы работало, то Служба Поддержки бы этого не скрывала.
Не согласен?
22 Serginio1
 
13.06.15
15:44
Какой смысл мне тебя троллить? Почитай про авторизацию например http://samag.ru/archive/article/1631

Обычно авотризация отдается на откуп Api. Например в Net

https://msdn.microsoft.com/ru-ru/library/system.net.networkcredential(v=vs.110).aspx

Класс NetworkCredential является базовым классом, который поддерживает учетные данные в схемах аутентификации на основе пароля, таких как "Обычная", "Дайджест", "NTLM" и "Kerberos". Классы, реализующие интерфейс ICredentials, например класс CredentialCache, возвращают объекты NetworkCredential.

Ты не заботишься какую авторизацию поддерживает сервер и как данные авторизации нужно записать.
23 Serginio1
 
13.06.15
15:57
И если вдруг на сервере изменят тип авторизации тебе не нежно будет править код
24 ДенисЧ
 
13.06.15
16:58
Кто о чём, а лыс^W Serginio1 о дотнете....
25 Serginio1
 
13.06.15
17:49
(24) А что в этом плохого? Так или иначе он использует не родные компоненты. Я же ему не предлагаю v8: v8: Использование сборок .NET в 1С 7.x и 8.x

Просто даю понять как правильно использовать инструменты.

Просто в Net куча библиотек на все случаи жизни. Что тебя так коробит?
26 oleg_km
 
13.06.15
18:06
(25) "Православные 1сники все остальное считают ересью
27 vladimirmir2012
 
13.06.15
18:12
(26) Да нет просто 1С прекрасно интегрирована с automation и не сложно написать ВК, использующую .Net.
Отсюда и приверженность к .Net /в частности/.
28 oleg_km
 
13.06.15
18:21
(27) Вот и непонятно, почему многим упоминание дотнета вызывает ассоциацию чуть ли не с ассемблером
29 vladimirmir2012
 
13.06.15
18:24
(28) Так для 1С .Net и есть своего рода ассемблер
30 Serginio1
 
13.06.15
22:26
(29) Да нет ассемблер это низкоуровневый код которым пользуются гики. C# уже давно один из самых распространенных инструментов. Да и ВК давно уже писать не надо. Посмотри v8: v8: Использование сборок .NET в 1С 7.x и 8.x
В 8 ке есть подключение событий. А для 7 ки можно использовать только ВК достающий AppDispatch с передачей в нетовские сборки.
31 Anempadest
 
14.06.15
03:06
(22) Я в (18) написал, что при использовании предлагаемого тобой метода 1С выдаёт ошибку. Говорит, что сначала нужно установить связь, а только после этого использовать SetCredentials(Логин,Пароль). Если ты считаешь себя правым в теории, то докажи свою уверенность на практике и покажи, какой код нужно написать в 1С 7.7, чтобы предлагаемый тобой метод заработал.

Например, после прочтения литературы о HTTP я понял, что 1С 7.7 не способна выполнить нечто наподобие такого:
Соединение.Open("POST","http://sms-fly.com/api/api.php",0);

У неё есть только одна команда:
Соединение.ОтправитьДляОбработки(Адрес,Источник,2,Приемник,2);
И я так понимаю, что внутри 1С одновременно открывает, делает запрос POST и закрывает (?). И мне, кстати, очень жаль, что как бы встроенный функционал 1С не способен выполнить такую казалось бы популярную и простую задачу.
При этом, в 1С 8 подобная команда уже есть.
32 Serginio1
 
14.06.15
09:05
Вот пример на JavaScript

TTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0;
HTTPREQUEST_SETCREDENTIALS_FOR_PROXY = 1;

// Instantiate a WinHttpRequest object.
var WinHttpReq = new ActiveXObject("WinHttp.WinHttpRequest.5.1");

// Specify the target resource.
var targURL = "http://msdn.microsoft.com/downloads/samples/";+
              "internet/winhttp/auth/authenticate.asp";    
WinHttpReq.open("GET", targURL, false);

var Done = false;
var LastStatus=0;
do {
    // Send a request to the server and wait for a response.                              
    WinHttpReq.send();
    
    // Obtain the status code from the response.
    var Status = WinHttpReq.Status;

    switch (Status){
        // A 200 status indicates that the resource was retrieved.
        case 200:
            Done = true;
            break;
                
        // A 401 status indicates that the server
        // requires authentication.    
        case 401:
            WScript.Echo("Requires Server UserName and Password.");

            // Specify the target resource.
            WinHttpReq.open("GET", targURL, false);
                            
            // Set credentials for the server.
            WinHttpReq.SetCredentials("User Name", "Password",
                        HTTPREQUEST_SETCREDENTIALS_FOR_SERVER);

            // If the same credentials are requested twice, abort
            // the request.  For simplicity, this sample does not
            // check for a repeated sequence of status codes.
            if (LastStatus==401)
                Done = true;
            break;

        // A 407 status indicates that the proxy
        // requires authentication.    
        case 407:
            WScript.Echo("Requires Proxy UserName and Password.");
                
            // Specify the target resource.
            WinHttpReq.open("GET", targURL, false);
    
            // Set credentials for the proxy.
            WinHttpReq.SetCredentials("User Name", "Password",
                HTTPREQUEST_SETCREDENTIALS_FOR_PROXY);

            // If the same credentials are requested twice, abort
            // the request.  For simplicity, this sample does not
            // check for a repeated sequence of status codes.
            if (LastStatus==407)
                Done = true;
            break;
        
        // Any other status is unexpected.
        default:
            WScript.Echo("Unexpected Status: "+Status);
            Done = true;
            break;
    }
    
    // Keep track of the last status code.
    LastStatus = Status;
    
} while (!Done);

// Display the results of the request.
WScript.Echo(WinHttpReq.Status + "   " + WinHttpReq.StatusText);
WScript.Echo(WinHttpReq.GetAllResponseHeaders());
33 Serginio1
 
14.06.15
09:08
Из примера видно, что мы сначала посылаем  WinHttpReq.send(); и обрабатываем какой получили ответ.
Если он

case 401:
            WScript.Echo("Requires Server UserName and Password.");

            // Specify the target resource.

            WinHttpReq.open("GET", targURL, false);
                            
            // Set credentials for the server.

            WinHttpReq.SetCredentials("User Name", "Password",
                        HTTPREQUEST_SETCREDENTIALS_FOR_SERVER);

То авторизуемся.
34 Serginio1
 
14.06.15
09:31
(31) А где это в семерке Соединение.ОтправитьДляОбработки

Если тебе, что то не хватает в семерке используй Net.
v8: v8: Использование сборок .NET в 1С 7.x и 8.x
Там будет что типа
врап=СоздатьОбъект("NetObjectToIDispatch45");

    врап.УстЭтоСемерка();
request = Врап.ПолучитьТип("System.Net.WebRequest").Create ("http://sms-fly.com/api/api.php";);
            // Set the Method property of the request to POST.
            request.Method = "POST";
          
request.Credentials = Врап.СоздатьОбъект("System.Net.NetworkCredential",Логин,Пароль);          
            // Get the stream containing content returned by the server.
            dataStream = response.GetResponseStream ();
            // Open the stream using a StreamReader for easy access.
            reader = врап.СоздатьОбъект(System.IO.StreamReader",dataStream);
            // Read the content.
          responseFromServer = reader.ReadToEnd ();
35 Serginio1
 
14.06.15
09:40
Если тебе нужно послать данные то

$data          = "<?xml version=\"1.0\" encoding=\"utf-8\"?><request><operation>GETBALANCE</operation></request>";


buffer = Врап.ПолучитьТип("System.Encoding").UTF8.GetBytes(sXml);  // Tried ASCII...same result
  
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";  // tried "text/xml"... same result
    request.ContentLength = buffer.Length;
  ReqStream = request.GetRequestStream();
    ReqStream.Write(buffer, 0, buffer.Length);
    ReqStream.Close();
36 Serginio1
 
14.06.15
10:02
request.ContentType = "text/xml; encoding='utf-8'";
37 Serginio1
 
14.06.15
10:06
например этот пример легко переделать https://secure.safeshop.co.za/documentation/code/codingexample_csharp.htm
38 Anempadest
 
14.06.15
23:53
(37) В посте (12) то же, только короче. В общем, спасибо. А я потихоньку буду на 8 переходить.
39 Serginio1
 
15.06.15
00:16
(38) Восьмерка не все решает Доменная аутентификация WS.  Как?