教程:为客户将 Power BI 报表嵌入应用程序

在本教程中,你将了解如何将 Power BI 报表嵌入到 .NET 5.0 应用程序,这属于“为客户嵌入内容”(也称为“应用拥有数据”)解决方案的一部分。 在“为客户嵌入内容”解决方案中,应用用户无需登录到 Power BI 或拥有 Power BI 许可证。

在本教程中,你将了解如何嵌入:

  • Power BI 报表。
  • 在“为客户嵌入内容”应用中。
  • 使用服务主体。
  • 使用 .NET 5.0。
  • 通过 Microsoft.Identity.Web 库(.NET Core 也支持此库)。

注意

本教程中使用的完整解决方案可从 DOTNET5-AppOwnsData-Tutorial GitHub 存储库获取。

先决条件

资源

在本教程中,你使用:

方法

若要在“为客户嵌入内容”解决方案中嵌入 Power BI 内容,请执行以下步骤:

  1. 配置 Microsoft Entra 应用和服务主体

  2. 获取嵌入的参数值

  3. 添加所需的 NuGet 包

  4. 启用服务器端身份验证

  5. 构建应用的客户端

  6. 运行应用程序

步骤 1 - 配置 Microsoft Entra 应用和服务主体

在本教程中,你将使用服务主体向 Microsoft Entra ID 验证你的 Web 应用。 你还需要一个 Microsoft Entra 应用,以便能够生成 Microsoft Entra 令牌。 通过使用 Microsoft Entra 令牌,你的 Web 应用可以调用 Power BI REST API 并嵌入 Power BI 项,例如报表、仪表板和磁贴。

按照服务主体说明创建 Microsoft Entra 应用,使应用服务主体能够使用 Power BI 内容。

步骤 2 - 获取嵌入的参数值

若要嵌入报表,需要以下值:

域和租户 ID

如果不清楚自己的域或租户 ID,请参阅查找 Microsoft Entra 租户 ID 和主域名

注意

若要为其他租户上的用户(来宾用户)嵌入内容,你需要调整 authorityUri 参数。

客户端 ID

若要获取客户端 ID GUID(也称为“应用程序 ID”),请执行以下步骤:

  1. 登录 Microsoft Azure

  2. 搜索“应用程序注册”,然后选择“应用程序注册”链接。

  3. 选择用于嵌入 Power BI 内容的 Microsoft Entra 应用。

  4. 从“概述”部分,复制“应用程序(客户端) ID”GUID 。

客户端机密

若要获取客户端机密,请执行下列步骤:

  1. 登录 Microsoft Azure

  2. 搜索“应用程序注册”,然后选择“应用程序注册”链接。

  3. 选择用于嵌入 Power BI 内容的 Microsoft Entra 应用。

  4. 在“管理”下,选择“证书和机密”

  5. 在“客户端机密”下,选择“新建客户端密钥” 。

  6. 在“添加客户端机密”弹出窗口中,提供应用程序机密的说明,选择应用程序机密过期时间,然后选择“添加” 。

  7. 从“客户端机密”部分,复制新创建的应用程序机密的“值”列中的字符串 。 客户端机密值为你的客户端 ID。

注意

请确保在第一次出现客户端密码值时复制它。 在你离开此页面后,客户端密码值将会隐藏起来,你将无法检索它。

工作区 ID

若要获取工作区 ID GUID,请执行以下步骤:

  1. 登录 Power BI 服务。

  2. 打开要嵌入的报表。

  3. 复制 URL 中的 GUID。 GUID 是 /groups/ 和 /reports/ 之间的数字 。

    A screenshot showing workspace ID GUID in the Power B I service U R L

注意

若要以编程方式获取工作区 ID,请使用获取组 API。

报表 ID

若要获取报表 ID GUID,请执行以下步骤:

  1. 登录 Power BI 服务。

  2. 打开要嵌入的报表。

  3. 复制 URL 中的 GUID。 GUID 是 /reports/ 和 /ReportSection 之间的数字 。

    A screenshot showing report ID GUID in the Power B I service U R L

注意

若要以编程方式获取报表 ID,请使用获取组中的报表 API。

步骤 3 - 添加所需的 NuGet 包

