I am writing to seek help, in regards creating a real-time data update using SignalR. I am currently having issue on the client-side, where I am unable to render the data content.
I have a tested the query command and it seems to be returning data. This leads me to believe, that my client-side code, maybe incorrect.
<script src="~/Scripts/jquery-1.8.2.min.js" type="text/javascript" ></script>
<script src="~/Scripts/jquery.signalR-2.0.1.min.js" type="text/javascript" ></script>
<script src="~/signalr/hubs" type="text/javascript" ></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.NotificationHub;
// Create a function that the hub can call to broadcast messages.
notifications.client.recieveNotification = function (role, descrip) {
// Add the message to the page.
$('#spanNewMessages').text(role);
$('#spanNewCircles').text(descrip);
};
// Start the connection.
$.connection.hub.start().done(function () {
notifications.server.sendNotifications(function () {
alert("does it work");
});
}).fail(function (e) {
alert(e);
});
</script>
<h1>New Notifications</h1>
<div>
<b>New <span id="spanNewMessages"></span> role.</b><br />
<b>New<span id="spanNewCircles"></span> descrip.</b><br />
</div>
Hub Class:
[HubName("NotificationHub")]
public class notificationHub : Hub
{
string role = "";
string descrip = "";
[HubMethodName("sendNotifications")]
public void SendNotifications()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["dummyConnectionString"].ConnectionString))
{
string query = "SELECT [role],[description] FROM [dbo].[User]";
connection.Open();
using (SqlCommand command = new SqlCommand(query, connection))
{
command.Notification = null;
DataTable dt = new DataTable();
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
dt.Load(reader);
if (dt.Rows.Count > 0)
{
role = dt.Rows[0]["role"].ToString();
descrip = dt.Rows[0]["description"].ToString();
}
}
}
Clients.All.RecieveNotification(role, descrip);
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
notificationHub nHub = new notificationHub();
nHub.SendNotifications();
}
}
}
StartUp CLass:
using Microsoft.Owin;
using Owin;
using WebApplication2;
namespace WebApplication2
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Could anyone, please provide some assistant, to where I may be going wrong with this task. Thank you.
I mocked up your app. Your issue was you are returning a string from your hub action:
public string SendNotifications()
{
return context.Clients.All.RecieveNotification(role, descrip);
}
this should be void (you aren't returning anything, but actually calling the clients), and you also don't need to use GlobalHost to get the context here, only when the context isn't available (I.E. calling the hub from the server). Try making these changes:
[HubMethodName("sendNotifications")]
public void SendNotifications()
{
//using...
//IHubContext context = GlobalHost.ConnectionManager.GetHubContext<notificationHub>();
//return context.Clients.All.RecieveNotification(role, descrip);
Clients.All.RecieveNotification(role, descrip);
}
Put a breakpoint at Clients.All... and see if it is being triggered. Let me know if these updates fix your issue.
Related
I'm making an Android app using webview.
The app can print out receipts. What I want to do is when the printer is not working, alert box shows up to tell the printer isn't working, and return false to the form's onsubmit event to prevent form from being submitted.
Java code:
public class JSKicker {
#JavascriptInterface
public void callPrint(final String argumet) {
Thread thread = new Thread(new Runnable() {
public void run() {
int nRtn;
connectionNum = myPrinter.Connect("000.000.0.000");
if(connectionNum < 0){ //Printer not working
webview.post(new Runnable() {
#Override
public void run() {
String script = "alert('Printer Error'); return printer_connection = false;";
webview.evaluateJavascript(script, new ValueCallback<String>() {
#Override
// I can't figure out what to do here...
});
}
});
}else{ //Printer is working properly
connectionNum = myPrinter.SetLocale(8);
strText = argument;
nRtn = myPrinter.PrintText(strText, "SJIS");
nRtn = myPrinter.PaperFeed(64);
nRtn = myPrinter.CutPaper(1);
myPrinter.Disconnect();
}
}
});
thread.start();
}
JavaScript in header:
<script type="text/javascript">
function gate(){
jQuery.ajax({
url:'/cart_info.php',
type:'GET'
})
.done( (data) => {
window.JSKicker.callPrint(data);
})
if (printer_connection = false) {
return false;
}else{
return true;
}
}
</script>
HTML form tag:
<form method="post" id="order_form" onsubmit="return gate();">
How can I get this work?
Could you do it thru WebView.evaluateJavascript()?
https://developer.android.com/reference/android/webkit/WebView.html#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E)
So with that you could send simple CustomEvent to document in WebView
webView.evaluateJavascript("document.dispatchEvent(new Event('printer_error', { details: "No printer found!" }));");
and in JavaScript you can hook listener for your custom event to react.
document.addEventListener('printer_error', e => alert(e.details));
Didn't test this so might be that at least evaluateJavascript() needs callback function.
WebSocket can solve your problem.
WebSockets provide a persistent connection between a client and server that both parties can use to start sending data at any time. The client establishes a WebSocket connection through a process known as the WebSocket handshake.
Its very straight forward and easy to implement.
You can follow referrer links for more details:-
JAVA WebSocket:- WebSocket using Spring Boot, WebSocket using Simple JEE
Browser WebSocket(JavaScript):- WebSocket API
In Android ,if you want webview pass value to JavaScript.
First,you need to set the webview enable the JavaScript,
private WebView mWebView;
void onCreate(){
mWebView = findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);
}
And ,in you want do some thing code
if(connectionNum < 0){ //Printer not working
// I need to do something here to send a message that the printer isn't working to JS.
//In thread can not use mWebView,should send message to MainThread to do
// mWebView.loadUrl("javascript:javaCall()");
Message msg = new Message();
msg.what = 1;
myHandler.sendMessage(msg);
//myHandler can be your MainThread send to here
}
And where the mWebView created in your code, be in main thread ,you can use the
Handler to deal with the message sended to here.
private Handler myHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// this 1 is just thread send to here
if (msg.what == 1) {
//and here can do the webview UI method
mWebView.loadUrl("javascript:javaCall()");
}
}
};
the javaCall() is where you JacaScript invoke method,in javaScript you can writre like this:
<script language="javascript">
function javaCall(){
alert("Printer Error");
//other thing you can do
}
</script>
if you have problem ,you can refer to the official document.
public void loadUrl (String url)
Loads the given URL.
Also see compatibility note on evaluateJavascript(String, ValueCallback).
webview use link
Following scenario/my solution consists of the following:
Project one: (SELF HOST) I have a SignalR console application which handles the logic including the authentication process ( queries database with EF ). Project two: (CLIENT) I have an ASP.Net web application with an AngularJS client.
So far I can talk to the hub just fine. The problem is, I cannot seem to get the authentication to work. I've tried a bunch of things I've found but none of them worked. Most of them didn't even apply to my problem..
Currently I've stripped my project back to the basics and I have the following code:
Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
My hub:
[HubName("systemHub")]
public class systemHub : Hub
{
public void Authenticate(String pLogin, String pPassword)
{
User curUser = new AuthManager().Authenticate(pLogin, pPassword);
//this is where I'd want to send the auth cookie or whatever and contact the "loginCallback" function in my client
}
[Authorize]
public void Hello(String pMessage)
{
Clients.All.callbackFunc(pMessage);
}
}
Js client:
hinagApp.controller('hinagController', function ($scope) {
$(document).ready(function () {
var conURL = 'http://localhost:8080/signalr';
$.getScript(conURL + '/hubs', function () {
$.connection.hub.url = conURL;
var lHub = $.connection.systemHub;
lHub.client.callbackFunc = function(pM){
alert(pM);
}
lHub.client.loginCallback = function (pSuccess) {
if (pSuccess) {
//if logged in
lHub.server.hello("test");
}
else {
alert("fail");
}
}
$('#loginbutton').click(function () {
lHub.server.authenticate($('#input_login').val(), $('#input_pass').val());
});
$.connection.hub.start();
});
})
});
I recently ran into a similar problem. If I understand you right, you want to do the authentication on your signalr server application. Signalr can accept standard webrequests just fine.
Set the authenticationtype to cookies:
CookieAuthenticationOptions lOptions = new CookieAuthenticationOptions()
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
LoginPath = new PathString("/Auth/Login"),
LogoutPath = new PathString("/Auth/Logout"),
};
app.UseCookieAuthentication(lOptions);
If user wants to login, set the claims you'd like to use
var lForm = await context.Request.ReadFormAsync();
if (!String.IsNullOrEmpty(lForm["input_login"]) && !String.IsNullOrEmpty(lForm["input_pass"]))
{
//Benutzer authentifizieren
var lAuthenticatedUser = new UserManager().Authenticate(lForm["input_login"], lForm["input_pass"]);
if (lAuthenticatedUser != null)
{
//Existiert der Nutzer legen wir die Claims an
ClaimsIdentity lIdentity = new ClaimsIdentity(lOptions.AuthenticationType);
lIdentity.AddClaim(new Claim(ClaimTypes.Name, lAuthenticatedUser.Username));
lIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, lAuthenticatedUser.InternalUserId.ToString()));
lIdentity.AddClaim(new Claim(ClaimTypes.SerialNumber, context.Request.RemoteIpAddress));
//Und zum Schluss einloggen
context.Authentication.SignIn(lIdentity);
//Und auf die Spieleseite weiterleiten
context.Response.Redirect(BLL._Configuration.HinagGameURL);
}
}
If you want to serve the login page you can do it like this (_Authpage is your page as String, for example)
else if (context.Request.Path.Value == "/Auth/")
{
if (context.Authentication.User != null)
context.Response.Redirect(BLL._Configuration.HinagGameURL);
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(_Authpage);
}
If the user needs anything else ( such as additional style files in your authpage )
else
{
await next();
}
All of this belongs in your Startup.
In Startup.cs you need to add forms authentication middleware (probably you need to tune it a bit):
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
https://msdn.microsoft.com/en-us/library/microsoft.owin.security.cookies.cookieauthenticationoptions(v=vs.113).aspx
You kind screwed up Angular with that code. Try this one:
hinagApp
.controller('hinagController', function ($scope, $http) {
var conURL = 'http://localhost:8080/signalr';
var lHub = $.connection.systemHub;
lHub.client.callbackFunc = function(pM){
alert(pM);
}
lHub.client.loginCallback = function (pSuccess) {
if (pSuccess) {
//if logged in
lHub.server.hello("test");
}
else {
alert("fail");
}
}
$http
.get(conURL + '/hubs')
.then(function(response) {
$.connection.hub.url = conURL;
$('#loginbutton').click(function () {
lHub.server.authenticate($('#input_login').val(), $('#input_pass').val());
});
$.connection.hub.start();
});
});
I have setup a page so that when a link is clicked it calls a JavaScript function which calls a Page Method. This works perfectly on our development and staging sites but does not work on our live site. There is no error being generated, it just doesn't call the method. I do have the script manager tag on the page and "EnablePageMethods" is set to true. Does anyone have any ideas. Thank you in advance for any help that maybe offered.
<script type='text/javascript'>
function RequestAQuote_Click(ctti) {
PageMethods.RecordClickThrough(ctti);
}
</script>
[WebMethod]
public static void RecordClickThrough(int clickThroughTrackedId)
{
try
{
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["globalair"].ToString()))
{
connection.Open();
using (SqlCommand command = new SqlCommand("seo.spRecordClickThroughs", connection))
{
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#ClickThroughsTrackedId", clickThroughTrackedId);
command.ExecuteNonQuery();
}
}
}
catch(Exception ex)
{
ErrorLog(ex.ToString(), "RecordClickThrough");
}
}
I am running the following script on the client-side and the script is failing to update, when there is change in the database. I debugged the script using DevTools and discovered my Jquery scripts are responding back as "304 not modified". Does this issue, indicate why the client-side content is failing to update.
<script src="../Scripts/jquery-1.6.4.js"></script>
<script src="../Scripts/jquery-1.6.4.min.js"></script>
<script src="../Scripts/jquery.signalR-2.1.2.min.js"></script>
<script src='<%: ResolveClientUrl("~/signalr/hubs") %>'></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.NotificationHub;
// Create a function that the hub can call to broadcast messages.
notifications.client.recieveNotification = function (role, descrip) {
// Add the message to the page.
$('#spanNewMessages').text(role);
$('#spanNewCircles').text(descrip);
};
// Start the connection.
$.connection.hub.start().done(function () {
notifications.server.sendNotifications();
alert("Notifications have been sent.");
}).fail(function (e) {
alert(e);
});
//$.connection.hub.start();
});
</script>
<h1>New Notifications</h1>
<div>
<br />
<b>New <span id="spanNewMessages"></span> = role.</b><br />
<b>New <span id="spanNewCircles"></span> = descrip.</b><br />
</div>
Hub Class:
[HubName("NotificationHub")]
public class notificationHub : Hub
{
string role = "";
string descrip = "";
[HubMethodName("sendNotifications")]
public void SendNotifications()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["dummyConnectionString"].ConnectionString))
{
string query = "SELECT top 1 [role],[description] FROM [dbo].[User] order by uploadDate desc";
connection.Open();
SqlDependency.Start(GetConnectionString());
using (SqlCommand command = new SqlCommand(query, connection))
{
try
{
command.Notification = null;
DataTable dt = new DataTable();
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
dt.Load(reader);
if (dt.Rows.Count > 0)
{
role = dt.Rows[0]["role"].ToString();
descrip = dt.Rows[0]["description"].ToString();
}
connection.Close();
}
catch (Exception ex)
{
throw ex;
}
}
}
Clients.All.RecieveNotification(role, descrip);
}
[HubMethodName("onStatusChanged")]
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Insert)
{
notificationHub nHub = new notificationHub();
nHub.SendNotifications();
}
}
Please advice. Thank you.
I am trying to create a demo of Group Chat using reverse ajax in Spring. I am using Spring 3.2.0.RELEASE version.
I am using DeferredResult to perform reverse ajax in my controller. Following is the snippet of my Controller class.
#Autowired
private AsyncRepository asyncRepository;
Map<Integer, List<DeferredResult<String>>> watchers = new ConcurrentHashMap<Integer, List<DeferredResult<String>>>();
#RequestMapping(value="/asyncRequest/getMessages/{id}", method=RequestMethod.GET)
#ResponseBody
public DeferredResult<String> getMessages(final #PathVariable("id") Integer id){
final DeferredResult<String> deferredResult = new DeferredResult<String>(null, Collections.emptyList());
if(watchers.containsKey(id)) {
watchers.get(id).add(deferredResult);
} else {
watchers.put(id, new ArrayList<DeferredResult<String>>());
watchers.get(id).add(deferredResult);
}
deferredResult.onCompletion(new Runnable() {
#Override
public void run() {
watchers.get(id).remove(deferredResult);
}
});
return deferredResult;
}
#RequestMapping(value="/asyncRequest/setMessages/{id}/{message}", method=RequestMethod.GET)
#ResponseBody
public String setMessage(#PathVariable("id") Integer id, #PathVariable("message") String message) {
asyncRepository.setMessage(id, message);
return "";
}
#Scheduled(fixedRate=1000)
public void processQueues() {
for (Map.Entry<Integer, Queue<AsyncDataBean>> entry : asyncRepository.getAsyncBeans().entrySet()) {
while(entry != null && entry.getValue() != null && !entry.getValue().isEmpty()) {
AsyncDataBean asyncDataBean = entry.getValue().poll();
for (DeferredResult<String> deferredResult : watchers.get(asyncDataBean.getId())) {
deferredResult.setResult(asyncDataBean.getMessage());
}
}
}
}
And below is the Repository class which holds the Map of GroupID and its relevant messageQueue. And it also has the functions for getting and setting the messages for relevant group id.
#Repository
public class AsyncRepository {
private Map<Integer, Queue<AsyncDataBean>> asyncBeans = new ConcurrentHashMap<Integer, Queue<AsyncDataBean>>();
public String getMessages(Integer id) {
StringBuilder stringBuilder = new StringBuilder();
while (asyncBeans.get(id) != null && !asyncBeans.get(id).isEmpty()) {
stringBuilder.append(asyncBeans.get(id).poll().getMessage()).append("~");
}
return stringBuilder.toString();
}
public void setMessage(Integer id, String message) {
if(asyncBeans.containsKey(id)) {
asyncBeans.get(id).add(new AsyncDataBean(id, message));
} else {
Queue<AsyncDataBean> messageQueue = new ConcurrentLinkedQueue<AsyncDataBean>();
messageQueue.add(new AsyncDataBean(id, message));
asyncBeans.put(id, messageQueue);
}
}
public Map<Integer, Queue<AsyncDataBean>> getAsyncBeans() {
return asyncBeans;
}
public void setAsyncBeans(Map<Integer, Queue<AsyncDataBean>> asyncBeans) {
this.asyncBeans = asyncBeans;
}
}
And below is the data bean I am using to store each message with its group id.
public class AsyncDataBean {
private Integer id;
private String message;
public AsyncDataBean() {
}
public AsyncDataBean(int id, String message) {
this.setId(id);
this.setMessage(message);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
And then comes the jsp page for group chat. which looks like below.
<script type="text/javascript">
var messagesWaiting = false;
function getMessages(){
if(!messagesWaiting){
$.ajax({ url: "${pageContext.servletContext.contextPath}/asyncRequest/getMessages/${id}",
dataType:"text",
success: function(data,textStatus,jqXHR) {
if(textStatus == 'success'){
messagesWaiting = false;
var arr = data.split("~");
for(var i=0; i<arr.length; i++)
{
try
{
if(arr[i] != '') {
$("#txtaMessages").val($("#txtaMessages").val() + "\n\n" + arr[i]);
document.getElementById("txtaMessages").scrollTop = document.getElementById("txtaMessages").scrollHeight;
}
}
catch(e){
alert(e.message);
}
}
}
},
complete: function(j) {
},
error: function(xhr) {
}
});
messagesWaiting = true;
}
}
setInterval(getMessages, 1000);
getMessages();
function sendMessage() {
var xmlhttp1 = new XMLHttpRequest();
xmlhttp1.open("GET", '${pageContext.servletContext.contextPath}/asyncRequest/setMessages/${id}/' + $("#txtMessage").val(), true);
xmlhttp1.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp1.send();
$("#txtMessage").val("");
$("#txtMessage").focus();
}
</script>
</head>
<body>
<h1>Hello World!</h1>
<table>
<tr>
<td>Messages :: </td>
<td>
<textarea cols="100" rows="10" id="txtaMessages"></textarea>
</td>
</tr>
<tr>
<td>Send Message :: </td>
<td><input type="text" id="txtMessage"/></td>
</tr>
<tr>
<td><input type="button" value="Send" onclick="sendMessage();"/></td>
</tr>
</table>
</body>
</html>
That is what I have coded till now to get this working. And everything is working finw in FF and Chrome. But in IE it is not working as expected. The request is never gets hold on the server and it always gets executed every second as configured in the javascript code. And it always returns the same result as previous. I have tried to use several other methods to send ajax request for IE but its not working. Can anyone get it working for me?
Since everything works fine in FF and Chrome, I suspect the problem is with javascript code to send the request to get messages.
Please help me.
Thanks in advance.
This is very very frustrating.
To get this thing work properly in IE I need to set cache:false attribute in the ajax request I am creating with jquery for getMessages. Otherwise IE will not hold the request in pending status and always returns back with the old response text.
Its a very big issue with IE. I hope no one face the problem again or finds this answer as early as possible.
:)