D365fo Rest Api Smart Talks
Вступление
Данная статья поможет разобраться в базовых различиях в техниках ти технологиях одного из механизмов обмена данными в современной Аксапте. Многие моменты упрощены для более легкого понимания. Все точные формулировки, вызовы и спецификации, сложные примеры использования есть в открытом доступе.
Практически все материалы показаны в SMART TALKS 203
Что такое XML
XML это текстовый формат представления данных. У него есть заголовок, и произвольный набор тегов. Теги должны быть парными. Внутри тегов может быть представлены различные типы данных как простые (текст, число, дата), так и более сложных - например список элементов.
XML выглядит приблизительно так
Что такое JSON
Так же как и XML - текстовый формат для передачи данных, очень похож на XML только нотация чуть-чуть другая. Рассмотрим как выглядят эти же данные в формате JSON
В чем разница или почему используют один или другой формат? Исторически сложилось, так что сначала был 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 Method | CRUD | Entire Collection (e.g. /users) | Specific Item (e.g. /users/123) |
---|---|---|---|
POST | Create | 201 (Created), ‘Location’ header with link to /users/{id} containing new ID. | Avoid using POST on single resource |
GET | Read | 200 (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. |
PUT | Update/Replace | 405 (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. |
PATCH | Partial Update/Modify | 405 (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. |
DELETE | Delete | 405 (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
Позволяет выполнять запросы из пользовательского окружения, удобный механизм сохранения и передачи запросов от человека к человеку, есть механизм автоматического тестирования запросов! Итак, шаг за шагом.
Где его взять … - тут
- Коллекции - тут создаем для себя папочку с текущим нашим проектом. Здесь будут размещаться наши сохраненные запросы (создали, описали)
- Запросы - создаются плюсиком - основная рабочая область
- Окружение - сюда можно будет заносить значения переменных. Об этом позже, но в двух словах, мы можем базовый URL записать в переменную и один и тот же запрос выполнять для разных окружений - в каждом окружении значение этой переменной базовый URL может быть свое …
Что бы создать запрос - нажимаем + из 2)
- можно запросу дать имя
- тип запроса GET, POST …
- к чему запрос
- заголовки - рассмотрим дальше отдельно. В двух словах - мы говорим сервису что мы от него хотим - например мы ему можем сообщить что хотим получить результат в формате xml или json. Другой пример - здесь задается токен авторизации
- тело запроса. Если мы хотим получить данные от сервиса - тело пустое. А если хотим вставить данные - то в этой секции описываем что хотим вставить
- отправить запрос на выполнение
Пример запроса, который мы использовали в браузере
Результат - внизу - на закладке 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/
resource=https%3A%2F%2FBASEURL&client_id=f78fe522-29b3-4acf-a61b-f5581e121496&client_secret=bZGMw90F7SwVyBCuIllJH7umNu2SutM0qOczMPUOzOg%3D&grant_type=client_credentials
где
BASEURL
- значение описано выше
client_id
, client_secret
- значения которые мы запомнили выше
Запустим этот запрос в Postman
- в результате мы получим ответ типа
В красном квадрате access_token
- который будет использован для авторизации. Этот токен который будет использоваться в каждом запросе.
Обратите внимание на использование переменных в секции body
. Эти переменные установлены в окружении
Если в запросе на авторизацию на закладке Tests этот код
pm.environment.set("token", pm.response.json().access_token);
и выполнить запрос еще раз - то в переменную token будет записано вот это значение и мы сможем дальше ее использовать в запросах.
Запрос на чтение данных
Чтение данных происходит запросом GET
Запрос к Entity CustomerGroups
вернет 7 записей
$top
$top
- параметр - вернет первых несколько записей
Например в этом же запросе GET https://{{base_url}}/data/CustomerGroups?$top=1
Вернет только одну запись (первую запись)
$skip
$skip
- параметр - пропустит первых несколько записей
В этом запросе GET https://{{base_url}}/data/CustomerGroups?$top=1&$skip=1
Вернет только вторую запись
$count
Возвращает кол-во записей запроса
В этом виде GET https://{{base_url}}/data/CustomerGroups/$count
Получаем сколько записей у нас сейчас в системе
Запрос GET https://{{base_url}}/data/CustomerGroups?$count=true
Вернет
и т.д. …
Запрос GET https://{{base_url}}/data/CustomerGroups?$count=true&$top=1
Вернет все равно 9 записей
$select
Указываем список полей для выборки
GET https://{{base_url}}/data/CustomerGroups?$top=2&$select=dataAreaId,CustomerGroupId
Получим
$filter
фильтруем записи
GET https://{{base_url}}/data/CustomerGroups?$count=true&$filter=PaymentTermId eq 'Net30'
Допускается использовать AND
, OR
и …
$orderby
сортируем записи, по умолчанию ASK, доступно использование DESC
GET https://{{base_url}}/data/CustomerGroups?$count=true&$filter=PaymentTermId eq 'Net30'&$orderby=Description
KEY
Возможность выбирать данные по ключу - одну запись. Очень важное свойство в операциях PATCH
, PUT
, DELETE
GET https://{{base_url}}/data/CustomerGroups(dataAreaId='usmf',CustomerGroupId='10')
Ответ
как узнать ключ
Accept: odata.metadata=minimal, odata.metadata=full
Ключ Accept
говорит о том, как мы ожидаем получить данные от сервиса
В случае odata.metadata=minimal
- сервис выдаст минимальный набор данных - все запросы до этого момента были выполенены с этим параметром
Как выглядит ответ odata.metadata=full
вернет такой ответ
GET https://{{base_url}}/data/CustomerGroups?$count=true&$filter=CustomerGroupId eq '10'
где
@odata.type
- название Entity
@odata.id
- ссылка на получение уникальной текущей записи - в скобках - ключи
@odata.nextLink
- ссылка на следующую страницу
Ограничения
Максимальная выборка ограничена только 1000 записями. Если в результирующей выборке записей больше - включается постраничное отображение. Постраничное отображение реализовано при помощи $top
и $skip
. Дополнительно появляется тег @odata.nextLink
со ссылкой на следующий набор записей.
$expand
Позволяет присоединить один набор данных в другому
Возмем другой набор данных - GET https://{{base_url}}/data/SalesOrderHeadersV2?$top=1
Теперь его расширим за счет expand
Запрос GET https://{{base_url}}/data/SalesOrderHeadersV2(dataAreaId='usmf',SalesOrderNumber='000002')?$expand=SalesOrderLines
Ответ
мы можем использовать дополнительные параметры в $expand запросе
GET https://{{base_url}}/data/SalesOrderHeadersV2?$top=1&$select=SalesOrderNumber&$expand=SalesOrderLines($select=InventoryLotId,ItemNumber,SalesOrderNumber)
вернет
cross-company=true
По умолчанию запросы выполняются к компании в которой по умолчанию привязан пользователь ассоциированный с парой clientId
, secret
Для того что бы выполнить запрос по всем компаниям компаниям, необходимо добавить параметр cross-company=true
Вставка записей (Insert/POST)
Вставка осуществляется при помощи запроса POST
POST https://{{base_url}}/data/CustomerGroups
Закладка Body
Где
@odata.type
- тип записи который мы вставляем. Очень грубо говоря - Entity в которую мы осуществляем вставку называется CustomerGroups, мы в нее вставляемзапись типа #Microsoft.Dynamics.DataEntities.CustomerGroup. Внешне энтити и тип помут быть похожи, а могут быть не похожи.
Заполняем поля - поля со звездочной - обязательные для заполнения поля
* "CustomerGroupId":"T1",
* "dataAreaId":"usmf",
"Description":"Test group T1"
Выполняем запрос, получаем ответ
Статус ответа 201 Created
- запись создана
Выполняем запрос с фильтром на выборку и проверяем
GET https://{{base_url}}/data/CustomerGroups?$filter=CustomerGroupId eq 'T1'
Обновление записей (Update/PUT)
PUT https://{{base_url}}/data/CustomerGroups(dataAreaId='usmf',CustomerGroupId='T1')
Обратите внимание - мы должны четко выбрать одну запись по ключу, и только тогда ее обновить.
Headers
- такой же
Body
Ответ Status 204 No content
проверяем ззапросом на выборку
GET https://{{base_url}}/data/CustomerGroups?$filter=CustomerGroupId eq 'T1'
Удаление записей (Delete/DELETE)
Заголовок тот же, боди -пустое, запрос DELETE https://{{base_url}}/data/CustomerGroups(dataAreaId='usmf',CustomerGroupId='T1')
Ответ
Статус 204 - No Content
проверяем GET https://{{base_url}}/data/CustomerGroups?$filter=CustomerGroupId eq 'T1'
Нет записей для отображения, мы справились
Транзакция ttsbegin..ttscommit BATCH
Для того, что бы выполнить вставку многих записей в один раз мы должны выполнить запрос к сервису batch
POST https://{{base_url}}/data/$batch
В Header:
Content-Type: multipart/mixed; boundary=batch_boundary
Body
:
Если по какой-то причине какая-то запись не вставилась - сервис batch
вернет статус 200 - что он отработал нормально. Другое дело, при вызове multipart
нужно анализировать все ответы.
Обратите внимание - внутри по Cintent-ID: 1
ошибка! Т.е. сервис отработал, но запись не вставлена. Попробуйте multipart
запрсос на вставку запустить два раза-вы увидите такую ошибку
Номерные серии
Работа с Entity позволяет получать автозаполнение полей стандартным способом. Например при вставке строк заказов на продажу достаточно заполнить всего пару полей в POST
запросе
Строка успешно создастся и поля единица измерения, кол-во, цена, сумма … аналитики - заполнятся по умолчанию.
В случае вставки заказа на продажу (шапка) та же история
При этом номер заказа для шапки будет сгенерирован автоматически.
Что нам делать, если мы попробуем сделать вставку Шапки заказа и строк в одной транзакции при помощи batch
?
Нам нужно что бы шапка и строки были с одним номером. Для этого у нас есть два варианта. Первый - нужно присвоить следующий номер или произвольный( в случае с произвольным номером- номерная серия для номера заказа на продажу должна допускать редактирование).
Заключение
В принципе мы рассмотрели возможности применения данной технологии. Это не единственная технология интеграции в аксапте, но точно очень полезная. Понимая эти принципы - можно дальше идти с интеграцией в MS Power Automate
.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.