教程:使用门户 Web API
备注
从 2022 年 10 月 12 日起,Power Apps 门户更名为 Power Pages。 详细信息请参阅:Microsoft Power Pages 现已正式发布(博客)
不久后我们将迁移 Power Apps 门户文档并将其与 Power Pages 文档合并在一起。
在本教程中,您将设置一个网页和自定义 Web 模板,它们将使用 Web API 从联系人表读取、写入、更新和删除记录。
备注
执行本示例中的步骤时您可以更改列名称或使用不同表。
第 1 步。 创建网站设置
您必须先使用“门户管理”应用启用所需的站点设置,然后才能使用门户 Web API。 站点设置取决于与 Web API 交互时将使用的表。
转到 Power Apps。
选择左侧窗格中的 应用。
选择 门户管理 应用。
在 门户管理 应用的左窗格中,选择 站点设置。
选择 新建。
在 名称 框中,输入 Webapi/contact/enabled。
在 网站 列表中,选择您的网站记录。
在 值 框中输入 true。
选择 保存并关闭。
选择 新建。
在 名称 框中,输入 Webapi/contact/fields。
在 网站 列表中,选择您的网站记录。
在 值 框中,输入
firstname,lastname,fullname,emailaddress1,telephone1选择 保存并关闭。
选择 新建。
在 名称 框中,输入 Webapi/error/innererror。
在 网站 列表中,选择您的网站记录。
在 值 框中输入 true。
选择 保存并关闭。
验证 Web API 的站点设置。
步骤 2. 配置权限
您必须配置权限,以便用户能够使用 Web API 功能。 在此示例中,您将为启用联系人表的表权限,使用 Web API 创建 Web 角色,将联系人表的表权限添加到此 Web 角色,然后将 Web 角色添加到用户以允许他们使用 Web API。
在 门户管理 应用的左窗格中,选择 表权限。
选择 新建。
在 名称 框中,输入 联系人表权限。
在 表名称 列表中,选择 联系人(联系人)。
在 网站 列表中,选择您的网站记录。
在 访问类型 列表中,选择 全局。
选择读取、写入、创建和删除权限。
选择 保存并关闭。
创建 Web 角色
您可以在网站中使用现有的 Web 角色或创建新 Web 角色。
在左窗格中选择 Web 角色 。
选择 新建。
在 名称 框中,输入 Web API 用户(或最能反映访问此功能的用户角色的任何名称)。
在 网站 列表中,选择您的网站记录。
选择 保存。
添加相关表权限
对于新的或现有 Web 角色,选择 相关 > 表权限。
选择 添加现有表权限。
选择先前创建的 联系人表权限。
选择 添加。
选择 保存并关闭。
将联系人添加到 Web 角色
在左窗格中选择 联系人。
选择要在此示例中用于 Web API 的联系人。
备注
此联系人是在此示例中用于测试 Web API 的用户帐户。 请务必在您的门户中选择正确的联系人。
选择 相关 > Web 角色。
选择 添加现有 Web 角色。
选择之前创建的 Web API 用户 角色。
选择 添加。
选择 保存并关闭。
步骤 3. 创建网页
现在,您已启用 Web API 并且配置了用户权限,请创建一个带有示例代码的网页以查看、编辑、创建和删除记录。
在 门户管理 应用的左窗格中,选择网页。
选择新建。
在名称框中,输入 webapi。
在网站列表中,选择您的网站记录。
对于父页,请选择主页。
对于部分 URL,请输入 webapi。
对于页面模板,请选择主页。
对于发布状态,请选择已发布。
选择保存。
选择相关 > 网页。
从网页关联视图中,选择 webapi。
向下滚动到内容部分,然后转到复制(HTML)(HTML 设计器)。
选择 HTML 选项卡。
复制以下示例代码片段并将其粘贴到 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>
选择保存并关闭。
步骤 4. 清除门户缓存
您已经创建了一个用于测试 Web API 功能的 webapi 示例页。 在开始之前,请确保已清除 Power Apps 门户缓存,以便“门户管理”应用中的更改会反映在您的门户上。
重要提示: 清除门户服务器端缓存会导致从 Microsoft Dataverse 重新加载数据时门户性能暂时下降。
若要清除缓存,请执行以下操作:
作为管理员 Web 角色的成员登录您的门户。
更改 URL,在结尾处追加 /_‑services/about 。 例如,如果门户 URL 为 https://contoso.powerappsportals.com,请将其更改为 https://contoso.powerappsportals.com/_services/about。
注意: 您必须是 管理员 Web 角色的成员才能清除缓存。 如果看到空白屏幕,请检查 Web 角色分配。
选择 清除缓存。
详细信息: 清除门户的服务器端缓存
第 5 步。 使用 Web API 读取、查看、编辑、创建和删除
之前创建的带有 URL webapi 的示例网页现在已可以进行测试。
要测试 Web API 功能:
使用已分配有您先前创建的 Web API 用户角色的用户帐户登录门户。
转到先前创建的 webapi 网页。 例如,*https://contoso.powerappsportals.com/webapi*。 Web API 将从 Micrsoft Dataverse 检索记录。
选择添加示例记录从脚本添加示例记录。
选择字段。 在此示例中,我们已选择电子邮件以更改联系人的电子邮件地址。
选择
删除记录。
现在,您已经创建了一个包含读取、编辑、创建和删除记录示例的网页,您可以自定义窗体和布局了。
下一步
另请参见
门户 Web API 概述
使用 Web API 的门户写入、更新和删除操作
使用 Web API 的门户读取操作
配置列权限