Практическое руководство. Взаимодействие с внешними службами с помощью логики сервера

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

Замечание

В этом примере используется https://services.odata.org/TripPinRESTierService , но вы можете использовать другие общедоступные средства бесплатного тестирования.

Шаг 1. Создание логики сервера

  1. Войдите в Power Pages.

  2. Выберите сайт и изменить.

  3. Перейдите в рабочую область "Настройка ", а затем выберите логику сервера (предварительная версия)

    Снимок экрана: место выбора логики сервера в рабочей области

  4. Выберите +Создать логику сервера

  5. Введите имя логики сервера. Это имя используется в API в качестве идентификатора ресурса при создании API логики сервера. Рассмотрим пример.

    Недвижимость Пример значения
    Имя services.odata.org
    Отображаемое имя Performing CRUD operations using OData service
  6. Нажмите кнопку +Добавить роли , чтобы назначить соответствующую веб-роль. Добавлена роль администратора в логике сервера В этом примере выберите "Администраторы ", а затем выполните вход от имени администратора.

  7. Нажмите кнопку "Добавить".

  8. Выберите логику сервера и выберите команду "Изменить код".

    Выбор кода редактирования

  9. В диалоговом окне Изменить в Microsoft Visual Studio Code для Интернета выберите Открыть Visual Studio Code, чтобы разработать пользовательскую логику.

    В файле вы найдете предварительно определенные методы и скрипты.

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

    Добавьте следующую baseUrl константу перед методом get()

    
     const baseUrl  = "https://services.odata.org/TripPinRESTierService";
    
    
  11. Замените примерные функции get, post, put, и del следующим кодом.

    
    async function get() { 
       try { 
          Server.Logger.Log("GET called"); // Logger reference 
          if (!Server.Context.QueryParameters["username"]) { 
                const response = await Server.Connector.HttpClient.GetAsync( 
                   `${baseUrl}/People?$select=UserName,FirstName,LastName,MiddleName,Age`, 
                   {'content-type':'application/json'}); 
                return response; 
          } 
          else {             
                const id = Server.Context.QueryParameters["username"]; // Context reference 
                const response = await Server.Connector.HttpClient.GetAsync( 
                   `${baseUrl}(${id})/People?$select=UserName,FirstName,LastName,MiddleName,Age`, 
                   {'content-type':'application/json'}); 
                return response; 
          }         
       } catch (err) { 
          Server.Logger.Error("GET failed: " + err.message); 
          return JSON.stringify({ status: "error", method: "GET", message: err.message }); 
       } 
    } 
    
    async function post() { 
       try { 
          Server.Logger.Log("POST called"); 
          const data = JSON.parse(Server.Context.Body);            
          const url = `${baseUrl}/${data.serviceSession}/People`; 
          const { serviceSession, ...body } = data;  
          const response = await Server.Connector.HttpClient.PostAsync( 
                url,  
                JSON.stringify(body), 
                {'content-type':'application/json'}); 
          return response; 
       } catch (err) { 
          Server.Logger.Error("POST failed: " + err.message); 
          return JSON.stringify({ status: "error", method: "POST", message: err.message }); 
       } 
    }  
    
    async function put() { 
       try { 
          Server.Logger.Log("PUT called"); 
          const data = JSON.parse(Server.Context.Body);         
          const url = `${baseUrl}/${data.serviceSession}/People('${data.UserName}')`; 
          const { UserName, ...body1 } = data; 
          const { serviceSession, ...body } = body1; 
          const response = await Server.Connector.HttpClient.PatchAsync( 
                url,  
                JSON.stringify(body), 
                {'content-type':'application/json'}); 
          return response; 
       } catch (err) { 
          Server.Logger.Error("PUT failed: " + err.message); 
          return JSON.stringify({ status: "error", method: "PUT", message: err.message }); 
       } 
    }    
    
    async function del() { 
       try { 
          // "delete" keyword should not be used in script file. 
          Server.Logger.Log("DEL called"); 
          const username = Server.Context.QueryParameters["username"]; 
          const serviceSession = Server.Context.QueryParameters["serviceSession"]; 
          const url = `${baseUrl}/${serviceSession}/People('${username}')`; 
          const response = await Server.Connector.HttpClient.DeleteAsync( 
                url,  
                {'content-type':'application/json'}); 
          return response; 
       } catch (err) { 
          Server.Logger.Error("Deletion failed: " + err.message); 
          return JSON.stringify({ status: "error", method: "DEL", message: err.message }); 
       } 
    } 
    
  12. Сохраните файл.

    Подсказка

    Нажмите клавиши CTRL+S для сохранения.

