Нюансы разработки веб сервисов для 1С на C# (.NET)

Я уже как-то писал о муках разработки web service (веб сервисов) на C# (.NET) для . Продолжаем разбираться с проблемами.

При разработке веб сервиса на C# под MS Visual Studio для 1С 8.2 столкнулись с невразумительной ошибкой. В качестве сервера — Microsoft-IIS/8.5. Поиск решения занял немало времени. 🙁 Но обо всем по порядку.

Название вызываемого метода веб сервиса — BarcodeFontIsInstalled. В качестве единственного аргумента передается строка.  Ошибка выглядит так:

{Форма.Форма.Форма(22)}: Ошибка при вызове метода контекста (BarcodeFontIsInstalled)
НоваяСтрока = Прокси.BarcodeFontIsInstalled(WSПараметр);
по причине:
Ошибка вызова операции сервиса: {http://tempuri.org/}:BarcodeWebService:BarcodeFontIsInstalled()
по причине:
Неизвестная ошибка. Ошибка разбора 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
{
    [WebService(Namespace = "http://tempuri.org/")]
    [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);
        }
    }
}

1C_web_service

Замечу, что вызов проходил через 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:

POST http://server/webservice/barcodewebservice.asmx HTTP/1.1
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4....==
User-Agent: 1C+Enterprise/8.2
Host: server
Accept: */*
Connection: Keep-Alive
SOAPAction: "http://tempuri.org/BarcodeFontIsInstalled"
Content-Type: text/xml; charset=utf-8
Content-Length: 427

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Header/>
	<soap:Body> <m:BarcodeFontIsInstalled xmlns:m="http://tempuri.org/">
	<m:fontName xmlns:xs="http://www.w3.org/2001/XMLSchema"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xsi:type="m:BarcodeFontIsInstalled">
		<m:fontName>Code 128</m:fontName>
	</m:fontName>
</m:BarcodeFontIsInstalled></soap:Body>
</soap:Envelope>

Бросается в глаза странный кусок XML:

<m:fontName xmlns:xs="http://www.w3.org/2001/XMLSchema"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			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 получается:

POST http://server/webservice/barcodewebservice.asmx HTTP/1.1
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
SOAPAction: "http://tempuri.org/BarcodeFontIsInstalled"
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>
		<BarcodeFontIsInstalled xmlns="http://tempuri.org/">
			<fontName>Code 128</fontName>
		</BarcodeFontIsInstalled>
	</soap:Body>
</soap:Envelope>

Расстраиваемся, поскольку ситуация выглядит тупиковой, но пытаемся найти решение.

Перехват обращений из 1С к web service (веб сервису) cbr.ru

Обратимся к исходной статье и посмотрим как выглядит request к web service (веб сервису) www.cbr.ru. Код 1С описан в статье, а request будет таким:

POST http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx HTTP/1.1
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gUA...
User-Agent: 1C+Enterprise/8.2
Host: www.cbr.ru
Accept: */*
Connection: Keep-Alive
SOAPAction: "http://web.cbr.ru/GetCursOnDate"
Content-Type: text/xml; charset=utf-8
Content-Length: 337

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Header/>
	<soap:Body> <GetCursOnDate xmlns="http://web.cbr.ru/"
		xmlns:xs="http://www.w3.org/2001/XMLSchema"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<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С к веб сервису выглядит следующим образом:

POST http://server/webservice/barcodewebservice.asmx HTTP/1.1
Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gUABQ...
User-Agent: 1C+Enterprise/8.2
Host: server
Accept: */*
Connection: Keep-Alive
SOAPAction: "http://tempuri.org/BarcodeFontIsInstalled"
Content-Type: text/xml; charset=utf-8
Content-Length: 355

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
	<soap:Header/>
	<soap:Body> <m:BarcodeFontIsInstalled xmlns:m="http://tempuri.org/">
	<m:fontName xmlns:xs="http://www.w3.org/2001/XMLSchema"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Code 128</m:fontName>
</m:BarcodeFontIsInstalled></soap:Body>
</soap:Envelope>

При таком вызове web service (веб сервис) возвращает корректное значение. Ошибка исчезла. В качестве аргумента в метод веб сервиса из 1С передавали бинарные данные (фотографии), массивы, ну и простые типы, вроде даты, строки и пр.

Наслаждайтесь! 🙂

Опубликовать в Facebook
Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники
Опубликовать в Яндекс
Запись опубликована в рубрике IT рецепты с метками , , , , , . Добавьте в закладки постоянную ссылку.