如何在Blazor项目中嵌入并切换显示多个PowerBI报表
解决方案:Blazor嵌入PowerBI多报表动态切换
核心思路
通过让API接收报表标识参数,动态读取appsettings.json中对应报表的配置,配合Blazor页面传递不同参数,实现不同页面加载不同报表的效果,无需新增控制器。
步骤1:优化配置结构(可选但更规范)
修改appsettings.json,将多报表配置统一归类,便于管理:
{ "AzureAppInfo": { "AuthenticationMode": "ServicePrincipal", "AuthorityUri": "https://login.microsoftonline.com/TenantId", "ClientId": "d7177345-fa2e-4ef5-9d42-619441aa18f0", "TenantId": "32e5e8eb-b522-4d94-b35f-75e6755a9b83", "Scope": "https://analysis.windows.net/powerbi/api/.default", "ClientSecret": "V3d8Q~s3TyFB3aKHxkEsf36UsU2ngP8O~K.UEbcP" }, "PowerBIReports": { "Default": { "WorkspaceId": "08ec7b7f-8b1a-4952-8f9c-c8d65e8d7ab3", "ReportId": "e3ceb447-0ae6-4398-a14d-abf16fe11031" }, "Test": { "WorkspaceId": "08ec7b7f-8b1a-4952-8f9c-c8d65e8d7ab3", "ReportId": "ac1d04be-9d9c-47b7-bcda-fb225ac00ce1" } }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
如果不想修改现有配置结构,可直接跳到步骤2,通过条件判断读取原有的PowerBI和ReportTest节点。
步骤2:修改PowerBIController,支持动态参数
2.1 添加配置模型类
创建对应配置的实体类,用于绑定配置:
public class PowerBIReportConfig { public string WorkspaceId { get; set; } public string ReportId { get; set; } }
2.2 绑定配置(Program.cs/Startup.cs)
在服务注册中绑定多报表配置:
// Program.cs builder.Services.Configure<Dictionary<string, PowerBIReportConfig>>( builder.Configuration.GetSection("PowerBIReports") );
2.3 修改API接口,接收报表标识
更新控制器方法,根据传入的reportKey动态获取对应报表配置:
[ApiController] [Route("api/[controller]")] public class PowerBIController : ControllerBase { private readonly IOptions<Dictionary<string, PowerBIReportConfig>> _reportConfigs; private readonly IConfiguration _configuration; public PowerBIController(IOptions<Dictionary<string, PowerBIReportConfig>> reportConfigs, IConfiguration configuration) { _reportConfigs = reportConfigs; _configuration = configuration; } [HttpGet("{reportKey}")] public async Task<IActionResult> Get(string reportKey) { // 校验报表标识是否存在 if (!_reportConfigs.Value.TryGetValue(reportKey, out var reportConfig)) { return BadRequest("无效的报表标识"); } // 原有认证逻辑不变 var scopes = new[] { _configuration["AzureAppInfo:Scope"] }; var app = ConfidentialClientApplicationBuilder .Create(_configuration["AzureAppInfo:ClientId"]) .WithClientSecret(_configuration["AzureAppInfo:ClientSecret"]) .WithAuthority(new Uri(_configuration["AzureAppInfo:AuthorityUri"])) .Build(); var authResult = await app.AcquireTokenForClient(scopes).ExecuteAsync(); var tokenCredentials = new TokenCredentials(authResult.AccessToken, "Bearer"); var pbiClient = new PowerBIClient(new Uri("https://api.powerbi.com/"), tokenCredentials); // 使用动态获取的配置加载报表 var workspaceId = new Guid(reportConfig.WorkspaceId); var reportId = new Guid(reportConfig.ReportId); var report = pbiClient.Reports.GetReportInGroup(workspaceId, reportId); var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, report.DatasetId); var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(workspaceId, reportId, tokenRequest); var reportViewModel = new EmbeddedReportViewModel( report.Id.ToString(), report.Name, report.EmbedUrl, embedTokenResponse.Token); return Ok(reportViewModel); } // 保持原有EmbeddedReportViewModel定义 public class EmbeddedReportViewModel { public string Id { get; set; } public string Name { get; set; } public string EmbedUrl { get; set; } public string Token { get; set; } public EmbeddedReportViewModel(string id, string name, string embedUrl, string token) { Id = id; Name = name; EmbedUrl = embedUrl; Token = token; } } }
如果保留原有appsettings.json结构,可替换控制器中的配置读取逻辑为:
[HttpGet("{reportType}")] public async Task<IActionResult> Get(string reportType) { string workspaceIdStr, reportIdStr; switch(reportType) { case "Default": workspaceIdStr = _configuration["PowerBI:WorkspaceId"]; reportIdStr = _configuration["PowerBI:ReportId"]; break; case "Test": workspaceIdStr = _configuration["ReportTest:WorkspaceId"]; reportIdStr = _configuration["ReportTest:ReportId"]; break; default: return BadRequest("无效的报表类型"); } // 后续认证、报表加载逻辑不变... }
步骤3:修改Blazor页面,传递报表标识
方案A:单组件多路由(推荐)
将PowerBIReport.razor改为可接收路由参数的组件,不同路由传递不同标识:
@page "/powerbi/{reportKey}" @inject HttpClient Http <div @ref="PowerBIElement" class="powerbi-container"></div> @if (!string.IsNullOrEmpty(errorMessage)) { <p class="text-danger">@errorMessage</p> } @code { [Parameter] public string ReportKey { get; set; } = "Default"; private IJSObjectReference? embedModule; private ElementReference PowerBIElement; private PowerBIController.EmbeddedReportViewModel? reportViewModel; private string errorMessage = string.Empty; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var embedModuleTask = JS.InvokeAsync<IJSObjectReference>("import", "./scripts/powerbi-embed.js"); var reportViewModelTask = Http.GetFromJsonAsync<PowerBIController.EmbeddedReportViewModel>($"api/PowerBI/{ReportKey}"); embedModule = await embedModuleTask; reportViewModel = await reportViewModelTask; StateHasChanged(); } else if (embedModule is not null && reportViewModel is not null) { errorMessage = string.Empty; await embedModule.InvokeVoidAsync("embedReport", PowerBIElement, reportViewModel.Id, reportViewModel.EmbedUrl, reportViewModel.Token); } } }
使用时通过不同路由访问:
/powerbi/Default:加载默认报表/powerbi/Test:加载测试报表
方案B:独立页面调用
如果是两个独立的Blazor页面,直接在各自页面中指定不同的报表标识:
@page "/report-default" @inject HttpClient Http <!-- 页面内容 --> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var embedModuleTask = JS.InvokeAsync<IJSObjectReference>("import", "./scripts/powerbi-embed.js"); var reportViewModelTask = Http.GetFromJsonAsync<PowerBIController.EmbeddedReportViewModel>("api/PowerBI/Default"); // 后续嵌入逻辑同PowerBIReport.razor } } }
@page "/report-test" @inject HttpClient Http <!-- 页面内容 --> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { var embedModuleTask = JS.InvokeAsync<IJSObjectReference>("import", "./scripts/powerbi-embed.js"); var reportViewModelTask = Http.GetFromJsonAsync<PowerBIController.EmbeddedReportViewModel>("api/PowerBI/Test"); // 后续嵌入逻辑同PowerBIReport.razor } } }
内容的提问来源于stack exchange,提问作者William Sewell
相关产品推荐
相关产品推荐

