Blazor .NET6.0 WASM嵌入Power BI报表遇PlatformNotSupportedException错误
Blazor .NET 6 WASM嵌入Power BI报表解决方案
1. 核心思路
Blazor WASM是客户端运行环境,无法直接使用Power BI的.NET SDK(该SDK依赖服务器端运行时特性),因此必须通过后端API中转处理认证和令牌获取,前端仅负责调用后端拿到嵌入令牌后,通过Power BI的JavaScript嵌入库渲染报表。
2. 搭建后端API处理令牌请求
创建ASP.NET Core Web API(可与Blazor WASM同解决方案),完成Power BI服务主体认证和嵌入令牌生成:
- 安装NuGet包:
Microsoft.PowerBI.Api、Microsoft.Identity.Client - 在
appsettings.json配置Power BI服务主体信息:
"PowerBI": { "ClientId": "你的服务主体ID", "ClientSecret": "你的服务主体密钥", "TenantId": "你的Azure租户ID", "WorkspaceId": "目标工作区ID", "ReportId": "目标报表ID" }
- 编写API接口:
[ApiController] [Route("api/powerbi")] public class PowerBiController : ControllerBase { private readonly IConfiguration _config; public PowerBiController(IConfiguration config) => _config = config; [HttpGet("embed-token")] public async Task<IActionResult> GetEmbedToken() { var clientId = _config["PowerBI:ClientId"]; var clientSecret = _config["PowerBI:ClientSecret"]; var tenantId = _config["PowerBI:TenantId"]; var workspaceId = _config["PowerBI:WorkspaceId"]; var reportId = _config["PowerBI:ReportId"]; // 获取服务主体访问令牌 var authority = $"https://login.microsoftonline.com/{tenantId}"; var scopes = new[] { "https://analysis.windows.net/powerbi/api/.default" }; var app = ConfidentialClientApplicationBuilder .Create(clientId) .WithClientSecret(clientSecret) .WithAuthority(authority) .Build(); var tokenResult = await app.AcquireTokenForClient(scopes).ExecuteAsync(); // 生成报表嵌入令牌 using var pbiClient = new PowerBIClient(new Uri("https://api.powerbi.com/"), new TokenCredentials(tokenResult.AccessToken)); var embedToken = await pbiClient.Reports.GenerateTokenAsync(workspaceId, reportId, new GenerateTokenRequest(accessLevel: "View")); return Ok(new { EmbedToken = embedToken.Token, ReportId = reportId, WorkspaceId = workspaceId }); } }
3. Blazor WASM前端集成Power BI JS库
通过JavaScript互操作实现报表嵌入:
- 在
wwwroot/index.html引入Power BI JS脚本:
<script src="https://microsoft.github.io/PowerBI-JavaScript/dist/powerbi.min.js"></script>
- 创建
PowerBiReport.razor组件:
@page "/powerbi-report" @inject HttpClient Http @implements IAsyncDisposable <div id="reportContainer" style="width: 100%; height: 800px;"></div> @code { private IJSObjectReference? _interopModule; private IJSObjectReference? _reportInstance; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // 调用后端获取嵌入信息 var embedData = await Http.GetFromJsonAsync<EmbedData>("api/powerbi/embed-token"); if (embedData != null) { _interopModule = await JS.InvokeAsync<IJSObjectReference>("import", "./powerbi-interop.js"); _reportInstance = await _interopModule.InvokeAsync<IJSObjectReference>("embedReport", "reportContainer", embedData.EmbedToken, embedData.ReportId, embedData.WorkspaceId); } } } public async ValueTask DisposeAsync() { if (_reportInstance != null) { await _reportInstance.InvokeVoidAsync("dispose"); await _reportInstance.DisposeAsync(); } if (_interopModule != null) await _interopModule.DisposeAsync(); } private class EmbedData { public string EmbedToken { get; set; } = string.Empty; public string ReportId { get; set; } = string.Empty; public string WorkspaceId { get; set; } = string.Empty; } }
- 在
wwwroot下创建powerbi-interop.js封装嵌入逻辑:
export async function embedReport(containerId, embedToken, reportId, workspaceId) { const embedConfig = { type: 'report', tokenType: 1, accessToken: embedToken, embedUrl: `https://app.powerbi.com/reportEmbed?reportId=${reportId}&groupId=${workspaceId}`, id: reportId, settings: { panes: { filters: { visible: false } } } }; const container = document.getElementById(containerId); return powerbi.embed(container, embedConfig); }
4. 关键注意事项
- 安全防护:禁止在Blazor WASM客户端处理服务主体密钥或认证逻辑,必须通过后端中转,避免密钥泄露。
- CORS配置:确保后端API允许Blazor WASM的跨域请求,在后端
Program.cs添加:
builder.Services.AddCors(options => { options.AddPolicy("AllowWasm", policy => policy.WithOrigins("https://localhost:5001") // 你的Blazor WASM地址 .AllowAnyHeader() .AllowAnyMethod()); }); app.UseCors("AllowWasm");
- 权限配置:确保Power BI服务主体拥有目标工作区和报表的
Member或Admin权限,否则会导致令牌生成失败。
内容的提问来源于stack exchange,提问作者DeepakArora
相关产品推荐
相关产品推荐

