Для устранения первой проблемы был реализован перехват сообщений от сервиса и корректировка пространства имен.
Для устранения второй проблемы просто был откорректирован автосгенеренный по WSDL файл Reference.cs.
Официально поддержка, сервисом Яндекс.Директ, приложений, разрабатываемых на C#, с использованием WCF не осуществляется.
В Яндекс.Директ приведен пример по взаимодействию с сервисом посредством формата хранения данных JSON и простого WebClient. Данный пример работает корректно, в отличие от SOAP, но имеет ряд недостатков, таких как: нужно десериализировать полученный объект самостоятельно, нет поддержки контрактов между сервисом и клиентом.
Подготовка
Документация по сервису представлена здесь.
Для работы с сервисом необходимо:
1. зарегистрироваться (для передачи в параметре аутентификации login),
2. создать клиентское приложение (для передачи в параметре аутентификации application_id),
3. получить тестовый токен (для передачи в параметре аутентификации token),
4. можно использовать для передачи в параметре аутентификации locale значение «ru».
Более подробно можно прочитать в документации к сервису, не стоит ее здесь переписывать.
Создание приложения
1. Создаем новое приложение в MS Visual Studio.
2. Добавляем новую ссылку на сервис, указываем wsdl: «api.direct.yandex.ru/wsdl/v4», namespace, например, YandexAPIService (ну или как Вам угодно).
3. В файле Reference.cs тип System.Nullable<System.DateTime> заменяем на System.DateTime
4. В файле Reference.cs в описании метода YandexAPIService.APIPort.GetRegions имя параметра «return» заменить на «ArrayOfRegionInfo».
5. Создать MessageInspector, в котором будут перехватываться сообщения от сервиса и заменяться «неправильные» namespace на «правильный».
using System; using System.IO; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.Xml; namespace YandexDirectRegions { // Client message inspector public class YandexDirectMessageInspector : IClientMessageInspector { public void AfterReceiveReply(ref Message reply, object correlationState) { XmlDocument doc = new XmlDocument(); MemoryStream ms = new MemoryStream(); XmlWriter writer = XmlWriter.Create(ms); reply.WriteMessage(writer); writer.Flush(); ms.Position = 0L; doc.Load(ms); foreach (XmlAttribute attr in doc.DocumentElement.Attributes) if (attr.Value == "http://namespaces.soaplite.com/perl" || attr.Value == "http://xml.apache.org/xml-soap") attr.Value = "API"; ms.Position = 0L; ms.SetLength(0); writer = XmlWriter.Create(ms); doc.WriteTo(writer); writer.Flush(); ms.Position = 0L; XmlReader reader = XmlReader.Create(ms); Message newReply = Message.CreateMessage(reader, int.MaxValue, reply.Version); newReply.Properties.CopyProperties(reply.Properties); newReply.Headers.CopyHeadersFrom(reply); reply = newReply; } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel) { return null; } } // Endpoint behavior public class YandexDirectEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { // No implementation necessary } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new YandexDirectMessageInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // No implementation necessary } public void Validate(ServiceEndpoint endpoint) { // No implementation necessary } } // Configuration element public class YandexDirectBehaviorExtension : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof(YandexDirectEndpointBehavior); } } protected override object CreateBehavior() { // Create the endpoint behavior that will insert the message // inspector into the client runtime return new YandexDirectEndpointBehavior(); } } }
6. В конфигурационном файле [app|web].config настраиваем endpoint
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="YandexDirectSoapBinding" maxReceivedMessageSize="100000"> <security mode="Transport" /> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://soap.direct.yandex.ru/v4/soap/" binding="basicHttpBinding" bindingConfiguration="YandexDirectSoapBinding" contract="YandexAPIService.APIPort" behaviorConfiguration="YandexDirectBehavior" name="APIPort" /> </client> <behaviors> <endpointBehaviors> <behavior name="YandexDirectBehavior"> <YandexDirectBehaviorExtension /> </behavior> </endpointBehaviors> </behaviors> <extensions> <behaviorExtensions> <add name="YandexDirectBehaviorExtension" type="YandexDirectRegions.YandexDirectBehaviorExtension, YandexDirectRegions" /> </behaviorExtensions> </extensions> </system.serviceModel> </configuration>
7. Реализуем вызов метода
using System; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using YandexDirectRegions.YandexAPIService; namespace YandexDirectRegions { // класс приведен здесь для примера, но по хорошему нужно его вынести куда-нибудь в папку/проект "модели" public class Region { public int RegionID { get; set; } public int? ParentID { get; set; } public string RegionName { get; set; } } public class UnitOfWork { YandexAPIService.APIPortClient yandexAPIPortClient; #region Singleton private static volatile UnitOfWork instance; private static object syncRoot = new Object(); private UnitOfWork() { yandexAPIPortClient = new YandexAPIService.APIPortClient(); System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; } public static UnitOfWork Instance { get { if (instance == null) { lock (syncRoot) { if (instance == null) instance = new UnitOfWork(); } } return instance; } } #endregion public Region[] GetRegions(string application_id, string token, string login, string locale) { Region[] regions = null; var applicationIdHeader = MessageHeader.CreateHeader("application_id", "ns", application_id); var tokenHeader = MessageHeader.CreateHeader("token", "ns", token); var loginHeader = MessageHeader.CreateHeader("login", "ns", login); var localeHeader = MessageHeader.CreateHeader("locale", "ns", locale); using (var scope = new OperationContextScope(yandexAPIPortClient.InnerChannel)) { OperationContext.Current.OutgoingMessageHeaders.Add(applicationIdHeader); OperationContext.Current.OutgoingMessageHeaders.Add(tokenHeader); OperationContext.Current.OutgoingMessageHeaders.Add(loginHeader); OperationContext.Current.OutgoingMessageHeaders.Add(localeHeader); var regionsInfo = yandexAPIPortClient.GetRegions(); if (regionsInfo != null) { regions = regionsInfo.Select<RegionInfo, Region>((regionInfo) => { return new Region() { RegionID = regionInfo.RegionID, ParentID = regionInfo.ParentID, RegionName = regionInfo.RegionName }; } ).ToArray(); } } return regions; } } }
ссылка на оригинал статьи http://habrahabr.ru/post/201746/
Добавить комментарий