- PVSM.RU - https://www.pvsm.ru -

Работа с SOAP сервисом из Windows Phone 8.1 приложения

С выходом Windows Phone 8.1 появилась новая возможность в разработке приложений магазина Windows / Windows phone с общей кодовой базой. Это так называемые универсальные приложения [1], базирующиеся на более общем API и возможности повторного использования разметки XAML в Visual Studio 2013 сразу из коробки.

Если приложение магазина Windows использует WCF для работы с SOAP сервисами, то попытка портирования на Windows phone может завершиться неудачей. Как оказалось, пространство имен System.ServiceModel отныне недоступно. Соответственно требуется замена, отвечающая следующим требованиям:

  • Простота использования, аналогично старому Add Service Reference, где на выходе получается сгенерированный код строго типизированного клиента сервиса;
  • Расширяемость, как показала практика может потребоваться поддержка различных схем аутентификации.


SOAP [2] запрос представляет собой особым образом сформированный XML документ. Все что необходимо сделать это сериализовать данные запроса в XML, поместить их в элемент Body и отправить в теле HTTP POST запроса. Структура ответа аналогична, результат получается из элемента Body.

Работа с SOAP сервисом из Windows Phone 8.1 приложения

На основе HttpClient [3] реализуем базовый класс для формирования запроса. В результате получаем функцию следующего вида:

public async Task<TResponse> CallAsync<TRequest, TResponse>(string action, TRequest request)
{
	IHttpContent httpContent = GetHttpContent(action, request);
	var response = await Client.PostAsync(EndpointAddress, httpContent);
	var responseContent = await response.Content.ReadAsStringAsync();
	return GetResponse<TResponse>(responseContent);
}

Остается только подставить соответствующие классы TRequest и TResponse которые можно получить на основе описания сервиса. SOAP сервисы описываются при помощи Web Services Description Language (WSDL [4]), языка, описывающего API сервиса в виде операций и типов данных.

К получению информации о сервисе можно подойти с разных сторон:

  • Работа с сырым WSDL. Можно, но для поставленной задачи слишком трудоемко, пойдем лучше по пути наименьшего сопротивления;
  • Использовать класс System.ServiceModel.Description.MetadataExchangeClient [5], это самый разумный вариант, однако реализация этого класса не позволяет работать с любым WSDL. Если в документе содержится элемент import [6], то MetadataExchangeClient вываливается с ошибкой;
  • Использовать консольную утилиту SvcUtil [7] входящую в состав .Net Framework SDK. На выходе мы получим тот же исходный код что и при использовании Add Service Reference из Visual Studio, который и возьмем за основу.

Исходный код полученный при помощи SvcUtil не может напрямую использоваться для приложений магазина Windows Phone по причине наличия следующих не поддерживаемых конструкций:

  • Клиент сервиса на основе System.ServiceModel.ClientBase<T>. Использовать нельзя, но можно взять за основу интерфейс сервиса.
  • Атрибутов System.SerializableAttribute и System.ComponentModel.DesignerCategoryAttribute. Эти аттрибуты не поддерживаются, удаляем.
  • Свойств public System.Xml.XmlElement[] Any { get; set; }. Меняем тип на System.Xml.Linq.XElement[].

Преобразование кода производится при помощи Reflection + CodeDom. В итоге получаем реализацию классов TRequest и TRespone совместимых с платформой. Осталось только сгенерировать клиент сервиса реализующий соответствующий интерфейс. Всю необходимая для этого информация так же получается из исходного файла.

В результате, путем не хитрых манипуляций можно сгенерировать клиент для любого сервиса. В дополнении к этому API сервиса совпадает с тем что было получено при использовании WCF. Неизменность API позволяет подменить клиент сервиса без редактирования существующего кода.

Расширяемость этого решения основана на возможности использования конструктора класса HttpClient, принимающего параметр IHttpFilter [8], за счет которого достаточно легко реализовать шаблон проектирования Декоратор [9]. Таким образом, можно не затрагивая существующее API работать с HTTP заголовками, например, чтобы реализовать Digest аутентификацию, так и модифицировать SOAP запрос перед отправкой чтобы добавить требуемые параметры в элемент Header.

private static HttpClient GetClient(PasswordCredential cred, TimeSpan? timeDiff)
{
	var baseFilter = new HttpBaseProtocolFilter
	{
		AllowUI = false,
	};

	if (cred == null)
		return new HttpClient(baseFilter);

	var httpDigestHttpFilter = new HttpDigestHttpFilter(baseFilter)
	{
		UserName = cred.UserName, 
		Password = cred.Password
	};
			
	var soapDigestHttpFilter = new SoapDigestHttpFilter(httpDigestHttpFilter)
	{
		UserName = cred.UserName,
		Password = cred.Password,
		TimeDiff = timeDiff
	};

	return new HttpClient(soapDigestHttpFilter);
}

Приложения магазина Windows менее обделены функционалом чем Windows Phone и сохранили некоторое подмножество WCF [10]. Однако и тут можно столкнуться с проблемой, не позволяющей воспользоваться существующим функционалом. Проблема эта заключается в отправке с каждым запросом заголовка Expect: 100-Continue. Некоторые сервисы категорично отвечают на это ошибкой 417 Expectation Failed. В полной версии .Net Framework это решается сбросом флага Expect100Continue:

System.Net.ServicePointManager.Expect100Continue = false;

В .Net для приложений магазина Windows это свойство отсутствует. При использовании HttpClient это ограничение можно обойти и использовать полученную реализацию в том числе и для приложений магазина Windows.

Генерация исходного кода производится получившейся консольной утилитой в следующем формате:

SoapClientGenerator.exe <metadataDocumentPath> <file> <namespace> [/svcutil:<svcutilPath>]

Где:

  • <metadataDocumentPath> — путь к WSDL
  • <file> — путь к выходному файлу
  • <namespace> — пространство имен выходного файла
  • /svcutil:<svcutilPath> — необязательный параметр, путь к svcutil, по умолчанию C:Program Files (x86)Microsoft SDKsWindowsv8.1AbinNETFX 4.5.1 ToolsSvcUtil.exe

Исходный код доступен на GitHub [11].

Автор: Ne4to

Источник [12]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/soap/67811

Ссылки в тексте:

[1] универсальные приложения: https://dev.windows.com/ru-ru/develop/Building-universal-Windows-apps

[2] SOAP: http://www.w3.org/TR/soap/

[3] HttpClient: http://msdn.microsoft.com/ru-ru/library/windows/apps/xaml/windows.web.http.httpclient.aspx

[4] WSDL: http://www.w3.org/TR/wsdl

[5] System.ServiceModel.Description.MetadataExchangeClient: http://msdn.microsoft.com/ru-ru/library/system.servicemodel.description.metadataexchangeclient(v=vs.110).aspx

[6] import: http://www.w3.org/TR/wsdl#_document-n

[7] SvcUtil: http://msdn.microsoft.com/ru-ru/library/aa347733(v=vs.110).aspx

[8] IHttpFilter: http://msdn.microsoft.com/ru-ru/library/windows/apps/xaml/windows.web.http.filters.ihttpfilter.aspx

[9] Декоратор: https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BA%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)

[10] подмножество WCF: http://msdn.microsoft.com/en-us/library/hh556233.aspx

[11] GitHub: https://github.com/Ne4to/SoapClient

[12] Источник: http://habrahabr.ru/post/233799/