AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 23.06.2020, 14:12   #1  
Blog bot is offline
Blog bot
Участник
 
23,384 / 790 (73) +++++++
Регистрация: 28.10.2006
littleax: Simple Rest API in D365FO, D365F, D365SCM
Источник: http://littleax.blogspot.com/2020/06...f-d365scm.html
==============

Dynamics 365 FO Rest API




Вступление



Данная статья поможет разобраться в базовых различиях в техниках ти технологиях одного из механизмов обмена данными в современной Аксапте. Многие моменты упрощены для более легкого понимания. Все точные формулировки, вызовы и спецификации, сложные примеры использовани есть в открытом доступе.


Практически все материалы показаны в SMART TALKS 203 https://www.youtube.com/watch?v=yuJx6J4edio
Что такое XML




XML это текстовы формат представления данных. У него есть заголовок, и произвольный набор тегов. Теги должны быть парными. Внутри тегов может быть представлены разлиичные типы данных как простые (тескст, число, дата), так и более сложных - например список элементов.


XML выглядит приблизительно так



<span style="color: #f92672;"SalesOrder/span>
<span style="color: #f92672;"OrderId/span>"SO-00023"</span style="color: #f92672;"OrderId/span>
<span style="color: #f92672;"CustomerId/span>"Very impotant customer"</span style="color: #f92672;"CustomerId/span>
<span style="color: #f92672;"OrderLines/span>
<span style="color: #f92672;"SalesOrderLine/span>
<span style="color: #f92672;"OrderId/span>"SO-00023"</span style="color: #f92672;"OrderId/span>
<span style="color: #f92672;"ItemId/span>"It00123-5"</span style="color: #f92672;"ItemId/span>
<span style="color: #f92672;"Qty/span>5</span style="color: #f92672;"Qty/span>
<span style="color: #f92672;"Price/span>10</span style="color: #f92672;"Price/span>
<span style="color: #f92672;"Amount/span>50</span style="color: #f92672;"Amount/span>
</span style="color: #f92672;"SalesOrderLine/span>
<span style="color: #f92672;"SalesOrderLine/span>
<span style="color: #f92672;"OrderId/span>"SO-00023"</span style="color: #f92672;"OrderId/span>
<span style="color: #f92672;"ItemId/span>"It00123-6"</span style="color: #f92672;"ItemId/span>
<span style="color: #f92672;"Qty/span>1</span style="color: #f92672;"Qty/span>
<span style="color: #f92672;"Price/span>15</span style="color: #f92672;"Price/span>
<span style="color: #f92672;"Amount/span>15</span style="color: #f92672;"Amount/span>
</span style="color: #f92672;"SalesOrderLine/span>
</span style="color: #f92672;"OrderLines/span>
</span style="color: #f92672;"SalesOrder/span>




Что такое JSON




Так же как и XML - текстовый формат для передачи данных, очень похож на XML только натация чуть чуть другая. Рассмотрим как выглядят эти же данные в формате JSON


{
"SalesOrder": {
"OrderId": "SO-00023",
"CustomerId": "Very impotant customer",
"OrderLines": {
"SalesOrderLine": [
{
"OrderId": "SO-00023",
"ItemId": "It00123-5",
"Qty": 5,
"Price": 10,
"Amount": 50
},
{
"OrderId": "SO-00023",
"ItemId": "It00123-6",
"Qty": 1,
"Price": 15,
"Amount": 15
}
]
}
}
}




В чем разница или почему используют один или другой формат? Исторически сложилось, так что сначала был XML и он позволял описывать необходимые данные. К недостаткам можно отнести большую громоздкость (на больших объемах данных это становится важным). JSON (JavaScript object notation) - был взят из JavaScript где он имеет нативную поддержку и получил распространение для обмена данными как более легковестная альтернатива XML. С точки зрения Enterprise - есть протокол SOAP - с использованием XML, есть протокол Rest API - с использованием XML. SOAP дает возможность получить схему документа, REST API - нет. SOAP - тяжелый, REST легковестный. В D365FO* можно применять любой из подходов либо их комбинацию. Мы заглянем в схему XML но в основном будем рассматривать JSON.


Получение ключей


Если Вы консультант, то лучше всего попросить ключи у системного аржитектора, тим лида проекта, ближайшего разработчика ... и не заниматься их генерацией самостоятельно!

Авторизация дело не хитрое и у него есть свои особенности. Предположим наше приложение живет по какому-то адресу ... например https://devboxa5adevaos.cloudax.dynamics.com/?cmp=USMF&mi=DefaultDashboard - это точка входа в приложение. Значит наш базовый URL = devboxa5adevaos.cloudax.dynamics.com


Для того что бы мы получили доступ к приложению как пользователи - необходимо что бы администратор нас добавил как пользователь и назначил некоторые права в системе.


С точки зрения интеграции все в принципе работает так же. Мы специальным образом подключаем интеграцию к пользователю системы. Пользователю системы даем права на объекты системы. Подробная инчтрукция тут, а короткое описание ниже ...
Мы должны в Azure portal создать новую регастрацию для нашего приложения. При этом мы получим пару значений : clientId, client secret. Эти значения нужно запомнить!
Переходим в D365FO, System administration\setup\Azure Active Directory application и создаем новую запись





