Blazor浏览器扩展:从BackgroundWorker.js调用C# [JSInvokable]方法问题
Blazor浏览器扩展:从Background Worker调用C# JSInvokable方法问题
我在开发带自定义文件选择器的Blazor浏览器扩展时,尝试从BackgroundWorker.js调用标记了[JSInvokable]的C#方法。该调用在Index.razor页面内可正常工作,但仅处理浏览器标签页事件、无嵌入页面的场景下,后台脚本调用完全无响应(存在明显异常)。想咨询这种场景是否可行,以及对应的解决办法。

相关代码
FilesChooser.cs
public class FilesChooser { public FilesChooser(string? name) { Name = name ?? "No Name"; } public string? Name { get; set; } [JSInvokable] public string GetHelloMessage() => $"Hello, {Name}!"; }
JsInteropClasses3.cs
public class JsInteropClasses3 { private readonly IJSRuntime js; public JsInteropClasses3(IJSRuntime js) { this.js = js; } public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name) { using var objRef = DotNetObjectReference.Create(new FilesChooser(name)); return await js.InvokeAsync<string>("GetHelloMessage", objRef); } }
Index.razor(可正常工作的调用)
@code { private string? name; private string? result; private JsInteropClasses3? jsInteropClasses; protected override void OnInitialized() { jsInteropClasses = new JsInteropClasses3(JS); } private async Task TriggerDotNetInstanceMethod() { if (jsInteropClasses is not null) { result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name); } } }
BackgroundWorker.js(无响应的调用)
import * as _ from "/content/Blazor.BrowserExtension/lib/browser-polyfill.min.js"; browser.runtime.onInstalled.addListener(() => { const indexPageUrl = browser.runtime.getURL("index.html"); browser.tabs.create({ url: indexPageUrl }); }); browser.runtime.onMessage.addListener(function (request, sender, sendResponse) { let rez = ''; browser.invokeMethodsAsync = async (element) => { rez = await element.dotNetHelper.invokeMethodAsync('GetHelloMessage'); sendResponse({ message: "Background has received that message 🔥" + rez}); } return true; });
ContentScript.js(事件转发)
parent.addEventListener('message', function (event, sender, sendResponse) { if (event.data.type == 'notification') { chrome.runtime.sendMessage({ message: "openFileSelector" }, function (response) { console.log("send event"); console.log(response); window.parent.postMessage( { type: "notification_rez", data: "2222" }, "*", ); }); } });
可行性分析与解决方案
这种场景完全可行,核心问题在于Background Worker是独立于Blazor渲染进程的隔离环境,它无法直接获取Blazor的DotNetObjectReference或调用Blazor的JS互操作方法——Blazor运行时仅存在于自身页面(如index.html)中,后台脚本无权限直接访问。
解决思路
通过跨上下文消息传递间接完成交互,步骤如下:
- Blazor页面初始化后,主动向Background Worker注册自身的
DotNetObjectReference - 后台脚本收到ContentScript的请求时,将请求转发给已注册的Blazor页面
- Blazor页面调用对应C#方法,将结果通过消息回传给后台
- 后台脚本将结果回复给ContentScript
具体实现示例
1. 修改Index.razor,注册实例并监听后台消息
@inject IJSRuntime JS @implements IAsyncDisposable @code { private DotNetObjectReference<FilesChooser>? _fileChooserRef; protected override async Task OnInitializedAsync() { // 创建FilesChooser的引用 _fileChooserRef = DotNetObjectReference.Create(new FilesChooser("Blazor")); // 向后台注册Blazor实例 await JS.InvokeVoidAsync("registerBlazorInstance", _fileChooserRef); } [JSInvokable] public string GetHelloMessage() => $"Hello, {_fileChooserRef?.Value.Name}!"; public async ValueTask DisposeAsync() { _fileChooserRef?.Dispose(); } }
2. 添加全局JS脚本(wwwroot/js/backgroundInterop.js)
let blazorDotNetRef = null; // 注册Blazor实例 window.registerBlazorInstance = (dotNetRef) => { blazorDotNetRef = dotNetRef; }; // 初始化后台消息监听 browser.runtime.onMessage.addListener(async (request, sender, sendResponse) => { if (request.message === "openFileSelector" && blazorDotNetRef) { // 调用Blazor的C#方法 const result = await blazorDotNetRef.invokeMethodAsync('GetHelloMessage'); sendResponse({ message: "Background has received that message 🔥" + result }); } return true; });
3. 修改BackgroundWorker.js,移除无效逻辑
import * as _ from "/content/Blazor.BrowserExtension/lib/browser-polyfill.min.js"; browser.runtime.onInstalled.addListener(() => { const indexPageUrl = browser.runtime.getURL("index.html"); browser.tabs.create({ url: indexPageUrl }); }); // 消息处理逻辑已移交backgroundInterop.js
4. 在Index.html中引入新脚本
<script src="_framework/blazor.webassembly.js"></script> <script src="js/backgroundInterop.js"></script>
关键说明
- 必须确保Blazor页面完成初始化后,后台才能拿到有效的
DotNetObjectReference;若后台需处理页面未加载时的请求,可先缓存请求,等Blazor注册后再执行 - 浏览器扩展的不同上下文(Content Script、Background Worker、Blazor页面)之间,仅能通过
browser.runtime.sendMessage/browser.runtime.onMessage通信,无法直接共享对象或调用方法
内容的提问来源于stack exchange,提问作者velesUst
相关产品推荐
相关产品推荐