Шаг 2. Создание веб-страницы

  1. Запустите студию разработки Power Pages.

  2. В рабочей области Pages выберите +Page.

    Добавление новой страницы

  3. В диалоговом окне "Описание страницы", чтобы создать его , выберите другие способы добавления страницы.

  4. В диалоговом окне "Добавление страницы" введите сведения.

    1. В поле "Имя" введите логику сервера и выберите "Пуск" из пустого макета.
    2. Нажмите кнопку "Добавить".

    Введите значения для добавления страницы

  5. Выберите параметр "Изменить код " в правом верхнем углу.

  6. В диалоговом окне "Редактировать в Visual Studio Code для веба" выберите "Открыть Visual Studio Code".

  7. Замените код, найденный на HTML-странице, следующим образом:

    <div class="row sectionBlockLayout text-left" style="min-height: auto; padding: 8px;">
    <div class="container" style="display: flex; flex-wrap: wrap;">
       <div class="col-md-12 columnBlockLayout" style="padding: 16px; margin: 60px 0px; min-height: 200px;">
          <!-- Sample code for Web API demonstration -->
          <style>
          #processingMsg {
             padding: 6px 12px;
             background: #eee;
             border-radius: 4px;
             position: fixed;
             top: 10px;
             left: 50%;
             transform: translateX(-50%);
             display: none;
             z-index: 9999;
             text-align: center;
             font-weight: bold;
          }
    
          table {
             border-collapse: collapse;
             width: 100%;
             margin-top: 10px;
             font-family: Arial, sans-serif;
          }
    
          th,
          td {
             border: 1px solid #ccc;
             padding: 6px;
             text-align: left;
          }
    
          button {
             cursor: pointer;
             border: 1px solid #aaa;
             padding: 4px 8px;
             border-radius: 4px;
             background: #fff;
             font-size: 14px;
             margin-right: 2px;
          }
    
          button.add {
             color: green;
          }
    
          button.save {
             color: green;
          }
    
          button.cancel {
             color: orange;
          }
    
          button.delete {
             color: red;
          }
    
          input {
             width: 95%;
             box-sizing: border-box;
          }
    
          td.actions {
             white-space: nowrap;
          }
    
          td[data-attribute="UserName"] {
             background: #f9f9f9;
             cursor: default;
          }
          </style>
    
          <div id="processingMsg">Processing...</div>
          <div id="dataTable"></div>
    
          <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
          <script>
          $(function () {
    
             // Safe WebAPI wrapper for Power Pages
             (function (webapi, $) {
                function safeAjax(opts) {
                var d = $.Deferred();
                shell.getTokenDeferred().done(function (token) {
                   opts.headers = opts.headers || {};
                   opts.headers["__RequestVerificationToken"] = token;
                   $.ajax(opts)
                      .done((data, ts, jq) => validateLoginSession(data, ts, jq, d.resolve))
                      .fail(d.reject);
                }).fail(() => d.rejectWith(this, arguments));
                return d.promise();
                }
                webapi.safeAjax = safeAjax;
             })(window.webapi = window.webapi || {}, jQuery);
    
             // Notification overlay
             const notify = (() => {
                const $m = $('#processingMsg'); let s = 0, t;
                return {
                show: (msg = 'Processing...') => { $m.text(msg); if (!s) $m.show(); s++; },
                hide: () => { s = Math.max(0, s - 1); if (!s) t = setTimeout(() => $m.hide(), 200); }
                };
             })();
    
             function ajaxCall(msg, opts) {
                notify.show(msg);
                return webapi.safeAjax(opts)
                .fail(r => alert(r.responseJSON?.error?.message || 'Server error'))
                .always(notify.hide);
             }
    
             // Table columns
             const cols = [
                { name: 'UserName', label: 'User Name', editable: false },
                { name: 'FirstName', label: 'First Name' },
                { name: 'LastName', label: 'Last Name' },
                { name: 'MiddleName', label: 'Middle Name' },
                { name: 'Age', label: 'Age' }
             ];
    
             let data = [];
             let serviceSession = '';
             function render() {
                const html = `<table>
                <thead>
                   <tr>
                      ${cols.map(c => `<th>${c.label}</th>`).join('')}
                      <th>Actions <button class="add">➕</button></th>
                   </tr>
                </thead>
                <tbody>
                   ${data.map(r => `<tr data-UserName="${r.UserName}">
                      ${cols.map(c => `<td data-attribute="${c.name}" data-value="${r[c.name] || ''}">
                            ${r[c.name] || ''}
                      </td>`).join('')}
                      <td class="actions">
                            <button class="delete">🗑️</button>
                      </td>
                   </tr>`).join('')}
                </tbody>
          </table>`;
                $('#dataTable').html(html);
             }
    
             // Double click row to edit except username
             $('#dataTable').on('dblclick', 'tr', function () {
                const $tr = $(this);
                if ($tr.hasClass('editing')) return;
                $tr.addClass('editing');
                $tr.find('td[data-attribute]').each(function () {
                const $td = $(this);
                if ($td.data('attribute') === 'UserName') return;
                const old = $td.text().trim();
                $td.html(`<input type="text" value="${old}" data-attr="${$td.data('attribute')}"/>`);
                });
                $tr.find('.actions').append('<button class="save">✅</button><button class="cancel">❌</button>');
             });
    
             $('#dataTable').on('click', '.save', function () {
                const $tr = $(this).closest('tr');
                const username = $tr.data('username');
                const updates = {};
                $tr.find('input').each(function () {
                const attr = $(this).data('attr');
                let value = $(this).val().trim();
    
                if (attr === 'Age') {
                   value = Number(value);
                   if (isNaN(value)) value = null; // safety fallback
                }
    
                updates[attr] = value;
                });
    
                ajaxCall('Updating...', {
                type: 'PUT',
                url: `/_api/serverlogics/services.odata.org`,
                contentType: 'application/json',
                data: JSON.stringify(Object.assign({ UserName: username, serviceSession: `${serviceSession}` }, updates)),
                success: () => { Object.assign(data.find(x => x.UserName === username), updates); render(); }
                });
             });
    
             $('#dataTable').on('click', '.cancel', () => render());
    
             $('#dataTable').on('click', '.delete', function () {
                const username = $(this).closest('tr').data('username');
                if (!confirm('Delete ' + username + '?')) return;
                ajaxCall('Deleting...', {
                type: 'DELETE',
                url: `/_api/serverlogics/services.odata.org?username=${username}&serviceSession=${serviceSession}`,
                success: () => { data = data.filter(x => x.UserName !== username); render(); }
                });
             });
    
             $('#dataTable').on('click', '.add', function () {
                const r = {
                serviceSession: `${serviceSession}`,
                UserName: 'user' + Math.floor(Math.random() * 9000 + 1000),
                FirstName: 'New',
                LastName: 'Person',
                MiddleName: '',
                Age: 25
                };
                ajaxCall('Adding...', {
                type: 'POST',
                url: `/_api/serverlogics/services.odata.org`,
                contentType: 'application/json',
                data: JSON.stringify(r),
                success: () => { data.unshift(r); render(); }
                });
             });
    
             // Initial load
             ajaxCall('Loading...', {
                type: 'GET',
                url: `/_api/serverlogics/services.odata.org`,
                contentType: 'application/json'
             }).done(resp => {
                const parsed = JSON.parse(resp.data);
                const body = JSON.parse(parsed.Body);
    
                const ctx = body['@odata.context'];
                const match = ctx.match(/\\S\\[^)]*\\/);
                serviceSession = match ? match[0] : '';
    
                data = (body.value || []).map(r => ({
                UserName: r.UserName,
                FirstName: r.FirstName,
                LastName: r.LastName,
                MiddleName: r.MiddleName,
                Age: r.Age
                }));
                render();
             });
    
          });
          </script>
       </div>
    </div>
    </div>
    
  8. Сохранение файла

    Замечание

    Этот код создает интерактивный интерфейс таблицы данных для демонстрации операций CRUD (Создание, чтение, обновление, удаление) с внешней службой OData с помощью логики сервера Power Pages. Он включает стили CSS для адаптивного макета таблицы, наложения уведомлений обработки и функциональность JavaScript, которая позволяет пользователям просматривать данные, полученные из внешней службы, добавлять новые записи, нажимая кнопку ➕, редактировать существующие записи, дважды щелкая по строкам таблицы (кроме поля UserName), сохранять изменения с помощью ✅, отменять изменения с помощью ❌ и удалять записи с помощью кнопки 🗑️. Код использует jQuery и безопасную оболочку WebAPI для выполнения вызовов AJAX, прошедших проверку подлинности, к конечной точке /_api/serverlogics/services.odata.org, обрабатывая данные ответа для анализа контекста OData и управления сеансом службы для последующих операций.

  9. Вернитесь в конструктор Power Pages.

  10. В диалоговом окне Редактирования кода в Visual Studio Code для Интернета следуйте инструкциям и нажмите кнопку "Синхронизация ".

Шаг 3. Использование логики сервера для чтения, просмотра, редактирования, создания и удаления

Чтобы проверить функциональность логики сервера, выполните следующие действия.

  1. Выберите "Предварительный просмотр" и выберите "Рабочий стол".
  2. Войдите на сайт, используя учетную запись пользователя, которой ранее была назначена роль пользователя логики сервера. Как мы выбрали роль "Администраторы" на предыдущем шаге, в этом примере выполните вход от имени администратора.
  3. Перейдите на веб-страницу логики сервера , созданную ранее.
  4. Проверьте операции.

Общие сведения о логике сервера
Разработка логики сервера
Серверные объекты, взаимодействующие с таблицами Dataverse с помощью логики сервера