在开始之前,需要将 Microsoft.Identity.WebMicrosoft.PowerBI.Api NuGet 包添加到应用中。

向你的应用添加所需的 NuGet 包:

  • 在 VS Code 中,打开终端并输入以下代码。

  • 在 Visual Studio 中,导航到“工具”>“NuGet 包管理器”>“包管理器控制台”,然后键入以下代码。

dotnet add package Microsoft.Identity.Web
dotnet add package Microsoft.Identity.Web.UI
dotnet add package Microsoft.PowerBI.Api

步骤 4 - 启用服务器端身份验证

通过创建或修改下表中的文件,在应用中启用服务器端身份验证。

文件 用途
Startup.cs 初始化 Microsoft.Identity.Web 身份验证服务
appsettings.json 配置身份验证详细信息
PowerBiServiceApi.cs 获取 Microsoft Entra 令牌和嵌入元数据
HomeController.cs 将嵌入数据作为模型传递到视图

配置启动文件以支持 Microsoft.Identity.Web

修改 Startup.cs 中的代码以正确初始化 Microsoft.Identity.Web 提供的身份验证服务。

将以下代码添加到应用的 Startup.cs 文件。

注意

ConfigureServices 中的代码完成了几项重要任务:

  1. AddMicrosoftWebAppCallsWebApi 的调用配置 Microsoft 身份验证库,以获取访问令牌(Microsoft Entra 令牌)。
  2. AddInMemoryTokenCaches 的调用配置一个令牌缓存,Microsoft 身份验证库将使用该缓存在后台缓存访问令牌和刷新令牌。
  3. services.AddScoped(typeof(PowerBiServiceApi)) 的调用将 PowerBiServiceApi 类配置为可使用依赖项注入添加到其他类的服务类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppOwnsData.Services;

namespace AppOwnsData
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services) {

    services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
            .EnableTokenAcquisitionToCallDownstreamApi()
            .AddInMemoryTokenCaches();

            services.AddScoped(typeof(PowerBiServiceApi));

            services.AddControllersWithViews(options => {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            });
            services.AddRazorPages()
                    .AddMicrosoftIdentityUI();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You might want to change this for production scenarios. See https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
        }
    }
}

创建身份验证详细信息文件

在本教程中,appsettings.json 文件包含敏感信息,例如客户端 ID 和客户端机密。 出于安全考虑,不建议在设置文件中保留此信息。 在应用程序中嵌入时,请考虑使用更安全的工具(如 Azure Key Vault)来保护敏感信息。

  1. 在项目中,创建一个新文件并将其命名为 appsettings.json。

  2. 然后将以下代码添加到 appsettings.json 中:

    {
     "AzureAd": {
       "Instance": "https://login.microsoftonline.com/",
       "Domain": "yourtenant.onMicrosoft.com",
       "TenantId": "",
       "ClientId": "",
       "ClientSecret": "",
       "CallbackPath": "/signin-oidc",
       "SignedOutCallbackPath": "/signout-callback-oidc"
     },
     "PowerBi": {
       "ServiceRootUrl": "https://api.powerbi.com/"
     },
     "Logging": {
       "LogLevel": {
         "Default": "Information",
         "Microsoft": "Warning",
         "Microsoft.Hosting.Lifetime": "Information"
       }
     },
    "AllowedHosts": "*"
    }
    
  3. 填写从步骤 2 - 获取嵌入参数值获取的嵌入参数值。

注意

在上述代码中,将 PowerBi:ServiceRootUrl 参数作为自定义配置值添加,以跟踪 Power BI 服务的基 URL。 在对 Microsoft 公有云中的 Power BI 服务进行编程时,URL 为 https://api.powerbi.com/。 但是,Power BI 服务的根 URL 在其他云(例如政府云)中将有所不同。 因此,自定义配置值存储为项目配置值,从而可以根据需要对其进行更改。

获取 Microsoft Entra 访问令牌,并调用 Power BI 服务

为了嵌入 Power BI 内容(例如报表和仪表板),应用需要获取 Microsoft Entra 令牌。 若要获取令牌,则需要一个配置对象

