教程:使用门户 Web API

备注

从 2022 年 10 月 12 日起,Power Apps 门户更名为 Power Pages。 详细信息请参阅:Microsoft Power Pages 现已正式发布(博客)
不久后我们将迁移 Power Apps 门户文档并将其与 Power Pages 文档合并在一起。

在本教程中,您将设置一个网页和自定义 Web 模板,它们将使用 Web API 从联系人表读取、写入、更新和删除记录。

备注

执行本示例中的步骤时您可以更改列名称或使用不同表。

第 1 步。 创建网站设置

您必须先使用“门户管理”应用启用所需的站点设置,然后才能使用门户 Web API。 站点设置取决于与 Web API 交互时将使用的表。

  1. 转到  Power Apps

  2. 选择左侧窗格中的 应用

  3. 选择 门户管理 应用。

    启动门户管理应用。

  4. 在 门户管理 应用的左窗格中,选择 站点设置

    在门户管理应用中打开站点设置。

  5. 选择 新建

  6. 在 名称 框中,输入 Webapi/contact/enabled

  7. 在 网站 列表中,选择您的网站记录。

  8. 在  框中输入 true

    为 Web API 站点设置启用联系人表。

  9. 选择 保存并关闭

  10. 选择 新建

  11. 在 名称 框中,输入 Webapi/contact/fields

  12. 在 网站 列表中,选择您的网站记录。

  13. 在  框中,输入
    firstname,lastname,fullname,emailaddress1,telephone1

    启用 Web API 联系人表字段站点设置。

  14. 选择 保存并关闭

  15. 选择 新建

  16. 在 名称 框中,输入 Webapi/error/innererror

    启用 Web API 内部错误站点设置。

  17. 在 网站 列表中,选择您的网站记录。

  18. 在  框中输入 true

  19. 选择 保存并关闭

  20. 验证 Web API 的站点设置。

步骤 2. 配置权限

您必须配置权限,以便用户能够使用 Web API 功能。 在此示例中,您将为启用联系人表的表权限,使用 Web API 创建 Web 角色,将联系人表的表权限添加到此 Web 角色,然后将 Web 角色添加到用户以允许他们使用 Web API。

  1. 在 门户管理 应用的左窗格中,选择 表权限

  2. 选择 新建

  3. 在 名称 框中,输入 联系人表权限

  4. 在 表名称 列表中,选择 联系人(联系人)

  5. 在 网站 列表中,选择您的网站记录。

  6. 在 访问类型 列表中,选择 全局

  7. 选择读取写入创建删除权限。

  8. 选择 保存并关闭

    联系人表权限。

创建 Web 角色

您可以在网站中使用现有的 Web 角色或创建新 Web 角色。

  1. 在左窗格中选择 Web 角色 。

  2. 选择 新建

  3. 在 名称 框中,输入 Web API 用户(或最能反映访问此功能的用户角色的任何名称)。

  4. 在 网站 列表中,选择您的网站记录。

    添加 Web API 用户 Web 角色。

  5. 选择 保存

添加相关表权限

  1. 对于新的或现有 Web 角色,选择 相关 > 表权限

    为 Web 角色添加相关表权限。

  2. 选择 添加现有表权限

  3. 选择先前创建的 联系人表权限

    选择联系人表权限。

  4. 选择 添加

  5. 选择 保存并关闭

    表权限视图。

将联系人添加到 Web 角色

  1. 在左窗格中选择 联系人

  2. 选择要在此示例中用于 Web API 的联系人。

    备注

    此联系人是在此示例中用于测试 Web API 的用户帐户。 请务必在您的门户中选择正确的联系人。

  3. 选择 相关 > Web 角色

    选择相关 Web 角色。

  4. 选择 添加现有 Web 角色

  5. 选择之前创建的 Web API 用户 角色。

  6. 选择 添加

    Web 角色关联视图。

  7. 选择 保存并关闭

步骤 3. 创建网页

