Я уже как-то писал о муках разработки web service (веб сервисов) на C# (.NET) для 1С. Продолжаем разбираться с проблемами.
При разработке веб сервиса на C# под MS Visual Studio для 1С 8.2 столкнулись с невразумительной ошибкой. В качестве сервера — Microsoft-IIS/8.5. Поиск решения занял немало времени. 🙁 Но обо всем по порядку.
Название вызываемого метода веб сервиса — BarcodeFontIsInstalled. В качестве единственного аргумента передается строка. Ошибка выглядит так:
{Форма.Форма.Форма(22)}: Ошибка при вызове метода контекста (BarcodeFontIsInstalled) НоваяСтрока = Прокси.BarcodeFontIsInstalled(WSПараметр); по причине: по причине: Неизвестная ошибка. Ошибка разбора XML: - [1,1] Фатальная ошибка: Extra content at the end of the document по причине: Ошибка разбора XML: - [1,1] |
Она ни о чем не говорит, поскольку 1С безбожно переврала то, что вернул IIS.
Обращение из 1С к web service (веб сервису)
Вызов метода из 1С производился как описано в статье. Код веб сервиса в C#:
using System.Web; using System.Web.Services; using System.Xml.Serialization; namespace BarcodeWebService { [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem( false )] public class BarcodeWebService : System.Web.Services.WebService { [WebMethod] public bool BarcodeFontIsInstalled( string fontName) { return BarcodeUtils.IsFontInstalled(fontName); } } } |
Замечу, что вызов проходил через BarcodeWebServiceSoap — т.е. SOAP версии 1.1. BarcodeWebServiceSoap12 — соотвественно для работы через SOAP 1.2. 1C действительно меняет обращение к сервису, если вместо BarcodeWebServiceSoap указать BarcodeWebServiceSoap12, проверено опытным путем. 🙂
Вызов в 1С выглядел таким образом:
//Создаем прокси для обращения к внешнему веб-сервису, // передаем в функцию URI пространства имен, имя сервиса, имя порта. Прокси = WSСсылки.WSBarcodeService.СоздатьWSПрокси( http://tempuri.org/" , "BarcodeWebService" , "BarcodeWebServiceSoap" ); //Получаем тип параметра, который передается в метод BarcodeFontIsInstalled. ТипWSПараметра = Прокси.ФабрикаXDTO.Пакеты.Получить( http://tempuri.org/" ).Получить( "BarcodeFontIsInstalled" ); //Создаем параметр на основе типа и заполняем значение параметра fontName. WSПараметр = Прокси.ФабрикаXDTO.Создать(ТипWSПараметра); WSПараметр.fontName = "Code 128" ; //Вызываем метод веб-сервиса, записываем результат в переменную КурсыВалют. НоваяСтрока = Прокси.BarcodeFontIsInstalled(WSПараметр); |
Все делаем «по учебнику» но так не работает. 🙁
Отладка обращений 1С к web service (веб сервису)
Поскольку в 1С не понятно как отлаживать что-то в части работы с веб-сервисами, обращаемся за помощью к Fiddler, который покажет как-же 1С обращается к разработанному на C# web service:
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4....== User-Agent: 1C+Enterprise/8.2 Host: server Accept: */* Connection: Keep-Alive Content-Type: text/xml; charset=utf-8 Content-Length: 427 < soap:Header /> xsi:type = "m:BarcodeFontIsInstalled" > < m:fontName >Code 128</ m:fontName > </ m:fontName > </ m:BarcodeFontIsInstalled ></ soap:Body > </ soap:Envelope > |
Бросается в глаза странный кусок XML:
xsi:type = "m:BarcodeFontIsInstalled" > < m:fontName >Code 128</ m:fontName > </ m:fontName > |
Зачем 1С сгенерировал для такого простого аргумента такую вложенную иерархию fontName не понятно. Ответ веб сервиса (MS IIS) на такую конструкцию:
HTTP/1.1 400 Bad Request Cache-Control: private Content-Type: text/xml; charset=utf-8 Server: Microsoft-IIS/8.5 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Wed, 14 Oct 2015 10:19:14 GMT Content-Length: 0 |
Как видно по ответу, ошибка выдаваемая 1С рядом не лежала с тем, что на самом деле вернул IIS, а он вообще не вернул XML, сообщив, что запрос «кривой» (HTTP/1.1 400 Bad Request).
Чтобы узнать какой request должен быть, пишем пару строк на C#:
BarcodeWebService.BarcodeWebService svc = new BarcodeWebService.BarcodeWebService(); checkBox1.Checked = svc.BarcodeFontIsInstalled( "Code 128" ); |
и запустив Fiddler видим какой простой и красивый request получается:
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.34014) Content-Type: text/xml; charset=utf-8 Host: server Content-Length: 352 Expect: 100-continue Proxy-Connection: Keep-Alive <? xml version = "1.0" encoding = "utf-8" ?> < soap:Envelope xmlns:soap = http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi = http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd = http://www.w3.org/2001/XMLSchema" > < soap:Body > < fontName >Code 128</ fontName > </ BarcodeFontIsInstalled > </ soap:Body > </ soap:Envelope > |
Расстраиваемся, поскольку ситуация выглядит тупиковой, но пытаемся найти решение.
Перехват обращений из 1С к web service (веб сервису) cbr.ru
Обратимся к исходной статье и посмотрим как выглядит request к web service (веб сервису) www.cbr.ru. Код 1С описан в статье, а request будет таким:
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gUA... User-Agent: 1C+Enterprise/8.2 Host: www.cbr.ru Accept: */* Connection: Keep-Alive Content-Type: text/xml; charset=utf-8 Content-Length: 337 < soap:Header /> < On_date >2015-10-14T14:40:53</ On_date > </ GetCursOnDate ></ soap:Body > </ soap:Envelope > |
В данном случае все красиво, как при обращении из кода C#:
CBRWebService.DailyInfo info = new CBRWebService.DailyInfo(); DataSet ds = info.GetCursOnDate(DateTime.Now); |
Разбираться в WSDL, который генерирует веб сервис «Центробанка» и разработанный мною на .NET — дело неблагодарное, тем более, что даже поняв ошибку в генерации 1С request к внешнему веб сервису, повлиять на это нельзя, нужно ждать обновления платформы. Чтобы проверить одну мысль я сделал простой метод в веб сервисе:
[WebMethod] public DateTime Checker(DateTime date) { return date; } |
У меня была мысль, что проблема связана с тем, что string аргумент может быть null, соответственно, в WSDL minOccurs=»0″, в отличие от DateTime (который передавался cbr.ru методу GetCursOnDate), для которого minOccurs=»1″ . Однако, даже для такого варианта 1С сформировал столь же «кривой» запрос, т.е. дело не в этом.
Решение
Поиск решения заняло немало времени, как обычно, оно оказалось очень простым. Спасибо моим 1С разработчикам с которыми мы вымучивали столь красивое и столь многострадальное решение. 🙂
Итак, фанфары, рабочий код на 1С при обращении к внешнему веб сервису (web service) написанному на C# (.NET) и работающему под IIS выглядит следующим образом:
//Создаем прокси для обращения к внешнему веб-сервису, // передаем в функцию URI пространства имен, имя сервиса, имя порта. Прокси = WSСсылки.WSBarcodeService.СоздатьWSПрокси( http://tempuri.org/" , "BarcodeWebService" , "BarcodeWebServiceSoap" ); //Вызываем метод веб-сервиса, записываем результат в переменную КурсыВалют. Результат = Прокси.BarcodeFontIsInstalled( "Code 128" ); |
И это всё! 🙂 Предельно кратко и очень похоже на обращение к web service в C#. В этом случае перехваченный Fiddler-ом запрос 1С к веб сервису выглядит следующим образом:
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gUABQ... User-Agent: 1C+Enterprise/8.2 Host: server Accept: */* Connection: Keep-Alive Content-Type: text/xml; charset=utf-8 Content-Length: 355 < soap:Header /> </ m:BarcodeFontIsInstalled ></ soap:Body > </ soap:Envelope > |
При таком вызове web service (веб сервис) возвращает корректное значение. Ошибка исчезла. В качестве аргумента в метод веб сервиса из 1С передавали бинарные данные (фотографии), массивы, ну и простые типы, вроде даты, строки и пр.