- PVSM.RU - https://www.pvsm.ru -
С выходом Windows Phone 8.1 появилась новая возможность в разработке приложений магазина Windows / Windows phone с общей кодовой базой. Это так называемые универсальные приложения [1], базирующиеся на более общем API и возможности повторного использования разметки XAML в Visual Studio 2013 сразу из коробки.
Если приложение магазина Windows использует WCF для работы с SOAP сервисами, то попытка портирования на Windows phone может завершиться неудачей. Как оказалось, пространство имен System.ServiceModel отныне недоступно. Соответственно требуется замена, отвечающая следующим требованиям:
SOAP [2] запрос представляет собой особым образом сформированный XML документ. Все что необходимо сделать это сериализовать данные запроса в XML, поместить их в элемент Body и отправить в теле HTTP POST запроса. Структура ответа аналогична, результат получается из элемента Body.
На основе 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 сервиса в виде операций и типов данных.
К получению информации о сервисе можно подойти с разных сторон:
Исходный код полученный при помощи 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/
Нажмите здесь для печати.