现在,您已启用 Web API 并且配置了用户权限,请创建一个带有示例代码的网页以查看、编辑、创建和删除记录。

  1. 在 门户管理 应用的左窗格中,选择网页

  2. 选择新建

  3. 名称框中,输入 webapi

  4. 网站列表中,选择您的网站记录。

  5. 对于父页,请选择主页

  6. 对于部分 URL,请输入 webapi

  7. 对于页面模板,请选择主页

  8. 对于发布状态,请选择已发布

  9. 选择保存

    网页。

  10. 选择相关 > 网页

    相关网页

  11. 网页关联视图中,选择 webapi

    网页关联视图。

  12. 向下滚动到内容部分,然后转到复制(HTML)(HTML 设计器)。

    复制 HTML 内容

  13. 选择 HTML 选项卡。

    选择“HTML”选项卡

  14. 复制以下示例代码片段并将其粘贴到 HTML 设计器中。

        <!-- Sample code for Web API demonstration -->
    <style>
        #processingMsg {
            width: 150px;
            text-align: center;
            padding: 6px 10px;
            z-index: 9999;
            top: 0;
            left: 40%;
            position: fixed;
            -webkit-border-radius: 0 0 2px 2px;
            border-radius: 0 0 2px 2px;
            -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            display: none;
        }
    
        table td[data-attribute] .glyphicon-pencil {
            margin-left: 5px;
            opacity: 0;
        }
    
        table td[data-attribute]:hover .glyphicon-pencil {
            opacity: 0.7;
        }
    </style>
    
    <script>
      $(function() {
        //Web API ajax wrapper
        (function(webapi, $) {
          function safeAjax(ajaxOptions) {
            var deferredAjax = $.Deferred();
            shell.getTokenDeferred().done(function(token) {
              // Add headers for ajax
              if (!ajaxOptions.headers) {
                $.extend(ajaxOptions, {
                  headers: {
                    "__RequestVerificationToken": token
                  }
                });
              } else {
                ajaxOptions.headers["__RequestVerificationToken"] = token;
              }
              $.ajax(ajaxOptions)
                .done(function(data, textStatus, jqXHR) {
                  validateLoginSession(data, textStatus, jqXHR, deferredAjax.resolve);
                }).fail(deferredAjax.reject); //ajax
            }).fail(function() {
              deferredAjax.rejectWith(this, arguments); // On token failure pass the token ajax and args
            });
            return deferredAjax.promise();
          }
          webapi.safeAjax = safeAjax;
        })(window.webapi = window.webapi || {}, jQuery)
        // Notification component
        var notificationMsg = (function() {
          var $processingMsgEl = $('#processingMsg'),
            _msg = 'Processing...',
            _stack = 0,
            _endTimeout;
          return {
            show: function(msg) {
              $processingMsgEl.text(msg || _msg);
              if (_stack === 0) {
                clearTimeout(_endTimeout);
                $processingMsgEl.show();
              }
              _stack++;
            },
            hide: function() {
              _stack--;
              if (_stack <= 0) {
                _stack = 0;
                clearTimeout(_endTimeout);
                _endTimeout = setTimeout(function() {
                  $processingMsgEl.hide();
                }, 500);
              }
            }
          }
        })();
        // Inline editable table component
        var webAPIExampleTable = (function() {
          var trTpl = '<% _.forEach(data, function(data){ %>' +
            '<tr data-id="<%=data.id%>" data-name="<%=data.fullname%>">' +
            '<% _.forEach(columns, function(col){ %>' +
            '<td data-attribute="<%=col.name%>" data-label="<%=col.label%>" data-value="<%=data[col.name]%>">' +
            '<%-data[col.name]%><i class="glyphicon glyphicon-pencil"></i>' +
            '</td>' +
            '<% }) %>' +
            '<td>' +
            '<button class="btn btn-default delete" type="submit"><i class="glyphicon glyphicon-trash" aria-hidden="true"></i></button>' +
            '</td>' +
            '</tr>' +
            '<% }) %>';
          var tableTpl = '<table class="table table-hover">' +
            '<thead>' +
            '<tr>' +
            '<% _.forEach(columns, function(col){ %>' +
            '<th><%=col.label%></th>' +
            '<% }) %>' +
            '<th>' +
            '<button class="btn btn-default add" type="submit">' +
            '<i class="glyphicon glyphicon-plus" aria-hidden="true"></i> Add Sample Record' +
            '</button>' +
            '</th>' +
            '</tr>' +
            '</thead>' +
            '<tbody>' + trTpl + '</tbody>' +
            '</table>';
          function getDataObject(rowEl) {
            var $rowEl = $(rowEl),
              attrObj = {
                id: $rowEl.attr('data-id'),
                name: $rowEl.attr('data-name')
              };
            $rowEl.find('td').each(function(i, el) {
              var $el = $(el),
                key = $el.attr('data-attribute');
              if (key) {
                attrObj[key] = $el.attr('data-value');
              }
            })
            return attrObj;
          }
          function bindRowEvents(tr, config) {
            var $row = $(tr),
              $deleteButton = $row.find('button.delete'),
              dataObj = getDataObject($row);
            $.each(config.columns, function(i, col) {
              var $el = $row.find('td[data-attribute="' + col.name + '"]');
              $el.on('click', $.proxy(col.handler, $el, col, dataObj));
            });
            //User can delete record using this button
            $deleteButton.on('click', $.proxy(config.deleteHandler, $row, dataObj));
          }
          function bindTableEvents($table, config) {
            $table.find('tbody tr').each(function(i, tr) {
              bindRowEvents(tr, config);
            });
            $table.find('thead button.add').on('click', $.proxy(config.addHandler, $table));
          }
          return function(config) {
            var me = this,
              columns = config.columns,
              addHandler = config.addHandler,
              deleteHandler = config.deleteHandler,
              $table;
            me.render = function(el) {
              $table = $(el).html(_.template(tableTpl)({
                columns: columns,
                data: me.data
              })).find('table');
              bindTableEvents($table, {
                columns: columns,
                addHandler: addHandler,
                deleteHandler: deleteHandler
              });
            }
            me.addRecord = function(record) {
              $table.find('tbody tr:first').before(_.template(trTpl)({
                columns: columns,
                data: [record]
              }));
              bindRowEvents($table.find('tbody tr:first'), config);
            }
            me.updateRecord = function(attributeName, newValue, record) {
              $table.find('tr[data-id="' + record.id + '"] td[data-attribute="' + attributeName + '"]').text(newValue);
            }
            me.removeRecord = function(record) {
              $table.find('tr[data-id="' + record.id + '"]').fadeTo("slow", 0.7, function() {
                $(this).remove();
              });
            }
          };
        })();
        //Applicaton ajax wrapper 
        function appAjax(processingMsg, ajaxOptions) {
          notificationMsg.show(processingMsg);
          return webapi.safeAjax(ajaxOptions)
            .fail(function(response) {
              if (response.responseJSON) {
                alert("Error: " + response.responseJSON.error.message)
              } else {
                alert("Error: Web API is not available... ")
              }
            }).always(notificationMsg.hide);
        }
        function loadRecords() {
          return appAjax('Loading...', {
            type: "GET",
            url: "/_api/contacts?$select=fullname,firstname,lastname,emailaddress1,telephone1",
            contentType: "application/json"
          });
        }
        function addSampleRecord() {
          //Sample data to create a record - change as appropriate
          var recordObj = {
            firstname: "Willie",
            lastname: "Huff" + _.random(100, 999),
            emailaddress1: "Willie.Huff@contoso.com",
            telephone1: "555-123-4567"
          };
          appAjax('Adding...', {
            type: "POST",
            url: "/_api/contacts",
            contentType: "application/json",
            data: JSON.stringify(recordObj),
            success: function(res, status, xhr) {
              recordObj.id = xhr.getResponseHeader("entityid");
              recordObj.fullname = recordObj.firstname + " " + recordObj.lastname;
              table.addRecord(recordObj);
            }
          });
          return false;
        }
        function deleteRecord(recordObj) {
          var response = confirm("Are you sure, you want to delete \"" + recordObj.name + "\" ?");
          if (response == true) {
            appAjax('Deleting...', {
              type: "DELETE",
              url: "/_api/contacts(" + recordObj.id + ")",
              contentType: "application/json",
              success: function(res) {
                table.removeRecord(recordObj);
              }
            });
          }
          return false;
        }
        function updateRecordAttribute(col, recordObj) {
          var attributeName = col.name,
            value = recordObj[attributeName],
            newValue = prompt("Please enter \"" + col.label + "\"", value);
          if (newValue != null && newValue !== value) {
            appAjax('Updating...', {
              type: "PUT",
              url: "/_api/contacts(" + recordObj.id + ")/" + attributeName,
              contentType: "application/json",
              data: JSON.stringify({
                "value": newValue
              }),
              success: function(res) {
                table.updateRecord(attributeName, newValue, recordObj);
              }
            });
          }
          return false;
        }
        var table = new webAPIExampleTable({
          columns: [{
            name: 'firstname',
            label: 'First Name',
            handler: updateRecordAttribute
          }, {
            name: 'lastname',
            label: 'Last Name',
            handler: updateRecordAttribute
          }, {
            name: 'emailaddress1',
            label: 'Email',
            handler: updateRecordAttribute
          }, {
            name: 'telephone1',
            label: 'Telephone',
            handler: updateRecordAttribute
          }],
          data: [],
          addHandler: addSampleRecord,
          deleteHandler: deleteRecord
        });
        loadRecords().done(function(data) {
          table.data = _.map(data.value, function(record){
            record.id = record.contactid;
            return record;
          });
          table.render($('#dataTable'));
        });
      });
    </script>
    <div id="processingMsg" class="alert alert-warning" role="alert"></div>
    <div id="dataTable"></div>
    

    粘贴代码。

  15. 选择保存并关闭