本部分中的代码使用 .NET Core 依赖项注入模式。 当你的类需要使用服务时,你可以为该服务添加构造函数参数。 .NET Core 运行时负责在运行时传递服务实例。 在这种情况下,构造函数使用 IConfiguration 参数注入 .NET Core 配置服务的实例,该参数用于从 appsettings.json 中检索 PowerBi:ServiceRootUrl 配置值。 ITokenAcquisition 参数名为 tokenAcquisition,它包含对 Microsoft.Identity.Web 库提供的 Microsoft 身份验证服务的引用。 ITokenAcquisition 参数用于从 Microsoft Entra ID 获取访问令牌。

RequiredScopes 字段包含一个字符串数组,该数组包含由 Power BI 服务 API 支持的一组委托权限。 当应用程序通过网络调用以获取 Microsoft Entra 令牌时,该应用程序将传递这组委托权限,以便 Microsoft Entra ID 可以将其包含在其返回的访问令牌中。

注意

验证是否已使用 Web 应用所需的范围配置 Microsoft Entra 应用。 有关详细信息,请参阅更改 Microsoft Entra 应用的权限

  1. 在应用的项目中,创建名为“服务”的新文件夹。

  2. 在“服务”文件夹中,创建名为“PowerBiServiceApi.cs”的新文件 。

  3. 将以下代码添加到 PowerBiServiceApi.cs 中。

    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Identity.Web;
    using Microsoft.Rest;
    using Microsoft.PowerBI.Api;
    using Microsoft.PowerBI.Api.Models;
    using Newtonsoft.Json;
    
    namespace AppOwnsData.Services {
    
        // A view model class to pass the data needed to embed a single report
        public class EmbeddedReportViewModel {
            public string Id;
            public string Name;
            public string EmbedUrl;
            public string Token;
        }
    
        public class PowerBiServiceApi {
    
            private ITokenAcquisition tokenAcquisition { get; }
            private string urlPowerBiServiceApiRoot { get; }
    
            public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) {
                this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"];
                this.tokenAcquisition = tokenAcquisition;
            }
    
            public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default";
    
            // A method to get the Azure AD token (also known as 'access token')
            public string GetAccessToken() {
                return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result;
            }
    
            public PowerBIClient GetPowerBiClient() {
                var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer");
                return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials);
            }
    
            public async Task<EmbeddedReportViewModel> GetReport(Guid WorkspaceId, Guid ReportId) {
    
                PowerBIClient pbiClient = GetPowerBiClient();
    
                // Call the Power BI service API to get the embedding data.
                var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId);
    
                // Generate a read-only embed token for the report.
                var datasetId = report.DatasetId;
                var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId);
                var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest);
                var embedToken = embedTokenResponse.Token;
    
                // Return the report embedded data to caller.
                return new EmbeddedReportViewModel {
                    Id = report.Id.ToString(),
                    EmbedUrl = report.EmbedUrl,
                    Name = report.Name,
                    Token = embedToken
                };
            }
    
        }
    }
    

修改 HomeController.cs 文件

在此代码示例中,使用依赖项注入来修改 HomeController.cs 文件。 按照上一步操作,通过在 ConfigureServices 方法中调用 services.AddScopedPowerBiServiceApi 类配置为服务。 使用此代码将 PowerBiServiceApi 参数添加到构造函数,.NET Core 运行时将创建一个 PowerBiServiceApi 实例并将其传递给构造函数。

在 Controllers 文件夹中,打开 HomeController.cs 文件,并向其中添加以下代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using AppOwnsData.Models;
using AppOwnsData.Services;

namespace AppOwnsData.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private PowerBiServiceApi powerBiServiceApi;

        public HomeController(PowerBiServiceApi powerBiServiceApi)
        {
            this.powerBiServiceApi = powerBiServiceApi;
        }

        [AllowAnonymous]
        public IActionResult Index()
        {
            return View();
        }

        public async Task<IActionResult> Embed() {

            // Replace these two GUIDs with the workspace ID and report ID you recorded earlier.
            Guid workspaceId = new Guid("11111111-1111-1111-1111-111111111111");
            Guid reportId = new Guid("22222222-2222-2222-2222-222222222222");

            var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId);

            return View(viewModel);
        }

        [AllowAnonymous]
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

