How to connect from JavaScript to SignalR in Cross domain? - javascript

I have an signalR application which listen from different clients such as C#, JavaScript later we may extend it for other technologies. My signalR server is in console Application. right now I created .net client which are successfully connected to server. I tried a lot to write a javascript client but i didn't succeed. please help me to fix it.
1. I created server.Js file by running the command signalr.exe ghp /path: and include it in Js page
2. I am using 2.0.1 version for Owin and SignalR
3. tried with server URL http://127.0.0.1:8088/signalr and http://127.0.0.1:8088
Here is my server code
[assembly: Microsoft.Owin.OwinStartup(typeof(Program.Startup))]
namespace SignalRServer
{
internal class Program
{
private static IDisposable SignalR;
private static void Main(string[] args)
{
string url = "http://127.0.0.1:8088/signalr";
SignalR = WebApp.Start(url);
Console.ReadKey();
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
[HubName("MyHub")]
public class MyHub : Hub
{
public void Send(string name, string message)
{
Console.WriteLine($"{name}:{message}");
Clients.All.addMessage(name, message);
}
}
}
}
My JS Client Code
var con = $.hubConnection('http://127.0.0.1:8088');
alert(con);
var hub = con.createHubProxy('MyHub');
con.start().done(function () {
alert('Now connected, connection ID=' + connection.id);
});
hub.invoke('Send');

Related

webSocket.send() inside webSocket.onopen always fails

I'm starting with WebSockets and, to try things off, I built a minuscule WebSocket server in C# (using WebSocketSharp) and an html as a client to connect to it.
On the client I have this code:
const webSocket = new WebSocket("ws://localhost:51036/test");
webSocket.onopen = e => {
webSocket.send("hello :D");
console.log("Socket connection opened!");
}
This send() call never works — it sometimes result in an exception in the C# server, most other times it is just ignored as if nothing was ever received on the server's end. However, any send() I call afterwards (e.g. with a button) works fine, and send() inside onopen also works fine if I place it inside a timeout of just 200ms. If I don't call any send() onopen, and just call it later via a button, it always works fine (so it's not a 'first call always fails' issue).
On the C# side, the code is very simple:
public class TestWS : WebSocketBehavior {
protected override void OnMessage (MessageEventArgs e) {
System.Diagnostics.Debug.WriteLine($"Client message: {e.Data}");
Send($"String length: {e.Data.Length} [msg received: {e.Data}]");
}
}
public static class Sockets {
public const string SERVER_ROUTE = "ws://localhost:51036";
private static WebSocketServer socket;
public static void Start () {
socket = new WebSocketServer(SERVER_ROUTE);
socket.AddWebSocketService<TestWS>("/test");
socket.Start();
System.Diagnostics.Debug.WriteLine($"Server started on {SERVER_ROUTE}");
}
public static void Stop () {
socket.Stop();
}
}
The exception in question is this one:
2021-09-06 19:59:06|Fatal|WebSocket.<startReceiving>b__175_2:0|WebSocketSharp.WebSocketException: The header of a frame cannot be read from the stream.
at WebSocketSharp.WebSocketFrame.processHeader(Byte[] header)
at WebSocketSharp.WebSocketFrame.<>c__DisplayClass73_0.<readHeaderAsync>b__0(Byte[] bytes)
at WebSocketSharp.Ext.<>c__DisplayClass55_0.<ReadBytesAsync>b__0(IAsyncResult ar)

Is there a way to create bi-directional communication between a shell app and a webview in .NET 6 MAUI?

Recently I've had the assignment to create a bi-directional interop bridge between a shell app and a webpage in .NET MAUI. Not finding any way to solve this I had the idea of creating it in Xamarin.Forms first seeing as MAUI is a continuation on it.
After having created this app, I've tried to convert it over to MAUI using Microsoft's instructions on the dotnet/maui github wiki.
The main problem i'm encountering right now is that I've been using extensions on Android's WebViewRenderer, WebViewClient and Java.Lang.Object to be able to send and receive javascript to and from the WebView.
public class ExtendedWebViewRenderer : WebViewRenderer
{
private const String JavascriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
public ExtendedWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
((ExtendedWebView)Element).Cleanup();
}
if (e.NewElement != null)
{
Control.SetWebViewClient(new JavascriptWebViewClient($"javascript: {JavascriptFunction}"));
Control.AddJavascriptInterface(new JsBridge(this), "jsBridge");
}
}
}
public class JavascriptWebViewClient : WebViewClient
{
private readonly String _javascript;
public JavascriptWebViewClient(String javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(WebView view, String url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
public class JsBridge : Java.Lang.Object
{
private readonly WeakReference<ExtendedWebViewRenderer> _extendedWebViewMainRenderer;
public JsBridge(ExtendedWebViewRenderer extendedRenderer)
{
_extendedWebViewMainRenderer = new WeakReference<ExtendedWebViewRenderer>(extendedRenderer);
}
[JavascriptInterface]
[Export("invokeAction")]
public void InvokeAction(String data)
{
if (_extendedWebViewMainRenderer != null && _extendedWebViewMainRenderer.TryGetTarget(out var extendedRenderer))
{
((ExtendedWebView)extendedRenderer.Element).InvokeAction(data);
}
}
}
All three of these are either not available right now, or will not be implemented in MAUI, since a lot of platform dependent code has been automated now. Which leaves me with the problem that I can't seem to figure out how to change my current code to accomplish the same thing in MAUI.
Seeing as MAUI is currently not even fully released, I was wondering if this is currently just not possible or if there is some workaround to make it possible.
Any help would be greatly appreciated.
Calling C# from a webview is actually extremely simple. Just navigate and then intercept that in c#.
the xaml:
<WebView WidthRequest="400" HeightRequest="400" Navigating="WebView1_Navigating">
<WebView.Source>`enter code here`
<HtmlWebViewSource>
<HtmlWebViewSource.Html>
<![CDATA[
<HTML>
<script>
function callCsharp(){
window.location.href = 'http://poc.MyFunction?name=john&country=DK';
}
</script>
<BODY
A link that triggers C#
<br>
<button onclick="callCsharp()" type="button">A button calling javascript</button>
</BODY>
</HTML>
]]>
</HtmlWebViewSource.Html>
</HtmlWebViewSource>
</WebView.Source>
The C#:
private async void WebView1_Navigating(object sender, WebNavigatingEventArgs e)
{
var urlParts = e.Url.Split(".");
if (urlParts[0].ToLower().Equals("http://poc"))
{
var funcToCall = urlParts[1].Split("?");
var methodName = funcToCall[0];
var funcParams = funcToCall[1];
Debug.WriteLine("Calling " + methodName);
// prevent the navigation to complete
e.Cancel = true;
// TODO smart parsing and type casting of parameters and then some reflection magic
}
}
MAUI's default WebView has the Eval and EvaluateJavaScriptAsync functions to call JavaScript code from C#:
Eval just executes the script string you pass in a fire-and-forget way.
EvaluateJavaScriptAsync needs to be awaited but also returns a string with a stringified result of the data that the script returned.
If you want to use callback/bridge methods to automatically receive data from the JavaScript side without any input from the C# side of the app, you will have to extend the default per-platform renderers to add that functionality. The good news is that there is an official tutorial on how to do it for Xamarin Forms at Customizing a WebView which is almost straightforward to port to .NET MAUI - you only have to change how renderers are registered.

In Android, JavaScript Interface Injection Vulnerability

My application received [Remediation for JavaScript Interface Injection Vulnerability] from Google PlayStore.
Webview control via javascript interface in non-https webpage.
This is my solution.
It is correct for this issue? or how to modify?
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if(request.getUrl().getHost().equals("mydomain.com")) {
view.addJavascriptInterface(new MyJavaScriptInterface(), "myview");
} else {
view.removeJavascriptInterface("myview");
}
return super.shouldOverrideUrlLoading(view, request);
}
I solved using https url. I installed on my site an ssl certificate.

How to receive scriptNotify from html in appdata on UWP c#

Windows won't let my WebView_ScriptNotify Event receive a call if the html is loaded from ms-appdata.
I'm aware that I can use the ms-appx-web protocol to load such a file from my app bundle, but this is no option because the data to show are downloaded after install of the app.
I also can't just use webView.navigateToString because this won't include the referenced libraries in the html file.
Currently I'm trying something like this in my Class.xaml.cs
WebView webView = new WebView();
webView.ScriptNotify += WebView_ScriptNotify;
Uri navigationUri = new Uri(#"ms-appdata:///local/index.html");
webView.Navigate(navigationUri);
and
private void WebView_ScriptNotify(object sender, NotifyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
//I want to do the magic here, but this will never be called
}
in the html file is
<div id="content">
<div class="btn" onClick="window.external.notify('hello world');"</div>
</div>
Furthermore, it's no option to use InvokeScript(), because I don't know when the event must be fired and the values for it.
Yet it's mandatory to use files from ms-appdata.
Do you know a solution for this?
Even an alternative workaroung would amaze me.
Ref Script notify changes in XAML:
For content to be able to send notifications the following conditions apply:
The source of the page should be from the local system via NavigateToString(), NavigateToStream() or ms-appx-web:///
Or
The source of the page is delivered via https:// and the site domain name is listed in the app content URI’s section of the package manifest.
So to solve this issue, we can use WebView.NavigateToLocalStreamUri method with the protocol ms-local-stream://, rather than ms-appdata://. For example:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// The 'Host' part of the URI for the ms-local-stream protocol needs to be a combination of the package name
// and an application-defined key, which identifies the specific resolver, in this case 'MyTag'.
Uri url = webView.BuildLocalStreamUri("MyTag", "index.html");
StreamUriWinRTResolver myResolver = new StreamUriWinRTResolver();
// Pass the resolver object to the navigate call.
webView.NavigateToLocalStreamUri(url, myResolver);
}
private void webView_ScriptNotify(object sender, NotifyEventArgs e)
{
System.Diagnostics.Debug.WriteLine("ScriptNotifyValue: " + e.Value);
}
}
public sealed class StreamUriWinRTResolver : IUriToStreamResolver
{
public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
{
if (uri == null)
{
throw new Exception();
}
string path = uri.AbsolutePath;
// Because of the signature of the this method, it can't use await, so we
// call into a seperate helper method that can use the C# await pattern.
return GetContent(path).AsAsyncOperation();
}
private async Task<IInputStream> GetContent(string path)
{
// We use app's local folder as the source
try
{
Uri localUri = new Uri("ms-appdata:///local" + path);
StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(localUri);
IRandomAccessStream stream = await f.OpenAsync(FileAccessMode.Read);
return stream;
}
catch (Exception) { throw new Exception("Invalid path"); }
}
}
For more info, please see Remarks and Examples in WebView.NavigateToLocalStreamUri method and also Custom URI resolving in What’s new in WebView in Windows 8.1. Besides, there is also a WebView control (XAML) sample on GitHub.

Unable to send a message to client using signalr in firefox

startup class:
using Owin;
using Microsoft.Owin;
using Microsoft.AspNet.SignalR;
[assembly: OwinStartup(typeof(ESimSolFinancial.Startup))]
namespace ESimSolFinancial
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR("/signalr", new HubConfiguration());
}
}
}
Hub Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace ESimSolFinancial.Hubs
{
public class ProgressHub : Hub
{
public static void SendMessage(string msg,int nCount)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
hubContext.Clients.All.broadcastMessage(string.Format(msg), nCount);
}
}
}
in a js file i have this function:
function InitializeProgressBar() {
ProgressBarShow();
var progressNotifier = $.connection.progressHub;
progressNotifier.client.broadcastMessage = function (message, count) {
UpdateProgressBar(message, count);
};
$.connection.hub.url = "/signalr";
$.connection.hub.start({ transport: ['webSockets', 'longPolling'] }).done(function () {
//progressNotifier.client.broadcastMessage('Opening Connection',0);
});
}
it is called on a click event
the click event invokes an action result in which i have this code:
Thread.Sleep(5000);
ProgressHub.SendMessage("Getting Ledger Data", 10);
to update a progressbar after long processes finish.
the messages are sent correctly in chrome and IE but firefox does not receive these messages accordingly. UpdateProgressBar(message, count); does not get invoked. it works sometimes when i open firebug and stop execution for a long time.
i thought it was because the long processes were not long enough, so i increased the sleep time but that was not the case.
i don't understand what i'm doing wrong here. any help is appreciated.
UPDATE:
the actionresult is of the page where click event is. it seems to work only after the application has just started. after that firefox refuses to receive those messages.
Have you called server function for click event in js
progressNotifier.server.SendMessage(msg,count);
You can modify client code ... by calling server function
function InitializeProgressBar() {
ProgressBarShow();
var progressNotifier = $.connection.progressHub;
progressNotifier.client.broadcastMessage = function (message, count) {
UpdateProgressBar(message, count);
};
$.connection.hub.url = "/signalr";
$.connection.hub.start({ transport: ['webSockets', 'longPolling']
}).done(function () {
//Call to server function(SendMessage) made .This fuction broadcasts to all clients
//by calling client side(broadcast message ) function of all users
progressNotifier.server.SendMessage(msg,count);
//progressNotifier.client.broadcastMessage('Opening Connection',0);
});
}
Please try this.I cant run the program in an application. But this is how signal R works.One client calls a server function on an event(or server replies on no call but some logic) and the server broadcasts realtime notifications to selected groups,or selected users or all of them(in client side).
$('#id').click(function(){
progressNotifier.server.SendMessage(msg,count);
});
Server function has to be called

Categories