步骤 4. 清除门户缓存

您已经创建了一个用于测试 Web API 功能的 webapi 示例页。 在开始之前,请确保已清除 Power Apps 门户缓存,以便“门户管理”应用中的更改会反映在您的门户上。

重要提示: 清除门户服务器端缓存会导致从 Microsoft Dataverse 重新加载数据时门户性能暂时下降。

若要清除缓存,请执行以下操作:

  1. 作为管理员 Web 角色的成员登录您的门户。

  2. 更改 URL,在结尾处追加 /_‑services/about 。 例如,如果门户 URL 为  https://contoso.powerappsportals.com,请将其更改为  https://contoso.powerappsportals.com/_services/about

    清除缓存。

    注意: 您必须是 管理员 Web 角色的成员才能清除缓存。 如果看到空白屏幕,请检查 Web 角色分配。

  3. 选择 清除缓存

详细信息: 清除门户的服务器端缓存

第 5 步。 使用 Web API 读取、查看、编辑、创建和删除

之前创建的带有 URL webapi 的示例网页现在已可以进行测试。

要测试 Web API 功能:

  1. 使用已分配有您先前创建的 Web API 用户角色的用户帐户登录门户。

  2. 转到先前创建的 webapi 网页。 例如,*https://contoso.powerappsportals.com/webapi*。 Web API 将从 Micrsoft Dataverse 检索记录。

    示例 webapi 网页。

  3. 选择添加示例记录从脚本添加示例记录。

  4. 选择字段。 在此示例中,我们已选择电子邮件以更改联系人的电子邮件地址。

    编辑电子邮件

  5. 选择删除按钮 删除记录。

现在,您已经创建了一个包含读取、编辑、创建和删除记录示例的网页,您可以自定义窗体和布局了。

下一步

编写 HTTP 请求并处理错误

另请参见

门户 Web API 概述
使用 Web API 的门户写入、更新和删除操作
使用 Web API 的门户读取操作
配置列权限

备注

您能告诉我们您的文档语言首选项吗? 进行简短调查。(请注意,此调查是英文版调查)

此调查大约需要七分钟。 不会收集个人数据(隐私声明)。