步骤 5 - 构建应用的客户端

对于客户端实现,需要创建或修改下表中列出的文件:

文件 用途
embed.js 包含客户端 JavaScript 代码
Embed.cshtml 包含应用的文档对象模型 (DOM),以及用于嵌入报表的 DIV

为嵌入式报表创建容器

在本教程中,创建 Embed.cshtml 文件,该文件包含一个 div 元素(这是嵌入式报表的容器)和三个脚本。

  1. 在“视图”/“主页”文件夹中,创建名为 Embed.cshtml 的文件 。

  2. 将以下代码添加到 Embed.cshtml 文件。

    @model AppOwnsData.Services.EmbeddedReportViewModel;
    
    <div id="embed-container" style="height:800px;"></div>
    
    @section Scripts {
    
        <!-- powerbi.min.js is the JavaScript file that loads the client-side Power BI JavaScript API library.
        Make sure that you're working with the latest library version.
        You can check the latest library available in https://cdnjs.com/libraries/powerbi-client -->
        <script src="https://cdn.jsdelivr.net/npm/powerbi-client@2.18.0/dist/powerbi.min.js"></script>
    
        <!-- This script creates a JavaScript object named viewModel which is accessible to the JavaScript code in embed.js. -->
        <script> 
            var viewModel = {
                reportId: "@Model.Id",
                embedUrl: "@Model.EmbedUrl",
                token: "@Model.Token"
            }; 
        </script>
    
        <!-- This script specifies the location of the embed.js file -->
        <script src="~/js/embed.js"></script>
    }
    

添加客户端 JavaScript 以嵌入报表

若要嵌入 Power BI 内容,需要创建一个配置对象。 若要详细了解如何创建配置对象,请参阅嵌入报表

在本教程中,你将创建一个名为 embed.js 的 JavaScript 文件,其中带有一个配置对象,用于嵌入使用 models 变量的报表。

可以使用对 window['powerbi-client'].models 的调用来初始化 modelsmodels 变量用于设置配置值,如 models.Permissions.Allmodels.TokenType.Aadmodels.ViewMode.View

powerbi.embed 函数使用 models 配置对象嵌入报表。

  1. 在“wwwroot”/“js”文件夹中,创建名为 embed.js 的文件 。

  2. 将以下代码添加到 embed.js 文件。

    $(function () {
    
        // 1 - Get DOM object for the div that's the report container.
        var reportContainer = document.getElementById("embed-container");
    
        // 2 - Get the report embedding data from the view model.
        var reportId = window.viewModel.reportId;
        var embedUrl = window.viewModel.embedUrl;
        var token = window.viewModel.token
    
        // 3 - Embed the report by using the Power BI JavaScript API.
        var models = window['powerbi-client'].models;
    
        var config = {
          type: 'report',
          id: reportId,
          embedUrl: embedUrl,
          accessToken: token,
          permissions: models.Permissions.All,
          tokenType: models.TokenType.Embed,
          viewMode: models.ViewMode.View,
          settings: {
            panes: {
              filters: { expanded: false, visible: true },
              pageNavigation: { visible: false }
            }
          }
        };
    
        // Embed the report and display it within the div container.
        var report = powerbi.embed(reportContainer, config);
    
        // 4 - Add logic to resize the embed container on a window resize event.
        var heightBuffer = 12;
        var newHeight = $(window).height() - ($("header").height() + heightBuffer);
        $("#embed-container").height(newHeight);
        $(window).resize(function () {
          var newHeight = $(window).height() - ($("header").height() + heightBuffer);
          $("#embed-container").height(newHeight);
        });
    
      });
    

步骤 6 - 运行应用程序

完成上述所有步骤后,即可运行应用程序。 尝试运行应用程序,并试验 Power BI 报表的嵌入方式。 可以使用 Power BI 嵌入式分析客户端 API 来通过客户端 API 增强你的应用。

重要

如果使用免费嵌入试用令牌进行开发,则必须购买生产容量。 在购买容量之前,“免费试用版”横幅将继续显示在嵌入式报表顶部。

应用准备就绪后,可以将嵌入式应用移动到生产环境