1 - вносим client Id
2 - описание записи (обычно назначение канала или название системы)
3 - пользователь Акс


Таким образом мы связали clientId с пользователем Акс. Осталось передать clientId, client secret внешней сестеме.



Отступление. Что такое запрос, какие они бывают, что такое Postman





Давайте попробуем разобаться что такое запрос. Опять же, все упрощаем, главное сейчас - уловить суть.
Если мы откроем любую страницу в браузере, это и будет обычный запрос GET. Можно даже посмотреть что это GET и что он отправляет, получает.

Google chrome, правой клавишей на странице в любом месте в сплывающем контекстном меню выбрать Inspect (Ctrl+Shift+I), и переходим на закладку Network



Мы увидим, вот такую картину







Номер 5- показывает запрос GET, куда он был сделан, что он получил ответ сервера (Status code) 200. Сам ответ - на закладке Response


Какие еще могут быть запросы - может быть (взято отсюда)



HTTP MethodCRUDEntire Collection (e.g. /users)Specific Item (e.g. /users/123)POSTCreate201 (Created), ‘Location’ header with link to /users/{id} containing new ID.Avoid using POST on single resourceGETRead200 (OK), list of users. Use pagination, sorting and filtering to navigate big lists.200 (OK), single user. 404 (Not Found), if ID not found or invalid.PUTUpdate/Replace405 (Method not allowed), unless you want to update every resource in the entire collection of resource.200 (OK) or 204 (No Content). Use 404 (Not Found), if ID not found or invalid.PATCHPartial Update/Modify405 (Method not allowed), unless you want to modify the collection itself.200 (OK) or 204 (No Content). Use 404 (Not Found), if ID not found or invalid.DELETEDelete405 (Method not allowed), unless you want to delete the whole collection — use with caution.200 (OK). 404 (Not Found), if ID not found or invalid.



Что бы увидеть как отдает данные D365FO давайте попробуем выполнить такой запрос в адресной строке браузера


https://devboxa5adevaos.cloudax.dynamics.com/data/CustomerGroups(dataAreaId='usmf',CustomerGroupId='10')


И посмотрим на свойства в окошке Inspect



если данных на закладке Network нет - нажмите F5, если страница не открывается вовсе - выполните авторизацию в D365FO



Мы выполнили Rest API запрос GET к CustomersGroups отобрав данные только по полям DataAreaId = 'usmf' и CustomerGroupId = '10'


К сожалению возможности в браузере ограничены и нам будет проще в дальнейшем использовать другие инструменты, например CURL, Fiddler, Postman.



Postman




Позволяет выполнять запросы из пользовательского окружения, удоный механизм сохранения и передачи запросов от человека к человеку, есть механизм автоматического тестирования запросов! Итак шаг за шагом.



Где его взять ... - тут






1. Коллекции - тут создаем для себя папочку с текущим нашим проектом. Здесь будут размещаться наши сохраненные запросы (создали, описали)
2. Запросы - создаются плюсиком - основна рабочая область
3. Окружение - сюда можно будет заносить значения переменых. Об этом позже, но в двух словах, мы можем базовый URL записать в переменную и один и тот же запрос выполнять для разных окружений - в каждом окружении значение этой переменной базовый URL может быть свое ...



Что бы создать запрос - нажимаем + из 2)






1 - можно запросу дать имя
2 - тип запроса GET, POST ...
3 - к чему запрос
4 - заголовки - рассмотрим дальше отдельно. В двух словах - мы говорим сервиису что мы от него хотим - например мы ему можем сообщить что хотим получить результат в формате xml или json. Другой пример - здесь задается токен авторизации
5 - тело запроса. Если мы хотим получить данные от сервиса - тело пустое. А если хотим вставить данные - то в этой секции описываем что хотим вставить
6 - отправить запрос на выполнение


Пример запроса который мы использовали в браузере







Результат - внизу - на закладке Body





Браузер таблиц


Мы можем просмотреть записи таблицы если введем сслыку в виде


https://BASEURL/?mi=SysTableBrowser&TableName=CustGroup&cmp=USMF&lng=en-us&limitednav=true


где
TableName=Имя Таблицы или представления (View) - CustGroup

cmp - имя компании - USMF
lng - язык - un-us


В результате получим










Список Entity

Чтобы получить список энтитей - необходимо ввести


https://BASEURL/data/





Table and Entity Chrome extension







Просто необходимый инструмент. Вводится BASEURL, есть возможность получить список всех таблиц, View, Entity с возможностью найти по названию, просмотреть свойства ...



Однако, вернемся к запросам .....




Получение токена авторизации


Авториация производится при помощи протокола OAuth 2.0


Для этого выполняется запрос



POST https://login.windows.net//oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: login.windows.net

resource=https%3A%2F%2FBASEURL&client_id=f78fe522-29b3-4acf-a61b-f5581e121496&client_secret=bZGMw90F7SwVyBCuIllJH7umNu2SutM0qOczMPUOzOg%3D&grant_type=client_credentials

