'Blazor server Javascript Websocket callback to c# injected service does not work
I have a blazor server app, that calls an injected service to call a web socket in javascript to get some data. I want the web socket to return the data to a c# instance callback on the service. The c# callback works fine from the javascript method, but not from the any of the websocket callbacks (e.g. onmessge, onerror, onclose, etc.). No errors, just no data.
I have tried making the instance a global javascript and even a static value in the calling class, but still does not work. I have googled to no end and tried every suggestion, but still no luck.
Here is index page:
@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();
}
}
}
The service:
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();
}
}
}
The 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();
};
}
You will need to add this to the startup in ConfigureServices:
services.AddScoped<IService1, Service1>();
You will need to add the following to your _Hosts.cshtml:
<script src="~/scripts/Javascript.js"></script>
EDIT: it works when I try this without an injected service (putting the call and callback right in the razor page). Hrmmm.
Solution 1:[1]
By using Blazor's [JSInvokable] tag, call the method in .net in the JS WebSocket callback function onMessage(e). can solve the problem
E.g:
JS file
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)
}
BlazorPage
@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
}
}
Hope my answer can help you
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | SmallProgram |
