首页 > 解决方案 > 对 c# 注入服务的 Blazor 服务器 Javascript Websocket 回调不起作用

问题描述

我有一个 blazor 服务器应用程序,它调用注入服务来调用 javascript 中的 Web 套接字以获取一些数据。我希望 Web 套接字将数据返回到服务上的 ac# 实例回调。c# 回调在 javascript 方法中可以正常工作,但不能从任何 websocket 回调(例如 onmessge、onerror、onclose 等)中正常工作。没有错误,只是没有数据。

我已经尝试使实例成为全局 javascript,甚至是调用类中的静态值,但仍然无法正常工作。我已经用谷歌搜索并尝试了每一个建议,但仍然没有运气。

这是索引页面:

@page "/"
@inject BlazorJavaScriptCallback.IService1 _service1

<h1>Hello, world!</h1>

<p>And the answer back from the web service is @Message</p>

@code {

    protected string Message  {get; set;}

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            Message = await _service1.CallWebService();
            StateHasChanged();
        }
    }
}

服务:

using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace BlazorJavaScriptCallback
{
    public interface IService1
    {
        Task<string> CallWebService();
    }


    public class Service1 : IService1
    {

        AutoResetEvent _stopWaitHandle = new AutoResetEvent(false);
        String _message;

        private IJSRuntime _jsruntime;

        public Service1(IJSRuntime jsruntime)
        {
            _jsruntime = jsruntime;
        }

        public async Task<string> CallWebService()
        {
            _message = string.Empty;
            var dotNetObjRef = DotNetObjectReference.Create(this);
            await _jsruntime.InvokeVoidAsync("callWebService", dotNetObjRef, "OutputMessage");
            _stopWaitHandle.WaitOne();

            return _message;
        }

        [JSInvokable]
        public async Task OutputMessage(string message)
        {
            _message = message;
            Console.WriteLine($"**** OutputMessage {message}");
            _stopWaitHandle.Set();
        }

    }
}

的JavaScript:

async function callWebService(instance, method) {

    instance.invokeMethodAsync(method, "start");

    var wsUri = "wss://127.0.0.1:80/Test/";
    var websocket = new WebSocket(wsUri);
    console.log("websocket has been setup");

    websocket.onopen = function (e) {
        console.log("websocket connected");  
        websocket.send("Hellow world")
    };

    websocket.onclose = function (e) {
        instance.invokeMethodAsync(method, "closed");
        console.log("websocket closed");
    };

    websocket.onmessage = function (e) {
        console.log("websocket got message" + e.data);
        instance.invokeMethodAsync(method, e.data);
        websocket.close();
    };

    websocket.onerror = function (e) {
        console.log("websocket error");
        instance.invokeMethodAsync(method, "error");
        websocket.close();
    };

}

您需要将此添加到 ConfigureServices 中的启动中:

services.AddScoped<IService1, Service1>();

您需要将以下内容添加到您的 _Hosts.cshtml:

<script src="~/scripts/Javascript.js"></script>

编辑:当我在没有注入服务的情况下尝试这个时它可以工作(将调用和回调放在剃须刀页面中)。嗯。

标签: javascriptblazorjavascript-interop

解决方案


通过使用 Blazor 的[JSInvokable]标签,在 JS WebSocket 回调函数 onMessage(e) 中调用 .net 中的方法。可以解决问题

例如:

JS文件

var url = "ws://127.0.0.1:1234";
var websocket;
var connected = false;

/**
 * init webSocket
 * @param callback
 * @param value
 * @constructor
 */
function ConnectServer(callback, value) {

    if ('WebSocket' in window) {
        websocket = new WebSocket(url);
    } else if (window.WebSocket) {
        websocket = new WebSocket(url);
    } else if ('MozWebSocket' in window) {
        websocket = new MozWebSocket(url);
    } else {
        alert("The browser version is too low! Please use Chrome, Firefox, IE10+ browsers!");
    }

    websocket.onopen = function () {
        connected = true;
        callback(value);
    }
    websocket.onclose = function (e) {
        connected = false;
    }
    websocket.onmessage = function (e) {
        onMessage(e);
    }
    websocket.onerror = function (e) {
        alert("The websocket server is not connected, please make sure the server is running!")
    };

}

function onMessage(e) {
    var json = JSON.stringify(e.data);
    // call .net method
    DotNet.invokeMethodAsync('BlazorServerSample', 'CallBackMethod', json);
}

/**
 * A shared method for sending information to the server
 * @param jsonStr
 */
function sendMessage(jsonStr) {
    connected ? websocket.send(jsonStr) : alert("websocket server is disconnected or not running")
}


function callWebSocket(data){
    var json = JSON.stringify(data)
    connected ? sendMessage(json ) : ConnectServer(sendMessage, json)
}

Blazor 页面

@code{
    @inject IJSRuntime JSRuntime

    public static string? jsonStr { get; set; }
    [JSInvokable]
    public static Task<int> produceMessage(string json)
    {
        jsonStr = json;
        return Task.FromResult(1);
    }
    
    public async Task CallWebSocket()
    {
        jsonStr = "";
        // mock request data
        data = "{'position': 3,'has_more_items': true,'items_html': 'Car'}";
        await JSRuntime.InvokeVoidAsync("callWebSocket", data);
        
        bool t = true;
        int sec = 0;
        do
        {
            await Task.Delay(1000);
            sec++;
            if (!string.IsNullOrEmpty(jsonStr) || sec > 10)
            {
                t= false;
            }
        } while (t);

        // your business
    }

}

希望我的回答能帮到你


推荐阅读