где
- это значение нужно уточнить у технических специалистов либо в D365 знак вопроса, About/в секци This product is licensed to:


BASEURL- значение описыно выше
client_id, client_secret - значения которые мы запомнили выше


Запустим этот запрос в Postman - в результате мы получим ответ типа







В крассном квадрате access_token - который будет использован для авторизации. Этот токен который будет использоваться в каждом запросе.



Обратите внимание на использование переменных в секции body. Эти переменные установлены в окружении





Если в запросе на авторизацию на закладке Tests этот код
pm.environment.set("token", pm.response.json().access_token);







и выполнить запрос еще раз - то в переменную token будет записано вот это значение и мы сможем дальше ее использовать в запросах.



Запрос на чтение данных


Чтение данных происходит запросом GET


Запрос к Entity
CustomerGroups
вернет 9 записей





$top


$top - параметер - вернет первых нескоко записей


Например в этом же запросе GET
https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$top=1


Вернет только одну запись (первую запись)



{
"@odata.context": "https://devaos.cloudax.dynamics.com/data/$metadata#CustomerGroups",
"value": [
{
"@odata.etag": "W/\"JzAsMjI1NjU0MjExNzYn\"",
"dataAreaId": "usmf",
"CustomerGroupId": "10",
"ClearingPeriodPaymentTermName": "Net30",
"CustomerAccountNumberSequence": "",
"DefaultDimensionDisplayValue": "",
"Description": "Wholesales customers",
"IsSalesTaxIncludedInPrice": "No",
"WriteOffReason": "",
"PaymentTermId": "Net30",
"TaxGroupId": ""
}
]
}




$skip


$skip - параметр - пропустит первых несколько записей


В этом запросе
GET https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$top=1&$skip=1



Вернет только вторую запись



{
"@odata.context": "https://devaos.cloudax.dynamics.com/data/$metadata#CustomerGroups",
"value": [
{
"@odata.etag": "W/\"Jzc1MDAwNDY4LDIyNTY1NDIxMTc3Jw==\"",
"dataAreaId": "usmf",
"CustomerGroupId": "20",
"ClearingPeriodPaymentTermName": "Net30",
"CustomerAccountNumberSequence": "",
"DefaultDimensionDisplayValue": "",
"Description": "Major customers new",
"IsSalesTaxIncludedInPrice": "No",
"WriteOffReason": "GOODWILL",
"PaymentTermId": "Net30",
"TaxGroupId": ""
}
]
}




$count


Возвращает кол-во записей запроса


В этом виде GET
https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups/$count


Получаем 9






Запрос
GET https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$count=true



Вернет



{
"@odata.context": "https://devaos.cloudax.dynamics.com/data/$metadata#CustomerGroups",
"@odata.count": 9,
"value": [
{
"@odata.etag": "W/\"JzAsMjI1NjU0MjExNzYn\"",
"dataAreaId": "usmf",
"CustomerGroupId": "10",
"ClearingPeriodPaymentTermName": "Net30",
"CustomerAccountNumberSequence": "",
"DefaultDimensionDisplayValue": "",
"Description": "Wholesales customers",
"IsSalesTaxIncludedInPrice": "No",
"WriteOffReason": "",
"PaymentTermId": "Net30",
"TaxGroupId": ""
},
{




и т.д. ...


Запрос GET
https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$count=true&$top=1


Вернет все равно 9 записей


{
"@odata.context": "https://devaos.cloudax.dynamics.com/data/$metadata#CustomerGroups",
"@odata.count": 9,
"value": [
{
"@odata.etag": "W/\"JzAsMjI1NjU0MjExNzYn\"",
"dataAreaId": "usmf",
"CustomerGroupId": "10",
"ClearingPeriodPaymentTermName": "Net30",
"CustomerAccountNumberSequence": "",
"DefaultDimensionDisplayValue": "",
"Description": "Wholesales customers",
"IsSalesTaxIncludedInPrice": "No",
"WriteOffReason": "",
"PaymentTermId": "Net30",
"TaxGroupId": ""
}
]
}




$select



Указываем список полей для выборки





GET https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$top=2&$select=dataAreaId,CustomerGroupId


Получим


{
"@odata.context": "https://devaos.cloudax.dynamics.com/data/$metadata#CustomerGroups(dataAreaId,CustomerGroupId)",
"value": [
{
"@odata.etag": "W/\"JzAsMjI1NjU0MjExNzYn\"",
"dataAreaId": "usmf",
"CustomerGroupId": "10"
},
{
"@odata.etag": "W/\"Jzc1MDAwNDY4LDIyNTY1NDIxMTc3Jw==\"",
"dataAreaId": "usmf",
"CustomerGroupId": "20"
}
]
}




$filter




фильтруем записи


GET https://[COLOR=var(--brand-primary)]{{base_url}}[/COLOR]/data/CustomerGroups?$count=true&$filter=PaymentTermId eq 'Net30'


{
"@odata.context"