⬆️ ⬇️

Using the Yandex Direct service using the GetRegions method (WCF client)

Yandex.Direct does not work correctly using the SOAP protocol, because the actual data scheme used in answering requests does not coincide with the scheme from the WSDL description provided by the service. Namely, the namespace does not match (instead of “API” comes “ namespaces.soaplite.com/perl ” and “ xml.apache.org/xml-soap ”), as well as the names of the parameters (for example, in the GetRegions method instead of the name “return” comes "ArrayOfRegionInfo").



To resolve the first problem, intercepting messages from the service and updating the namespace was implemented.

To fix the second problem, the WSDL autogenerate file for Reference.cs was simply corrected.

Officially, with the Yandex.Direct service, applications developed in C # using WCF are not implemented.

Yandex.Direct provides an example of how to interact with the service using the JSON data storage format and a simple WebClient. This example works correctly, unlike SOAP, but it has several disadvantages, such as: you need to de-serialize the received object yourself, there is no support for contracts between the service and the client.



Training


Service documentation is available here .

To work with the service you need:

1. register (for transmission in the login authentication parameter),

2. create a client application (for transmission in the application_id authentication parameter),

3. get a test token (for transmission in the token authentication parameter),

4. can be used to send the value “ru” in the authentication parameter locale.

More details can be found in the documentation for the service, you should not rewrite it here.



Create application


1. Create a new application in MS Visual Studio.

2. Add a new link to the service, specify wsdl: “ api.direct.yandex.ru/wsdl/v4 ”, namespace, for example, YandexAPIService (well, or whatever you like).

3. In the file Reference.cs, the type System.Nullable <System.DateTime> is replaced by System.DateTime

4. In the file Reference.cs in the description of the YandexAPIService.APIPort.GetRegions method, the name of the parameter “return” is replaced by “ArrayOfRegionInfo”.

5. Create a MessageInspector in which messages from the service will be intercepted and the “wrong” namespace will be replaced with the “correct” one.

')

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. Configure endpoint in the [app | web] .config configuration file

 <?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. Implement the method call

 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; } } } 

Source: https://habr.com/ru/post/201746/



All Articles