|
|
#1 |
|
Administrator
|
D365FO: Создание сервиса по шагам
Добрый день! Решил обновить информацию AX 2012 Создание сервиса по шагам но уже для D365FO.
Изменений не так много в части кода на Х++ и радикально много в части кода на C#. Все проверялось на версии платформы Update35 (7.0.5644.35548) и продукта 10.0.11 (10.0.464.10002) Итак, задача: 1. Получить по коду клиента его полное наименование и группу 2. Получить по группе клиентов перечень всех клиентов, входящих в эту группу. Карточка клиента в перечне определяется полями: a. Код клиента (CustTable.AccountNum)Начинаем. 1. Создаем класс Tutorial_CustServiceContract, который будет хранить нашу карточку клиента из трех полей с parm-методами доступа к них (public переменные, объявленные на уровне класса из C# недоступны. Однако в данном примере я сознательно сделал переменные класса public, чтобы из X++ можно было с ними работать напрямую, без использования parm-методов). Класс помечаем атрибутом DataContractAttribute, а методы – DataMemberAttribute. X++: /// <summary> /// Класс-контракт для хранения данных для сервиса по клиентам /// </summary> // VSUH, 30.06.2020 [DataContractAttribute] class Tutorial_CustServiceContract { public CustAccount custAccount; public DirPartyName custAccountName; public CustGroupId custGroupId; [DataMemberAttribute] public CustAccount parmCustAccount(CustAccount _custAccount = CustAccount) { custAccount = _custAccount; return custAccount; } [DataMemberAttribute] public DirPartyName parmCustAccountName(DirPartyName _custAccountName = custAccountName) { custAccountName = _custAccountName; return custAccountName; } [DataMemberAttribute] public CustGroupId parmCustGroupId(CustGroupId _custGroupId = custGroupId) { custGroupId = _custGroupId; return custGroupId; } } X++: /// <summary> /// Класс-сервис для вызова снаружи /// </summary> // VSUH, 30.06.2020 class Tutorial_CustService { /// <summary> /// Получение деталей карточки клиента по коду клиента /// </summary> /// <param name = "_custAccount"> /// Код клиента /// </param> /// <returns> /// Класс-контракт с деталями карточки клиента /// </returns> public Tutorial_CustServiceContract getCustDetail(CustAccount _custAccount) { CustTable custTable; Tutorial_CustServiceContract contract; custTable = CustTable::find(_custAccount); contract = new Tutorial_CustServiceContract(); contract.custAccount = _custAccount; contract.custAccountName = custTable.name(); contract.custGroupId = custTable.CustGroup; return contract; } /// <summary> /// Получение перечня клиентов из заданной группы /// </summary> /// <param name = "_custGroupId"> /// Код группы клиентов /// </param> /// <returns> /// Список классов-контрактов с деталями найденных клиентов /// </returns> [AifCollectionTypeAttribute('return', Types::Class, classstr(Tutorial_CustServiceContract))] public List getCustListOfGroup(CustGroupId _custGroupId) { CustTable custTable; Tutorial_CustServiceContract contract; List contractList; contractList = new List(Types::Class); while select custTable where custTable.CustGroup == _custGroupId { contract = new Tutorial_CustServiceContract(); contract.custAccount = custTable.AccountNum; contract.custAccountName = custTable.name(); contract.custGroupId = custTable.CustGroup; contractList.addEnd(contract); } return contractList; } } Class = Tutorial_CustService (связываем сервис с классом Tutorial_CustService) External Name = Tutorial_LabCustService - под этим именем сервис будет доступен снаружи через SOAP-технологию. 4. Добавляем в сервис операции - наши методы getCustDetail и getCustListOfGroup. У обоих методов включаем свойство Enable Idempotence = Yes (это означает, что при повторном вызове этих методов с теми же параметрами будет выдан тот же результат. Пока не исследовал влияние этого свойства на функциональность работы веб-сервисов). Свойство Subscriber access level отвечает за необходимый доступ к операциям сервиса внешней системы. Я это свойство оставил, как Read по умолчанию, т.к. в моем примере не производится изменение данных. Логично это свойство изменить на Invoke, если веб-сервис предполагает изменение данных в системе. 5. Сервис нужно включить в Service Group, которая будет публиковаться. Поэтому создаем Service Group Tutorial_CustServiceGroup, устанавливаем у нее свойство AutoDeploy = Yes (иначе она не опубликуется) и включаем в эту группу наш сервис Tutorial_CustService 6. Делаем билд. Собственно, всё. Х++-ная часть на этом закончилась .Веб-сервисы теперь доступны снаружи через технологию SOAP и технологию JSON. Технология SOAP предполагает, что потребитель веб-сервиса получает от него объекты и работает с этими объектами. Технология JSON предполагает, что потребитель веб-сервиса получает некую текстовую информацию, с которой сам разбирается (в т.ч. сам должен создавать у себя необходимые объекты, например - ту же карточку клиента). Поэтому JSON работает быстрее, но сложнее для программиста, а SOAP - удобнее для разработки. Соответственно SOAP-сервис доступен по адресу https://<URL>/soap/services/Tutorial_CustServiceGroup, где <URL> - это URL нашей системы; на OneBox это usnconeboxax1aos.cloud.onebox.dynamics.com, а Tutorial_CustServiceGroup - название нашей Service Group Задав в браузере в адресе такую строку мы получаем ответ Такой ответ говорит нам, что сервис успешно развернут и можно им пользоваться. JSON-сервис доступен по адресу https://<URL>/api/services/Tutorial_CustServiceGroup, где <URL> - это URL нашей системы; на OneBox это usnconeboxax1aos.cloud.onebox.dynamics.com, а Tutorial_CustServiceGroup - название нашей Service Group Задав в браузере в адресе такую строку мы получаем ответ Такой ответ говорит нам, что сервис успешно развернут и можно им пользоваться. Для JSON можно получить перечень всех веб-сервисов просто по адресу https://<URL>/api/services/, а также можно получить формат данных конкретного сервиса https://<URL>/api/services/Tutorial_CustServiceGroup/Tutorial_CustService и каждой его операции (https://<URL>/api/services/Tutorial_CustServiceGroup/Tutorial_CustService/getCustDetail и https://<URL>/api/services/Tutorial_CustServiceGroup/Tutorial_CustService/getCustListOfGroup) Официальные (от Microsoft) примеры-заготовки по использованию сервиса через SOAP / JSON расположены по адресу: https://github.com/Microsoft/Dynamic...erviceSamples/
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 08.07.2020 в 00:16. |
|
|
|
| За это сообщение автора поблагодарили: raz (10), Logger (10), f18 (2), Stitch_MS (11), IvanS (1). | |
|
|
#2 |
|
Administrator
|
D365FO: Аутенфикация при использовании веб-сервиса
Независимо от применяемой технологии SOAP / JSON отдельной задачей стоит аутенфикация внешнего приложения при подключении к D365FO.
Для локальной (On premise) версии аутенфикация осуществляется через логин / пароль, которые проверяются службой ADFS (Active Directory Federation Services) Для облачной (Cloud) версии аутенфикация осуществляется через токен, т.е. большую текстовую строку, которая возвращается в приложение службой аутенфикации. Токен вычисляется на основе 4-х параметров:
Образец кода для аутенфикации лежит в https://github.com/microsoft/Dynamic...OAuthHelper.cs в методе GetAuthenticationHeader. X++: using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuthenticationUtility
{
public class OAuthHelper
{
/// <summary>
/// The header to use for OAuth authentication.
/// </summary>
public const string OAuthHeader = "Authorization";
/// <summary>
/// Retrieves an authentication header from the service.
/// </summary>
/// <returns>The authentication header for the Web API call.</returns>
public static string GetAuthenticationHeader(bool useWebAppAuthentication = false)
{
string aadTenant = ClientConfiguration.Default.ActiveDirectoryTenant;
string aadClientAppId = ClientConfiguration.Default.ActiveDirectoryClientAppId;
string aadClientAppSecret = ClientConfiguration.Default.ActiveDirectoryClientAppSecret;
string aadResource = ClientConfiguration.Default.ActiveDirectoryResource;
AuthenticationContext authenticationContext = new AuthenticationContext(aadTenant, false);
AuthenticationResult authenticationResult;
if (useWebAppAuthentication)
{
if (string.IsNullOrEmpty(aadClientAppSecret))
{
Console.WriteLine("Please fill AAD application secret in ClientConfiguration if you choose authentication by the application.");
throw new Exception("Failed OAuth by empty application secret.");
}
try
{
// OAuth through application by application id and application secret.
var creadential = new ClientCredential(aadClientAppId, aadClientAppSecret);
authenticationResult = authenticationContext.AcquireTokenAsync(aadResource, creadential).Result;
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Failed to authenticate with AAD by application with exception {0} and the stack trace {1}", ex.ToString(), ex.StackTrace));
throw new Exception("Failed to authenticate with AAD by application.");
}
}
else
{
// OAuth through username and password.
string username = ClientConfiguration.Default.UserName;
string password = ClientConfiguration.Default.Password;
if (string.IsNullOrEmpty(password))
{
Console.WriteLine("Please fill user password in ClientConfiguration if you choose authentication by the credential.");
throw new Exception("Failed OAuth by empty password.");
}
try
{
// Get token object
var userCredential = new UserPasswordCredential(username, password); ;
authenticationResult = authenticationContext.AcquireTokenAsync(aadResource, aadClientAppId, userCredential).Result;
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Failed to authenticate with AAD by the credential with exception {0} and the stack trace {1}", ex.ToString(), ex.StackTrace));
throw new Exception("Failed to authenticate with AAD by the credential.");
}
}
// Create and get JWT token
return authenticationResult.CreateAuthorizationHeader();
}
}
}X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuthenticationUtility
{
public partial class ClientConfiguration
{
public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }
public static ClientConfiguration OneBox = new ClientConfiguration()
{
// You only need to populate this section if you are logging on via a native app. For Service to Service scenarios in which you e.g. use a service principal you don't need that.
UriString = "https://usnconeboxax1aos.cloud.onebox.dynamics.com/",
UserName = "tusr1@TAEOfficial.ccsctp.net",
// Insert the correct password here for the actual test.
Password = "",
// You need this only if you logon via service principal using a client secret. See: [url]https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/services-home-page[/url] to get more data on how to populate those fields.
// You can find that under AAD in the azure portal
ActiveDirectoryResource = "https://usnconeboxax1aos.cloud.onebox.dynamics.com", // Don't have a trailing "/". Note: Some of the sample code handles that issue.
ActiveDirectoryTenant = "https://login.windows-ppe.net/TAEOfficial.ccsctp.net", // Some samples: [url]https://login.windows.net/yourtenant.onmicrosoft.com[/url], [url]https://login.windows.net/microsoft.com[/url]
ActiveDirectoryClientAppId = "d8a9a121-b463-41f6-a86c-041272bdb340",
// Insert here the application secret when authenticate with AAD by the application
ActiveDirectoryClientAppSecret = "",
// Change TLS version of HTTP request from the client here
// Ex: TLSVersion = "1.2"
// Leave it empty if want to use the default version
TLSVersion = "",
};
public string TLSVersion { get; set; }
public string UriString { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string ActiveDirectoryResource { get; set; }
public String ActiveDirectoryTenant { get; set; }
public String ActiveDirectoryClientAppId { get; set; }
public string ActiveDirectoryClientAppSecret { get; set; }
}
}Я изначально предполагаю, что имеется виртуалка OneBox, пользователь Admin которой сопоставлен с какой-нибудь учетной записью типа vasya@ivan-petrov.ru и есть возможность (в т.ч. права) под какой-нибудь учетной записью из домена @ivan-petrov.ru сделать настройки в Azure Active Directory. Итак, заходим на https://portal.azure.com/ и выбираем ярлык Azure Active Directory Слева выбираем пункт Регистрация приложений, жмем кнопку Новая регистрация и указываем имя приложения. Впоследствии мы свяжем данное приложение с пользователем, под которым внешнее приложение будет подключаться к нашему экземпляру системы D365FO, поэтому я указываю в качестве названия приложения - название нашего веб-сервиса Больше ничего указывать не обязательно, поэтому можно нажать на кнопку Зарегистрировать. После этого у нас откроется страничка, где уже будет нашему приложению присвоен Идентификатор приложения, он же Application Id (AppId), который мы впоследствии укажем в "четверке" параметров подключения в поле AppId Далее этому приложению нужно добавить секретный идентификатор, тот - который у нас будет указан в поле AppSecretId. Для этого, не уходя со странички приложения выбираем слева пункт "Сертификаты и секреты" И нажимаем кнопку "Новый секрет клиента" Указываем описание (любое) и срок действия - 1, 2 года или бессрочно и жмем кнопку Добавить Мы получаем секретный идентификатор, который впоследствии и нужно будет подставить в поле AppSecret. Его желательно сразу сохранить, т.к. потом на этой странице он уже отображаться не будет и его придется заново генерировать (в то время, как идентификатор приложения можно будет увидеть при повторном открытии страницы). Теперь нам нужно ассоциировать наше приложение с пользователем в D365FO. Для этого нужно зайти в систему в модуль Администрирование и выбрать пункт меню Настройка - Приложения Azure Active Directory И настроить соответствие между идентификатором приложения (AppId, код клиента) и пользователем системы (пользователь может и должен быть ограничен в правах) Дело осталось за малым - подготовить проект на C#, используя заготовки Microsoft и полученные идентификаторы AppId и AppSecret. На всякий случай отмечу, что в данном примере у меня получились следующие значения: Цитата:
AppId: "d4a93f6d-1d46-4e33-a258-3aa3ae7cb819"
AppSecretId: "J6Z86h8.JhXxYH.43TM5Bap7~_UZ29wI_R" Tenant: "ivan-petrov.ru" Resource: "https://usnconeboxax1aos.cloud.onebox.dynamics.com" X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AuthenticationUtility
{
public partial class ClientConfiguration
{
public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }
public static ClientConfiguration OneBox = new ClientConfiguration()
{
// You only need to populate this section if you are logging on via a native app. For Service to Service scenarios in which you e.g. use a service principal you don't need that.
UriString = "",
UserName = "",
// Insert the correct password here for the actual test.
Password = "",
// You need this only if you logon via service principal using a client secret. See: [url]https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/data-entities/services-home-page[/url] to get more data on how to populate those fields.
// You can find that under AAD in the azure portal
ActiveDirectoryResource = "https://usnconeboxax1aos.cloud.onebox.dynamics.com", // Don't have a trailing "/". Note: Some of the sample code handles that issue.
ActiveDirectoryTenant = "https://login.windows.net/ivan-petrov.ru", // Some samples: [url]https://login.windows.net/yourtenant.onmicrosoft.com[/url], [url]https://login.windows.net/microsoft.com[/url]
ActiveDirectoryClientAppId = "d4a93f6d-1d46-4e33-a258-3aa3ae7cb819",
// Insert here the application secret when authenticate with AAD by the application
ActiveDirectoryClientAppSecret = "J6Z86h8.JhXxYH.43TM5Bap7~_UZ29wI_R",
// Change TLS version of HTTP request from the client here
// Ex: TLSVersion = "1.2"
// Leave it empty if want to use the default version
TLSVersion = "",
};
public string TLSVersion { get; set; }
public string UriString { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string ActiveDirectoryResource { get; set; }
public String ActiveDirectoryTenant { get; set; }
public String ActiveDirectoryClientAppId { get; set; }
public string ActiveDirectoryClientAppSecret { get; set; }
}
}- Отсутствием значений переменных UserName, Password и UriString - Заполненными значениями переменных ActiveDirectoryResource (URL нашей системы D365FO), ActiveDirectoryTenant (строка "https://login.windows.net/", к которой добавлен наш домен ivan-petrov.ru), ActiveDirectoryClientAppId и ActiveDirectoryClientAppSecret, полученных при регистрации приложения в Azure AD
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 08.07.2020 в 00:03. |
|
|
|
|
#3 |
|
Administrator
|
D365FO: Использование веб-сервиса через SOAP
В качестве образца использования веб-сервиса можно взять файл https://github.com/microsoft/Dynamic...ion/Program.cs, однако в конечном счете все нужно будет собрать в один проект на C#, поэтому начинаем с того, что создаем проект на C# типа Console Application. Мы можем создать проект любого типа - главное, чтобы он мог бы быть напрямую запущен пользователем.
В созданном проекте для авторизации по токену будет не хватать ссылки на библиотеку Microsoft.IdentityModel.Clients.ActiveDirectory (см узел References) Поэтому эту библиотеку нужно добавить путем выполнения команды в Visual Studio: Tools - NuGet Package Manager - Package Manager Console Цитата:
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
Теперь в проект нужно добавить из заготовок от Microsoft следующие файлы: ClientConfiguration.cs (с правками по параметрам авторизации) OAuthHelper.cs (без правок) SoapHelper.cs (без правок) Также добавляем Service Reference указав путь к веб-сервису (и нажав кнопку Go), а также указав имя Tutorial_CustServiceReference, под которым веб-сервис будет представлен в коде Далее пишем вот такой вот класс Program.cs. Обращаю внимание, что parm-методы воспринимаются на C#, как свойства (property) класса, а перечень карточек клиента - как массив. X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using AuthenticationUtility;
using SoapUtility;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace Tutorial_ConsumeServiceSOAP
{
class Program
{
public const string serviceGroupName = "Tutorial_CustServiceGroup";
public const string serviceName = "Tutorial_LabCustService";
static void Main(string[] args)
{
var aosUriString = ClientConfiguration.Default.ActiveDirectoryResource;
string bearerKey;
bearerKey = OAuthHelper.GetAuthenticationHeader(true);
var serviceUriString = SoapUtility.SoapHelper.GetSoapServiceUriString(serviceGroupName, aosUriString);
EndpointAddress endpointAddress = new System.ServiceModel.EndpointAddress(serviceUriString);
var binding = SoapUtility.SoapHelper.GetBinding();
var client = new Tutorial_CustServiceReference.Tutorial_LabCustServiceClient(binding, endpointAddress);
var channel = client.InnerChannel;
Tutorial_CustServiceReference.CallContext refContext = new Tutorial_CustServiceReference.CallContext();
refContext.Company = "RUMF";
refContext.Language = "ru";
refContext.PartitionKey = "initial";
using (OperationContextScope operationContextScope = new OperationContextScope(channel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers[OAuthHelper.OAuthHeader] = bearerKey;
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
Tutorial_CustServiceReference.Tutorial_CustServiceContract result;
Tutorial_CustServiceReference.Tutorial_CustServiceContract[] colResult;
result = ((Tutorial_CustServiceReference.Tutorial_LabCustService)channel).getCustDetail(new Tutorial_CustServiceReference.getCustDetail(refContext, "RUMF-000001")).result;
Console.WriteLine(string.Format("{0} | {1} | {2}", result.parmCustAccount, result.parmCustAccountName, result.parmCustGroupId));
colResult = ((Tutorial_CustServiceReference.Tutorial_LabCustService)channel).getCustListOfGroup(new Tutorial_CustServiceReference.getCustListOfGroup(refContext, "Орг")).result;
foreach (Tutorial_CustServiceReference.Tutorial_CustServiceContract iterator in colResult)
{
Console.WriteLine(string.Format("{0} | {1} | {2}", iterator.parmCustAccount, iterator.parmCustAccountName, iterator.parmCustGroupId));
}
}
Console.ReadLine();
}
}
}У меня (на моей базе) получается такой результат:
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 09.12.2020 в 08:55. |
|
|
|
|
#4 |
|
Administrator
|
D365FO: Использование веб-сервиса через JSON
Для JSON структура построения кода несколько иная. Нам необходимо подготовить строку текста в некотором формате, затем вызвать сервис, передав ему эту строку, после чего получить ответ также в виде строки и разобрать полученную строку.
Тем не менее, нам также нужно создать проект на C# типа Console Application (или любого типа, который может быть запущен напрямую пользователем) Процедура аутенфикации такая же, поэтому также необходимо добавлять в проект ссылку на библиотеку Microsoft.IdentityModel.Clients.ActiveDirectory путем выполнения команды Цитата:
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
Строку можно готовить (сериализовать) и разбирать (десериализовать) как "ручками", так и при помощи библиотеки Newtonsoft.Json, которую аналогичным способом (как и Microsoft.IdentityModel.Clients.ActiveDirectory) можно добавить в Package Manager Console путем выполнения команды Цитата:
Install-Package Newtonsoft.Json
ClientConfiguration.cs (с правками по параметрам авторизации) OAuthHelper.cs (без правок) А теперь начинаются различия. Для начала нам понадобится класс JsonUtil, который будет генерировать нам URL-адрес, добавляя в него группу сервисов, сам сервис и операции сервиса. X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tutorial_ConsumeServiceJSON
{
public class JsonUtil
{
public const string OAuthHeader = "Authorization";
public static string GetJsonServiceGroupURL(string aosUriString, string serviceGroupName)
{
var serviceUriStringTemplate = "{0}/api/services/{1}";
var serviceUriString = string.Format(serviceUriStringTemplate, aosUriString.TrimEnd('/'), serviceGroupName);
return serviceUriString;
}
public static string GetJsonServiceNameURL(string aosUriString, string serviceGroupName, string serviceName)
{
var serviceUriStringTemplate = "{0}/{1}";
var serviceUriString = string.Format(serviceUriStringTemplate, GetJsonServiceGroupURL(aosUriString, serviceGroupName), serviceName);
return serviceUriString;
}
public static string GetJsonOperationURL(string aosUriString, string serviceGroupName, string serviceName, string functionName)
{
var serviceUriStringTemplate = "{0}/{1}";
var serviceUriString = string.Format(serviceUriStringTemplate, GetJsonServiceNameURL(aosUriString, serviceGroupName, serviceName), functionName);
return serviceUriString;
}
}
}1. Описать класс-контракт, описанный на X++, т.к. в отличие от SOAP - здесь не передаются объекты, а значит для получения карточки клиента - нам потребуется повторное описание полей карточки. Это делается так: X++: public class Tutorial_CustServiceContract { public string parmCustAccount { get; set; } [JsonProperty("parmCustAccountName")] public string custAccountName { get; set; } public string parmCustGroupId { get; set; } } 2. Библиотека Newtonsoft.Json умеет сериализовать / десериализовать только классы, поэтому если нам надо передать в сервис обычное значение (например, код клиента), то чтобы вручную не формировать строку JSON - нужно создать классы, состоящие из одной этой переменной: X++: public class ParmCustAccount { [JsonProperty("_custAccount")] public string custAccount { get; set; } } public class ParmCustGroupId { [JsonProperty("_custGroupId")] public string custGroupId { get; set; } } В итоге у меня получилось 2 класса: JsonServices, который вызывает веб-сервис X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using AuthenticationUtility;
namespace Tutorial_ConsumeServiceJSON
{
public class Tutorial_CustServiceContract
{
public string parmCustAccount { get; set; }
[JsonProperty("parmCustAccountName")]
public string custAccountName { get; set; }
public string parmCustGroupId { get; set; }
}
public class ParmCustAccount
{
[JsonProperty("_custAccount")]
public string custAccount { get; set; }
}
public class ParmCustGroupId
{
[JsonProperty("_custGroupId")]
public string custGroupId { get; set; }
}
public class JsonServices
{
public const string serviceGroupName = "Tutorial_CustServiceGroup";
public const string serviceName = "Tutorial_CustService";
public const string getCustDetailName = "getCustDetail";
public const string getCustListOfGroupName = "getCustListOfGroup";
string aosUriString;
string bearerKey;
[JsonProperty("return")]
public Tutorial_CustServiceContract[] serviceContracts { get; set; }
[JsonProperty("return")]
public Tutorial_CustServiceContract serviceContract { get; set; }
public JsonServices()
{
aosUriString = ClientConfiguration.Default.ActiveDirectoryResource;
bearerKey = OAuthHelper.GetAuthenticationHeader(true);
}
private HttpWebRequest CreateRequest(string _address)
{
HttpWebRequest webRequest;
webRequest = (HttpWebRequest)HttpWebRequest.Create(_address);
webRequest.Method = "POST";
// the request will be empty.
webRequest.ContentLength = 0;
webRequest.Headers.Set(JsonUtil.OAuthHeader, bearerKey);
return webRequest;
}
private string ReadJsonResponse(HttpWebRequest _request)
{
string jsonString;
using (HttpWebResponse webResponse = (HttpWebResponse)_request.GetResponse())
{
using (Stream stream = webResponse.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
jsonString = reader.ReadToEnd();
}
}
}
return jsonString;
}
public Tutorial_CustServiceContract getCustDetail(string _custAccount)
{
string operationURL = JsonUtil.GetJsonOperationURL(aosUriString, serviceGroupName, serviceName, getCustDetailName);
ParmCustAccount parmCustAccount = new ParmCustAccount();
parmCustAccount.custAccount = _custAccount;
string jsonCustAccount = JsonConvert.SerializeObject(parmCustAccount);
HttpWebRequest webRequest;
webRequest = CreateRequest(operationURL);
webRequest.SendChunked = true;
using (Stream stream = webRequest.GetRequestStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(jsonCustAccount);
writer.Flush();
}
}
string jsonResponse = ReadJsonResponse(webRequest);
serviceContract = JsonConvert.DeserializeObject<Tutorial_CustServiceContract>(jsonResponse);
return serviceContract;
}
public Tutorial_CustServiceContract[] getCustListOfGroup(string _custGroupId)
{
string operationURL = JsonUtil.GetJsonOperationURL(aosUriString, serviceGroupName, serviceName, getCustListOfGroupName);
ParmCustGroupId parmCustGroupId = new ParmCustGroupId();
parmCustGroupId.custGroupId = _custGroupId;
string jsonCustGroupId = JsonConvert.SerializeObject(parmCustGroupId);
HttpWebRequest webRequest;
webRequest = CreateRequest(operationURL);
webRequest.SendChunked = true;
using (Stream stream = webRequest.GetRequestStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(jsonCustGroupId);
writer.Flush();
}
}
string jsonResponse = ReadJsonResponse(webRequest);
serviceContracts = JsonConvert.DeserializeObject<Tutorial_CustServiceContract[]>(jsonResponse);
return serviceContracts;
}
}
}X++: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using AuthenticationUtility;
namespace Tutorial_ConsumeServiceJSON
{
class Program
{
static void Main(string[] args)
{
JsonServices jsonServices = new JsonServices();
Tutorial_CustServiceContract contract;
Tutorial_CustServiceContract[] contracts;
contract = jsonServices.getCustDetail("RUMF-000001");
Console.WriteLine(string.Format("{0} | {1} | {2}", contract.parmCustAccount, contract.custAccountName, contract.parmCustGroupId));
contracts = jsonServices.getCustListOfGroup("Орг");
foreach (Tutorial_CustServiceContract iterator in contracts)
{
Console.WriteLine(string.Format("{0} | {1} | {2}", iterator.parmCustAccount, iterator.custAccountName, iterator.parmCustGroupId));
}
Console.ReadLine();
}
}
}Результат, как и в случае с SOAP должен быть таким же
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 09.12.2020 в 08:56. |
|
|
|
|
#5 |
|
Administrator
|
Отдельно прикреплю проект. Все делалось в одном Solution.
Также хочу отметить, что домент @ivan-petrov.ru выдуманный, а созданное приложение в AAD уже удалено, поэтому в чистом виде данный код в режиме "скопировал и работает" работать не будет. Нужно будет подставить реальный домен, и реальные идентификаторы приложения и секрета. Ну и само собой данные должны быть в базе данных в соответствии с примером. 10.07.2020 Обновил архив и сообщения, т.к. как выяснилось - закралась ошибка. В строке, где инициализировалась переменная aosUriString - она бралась из файла ClientConfiguration, в котором переменная UriString была пустой. Исправил на ActiveDirectoryResource (т.е. переменная aosUriString теперь инициализируется из файла ClientConfiguration из переменной ActiveDirectoryResource). Исправление коснулось как SOAP, так и JSON. Поэтому архив перевыложил. EXE-шники от C# не перебилдивал - их все равно нужно будет перебилдивать после изменения файла ClientConfiguration на реальные значения идентификаторов
__________________
Возможно сделать все. Вопрос времени Последний раз редактировалось sukhanchik; 10.07.2020 в 20:28. |
|
|
|
| За это сообщение автора поблагодарили: trud (20), Manner (1), AvrDen (1), Weez (3), Jorj (1), imir (2), Pandasama (1). | |
|
|
|