You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)中,后台脚本无权限直接访问。

解决思路

通过跨上下文消息传递间接完成交互,步骤如下:

  1. Blazor页面初始化后,主动向Background Worker注册自身的DotNetObjectReference
  2. 后台脚本收到ContentScript的请求时,将请求转发给已注册的Blazor页面
  3. Blazor页面调用对应C#方法,将结果通过消息回传给后台
  4. 后台脚本将结果回复给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 07:34:56